Zoom Icon

Corso UIC Avanzato 05 Syscalo

From UIC

Corso UIC Newbies 02 Jnzero

Contents


Infos
Author: Jnzero
Email: Email
Website: Home page
Date: 16/06/2001 (dd/mm/yyyy)
Level: Working brain required/Some skills are required/Slightly hard
Language: Italian Image:Flag_Italian.gif
Comments:



Tools


Link e Riferimenti

Questo è il Corsi UIC Avanzati n°05 disponibile alla pagina Corsi per Studenti


Introduzione

Soluzione lezione 5 UIC. Questa lezione presenta diversi gradi di difficoltà, come descritto nella presentazione. Io ho risolto i tre punti fondamentali, ma non ho avuto il tempo di scrivere i generatori del seriale e del keyfile (causa interessi extra reversing & feste con relativi sballi ;) comunque se volete provarci voi non è molto complicato, soprattutto se decidete di scriverli in asm, perché potete "riciclare" quasi tutto il codice delle routine.

Vediamo un po' cosa richiede questo crackme:

  • un codice fisso (Que ha scelto Cr4nB&Rr13$) da inserire nella prima casella (quella più in alto).
  • un seriale, di lunghezza fissa di 15 caratteri, basato sul nickname. Se il nickname è lungo n caratteri, ma meno di 15, il seriale è determinato dal nickname solo per n caratteri ed i restanti possono essere scelti a caso. Se però li cambiamo dobbiamo ricordarci di generare il nuovo key file. Per capirci ecco i miei dati:

Nickname:

  • syscalo (7 caratteri)
  • seriale: 0201856 (parte basata sul nick -7 caratteri-) 90909090 (parte scelta a caso fino ad arrivare a 15 caratteri-)


  • un keyfile, chiamato crykey.key, di 700 bytes, basato sul seriale.

Tutto qua ?-) direte voi! No, no, Quequero ha voluto divertirsi ancora un po' ;) ha inserito un simpatico trucchetto che è necessario scoprire per far comparire la msgbox di congratulazioni (dopo aver trovato tutti i dati indicati sopra). Se osservate la form del programma vedete un tasto About; lo vedete?-) bene dovete premerlo 4 volte (ovviamente ogni volta dovete chiudere la msgbox presentata) prima che il programma effettui il controllo finale; vi consiglio di farlo subito prima di inserire i vari seriali. Dimenticavo; per rendere un po' antipatico il programma sono presenti alcuni check anti softice che vanno eliminati solo se intendiamo utilizzare questo (magnifico) debugger; niente di particolare, vedremo in seguito come fare. Ora siamo veramente giunti alla fine. Possiamo vedere un po' di asm. Buon divertimento ;)


Essay

Ok passiamo all'assembler -tanto per cambiare- Eliminazione check anti softice: Il programma se trova softice mostra una msgbox; questo è un ottimo aiuto per ricercare i punti in cui vengono effettuati i check, passiamo quindi a disassemblare il programma. Ora ricercate il testo "Chi usa softice alzi la mano", andate dove viene richiamato e troverete i check, due realizzati tramite l'int 68h e uno tramite l'int 3h; i primi due vengono effettuati all'avvio del programma e li trovate all'indirizzo 00401062 e 004010DB, nella seguente forma

00401062 mov ah,43h
00401064 int 68h
00401066 cmp ax,0F386h se viene ritornato questo valore,
softice è attivo...

0040106A jz loc_401B3E e salta a visualizzare la msgbox (nel
secondo cambia l'indirizzo a cui salta)

Il terzo viene effettuato prima del controllo del keyfile e lo trovate all'indirizzo 00401536, nella seguente forma:00401536 mov ebp,4243484Bh fa parte del trucco anti softice (x Que: mi sono informato ;)

0040153B mov ax,4
0040153F int 3 // Trap to Debugger -Commento inserito da Ida ;)-
00401540 cmp al,4 // se in al non c'è ancora 4 softice è attivo
00401542 jnz loc_4011EE // salta a visualizzare la msgbox

Per disabilitare i check è sufficiente nopparli come meglio credete. Nei primi due potreste noppare il jz, mentre nel terzo si potrebbe noppare l'int 3. Dopo aver visualizzato la msgbox il programma termina con la chiamata all'API ExitProcess. Ok ora possiamo tranquillamente utilizzare softice ;) Passiamo ad analizzare il programma:

Ricerca del primo codice: Impostiamo in sice un bpx GetWindowTextA, avviamo il programma; ora inseriamo una parola nella prima casella e premiamo il tasto register. Sice poppa all'indirizzo 00401247 dove troviamo il seguente codice:


sub_401206:
cmp byte_403271,8flag per impostare a una sola volta il check sul
primo seriale

jz loc_401323 // se =8 (->già eseguito) salta a loc_401323
push 3E9h
push dword_40201C
call j_GetDlgItem // ottiene l'handle della prima casella
mov dword_402030,eax // salva l'handle
push 68h
push dword_40201C
call j_GetDlgItem // ottiene l'handle della seconda casella
mov dword_402034,eax // salva l'handle
push 0Ch // numero max chr da leggere (0Ch=12)
push offset dword_403273 // ind a cui salvare il primo seriale
push dword_402030 // passa handle casella

loc_401247:
call j_GetWindowTextA // legge la prima casella
call sub_40159C procedura che crea dei valori (usati in seguito per le
crittazioni) basati sulle dimensioni dei file autoexec.bat e system.ini

xor ecx,ecx // azzera ecx

loc_401253:
xor dword_403273[ecx],0EDh xor di 4 chr alla volta del seriale letto
con 0EDh

inc ecx // incrementa conteggio chr (sposta di una posizione)
cmp ecx, 0Ch // continua per 12 volte
jnz short loc_401253 // esegue le istr precedenti
xor ecx, ecx // azzera ecx

loc_401265:
xor dword_403273[ecx], 134F7432h // xor di 4 chr del seriale alla volta
xor dword_403273[ecx], 4A710930h questi tre valori equivale a fare lo
xor con 01E960E4h

xor dword_403273[ecx], 58D71DE6h
add ecx, 4 incrementa di 4 il conteggio dei chr (sposta di 4
posizioni)

cmp ecx, 0Ch // esegue per 12 chr
jnz short loc_401265 // esegue le istr prec (per 3 volte)
call j_GetTickCount ottiene un valore "casuale" chiamando l'API che
ritorna il tempo da cui è attivo windows (ritorna il valore in eax)

xor dword_403273, eax // xora i primi 4 byte del seriale con TickCount
xor dword_403277, eax // xora i successivi 4 byte
xor dword_40327B, eax // xora i successivi 4 byte (in tutto 12)
xor dword_40327F, eax in queste locazioni sono memorizzati i valori
di confronto

xor dword_403283, eax // esegue lo stesso xor anche su queste
xor dword_403287, eax
and dword_40327F, 627893h and tra i primi 4 byte del valore di
confronto e la maschera

and dword_403273, 627893h and tra i primi 4 byte del seriale e la
maschera

mov esi, offset dword_403273 // copia in esi l'ind del seriale
mov edi, offset dword_40327F copia in edi l'ind del valore di
confronto

mov ecx, 0Ch ecx=12 - numero di volte che deve essere eseguito il
confronto

rep cmpsb // confronta byte per byte il seriale e il valore di confronto
jz short loc_401300 // salta se sono uguali

Spiegazione di come si ricava il seriale corretto: A noi interessa il valore di confronto prima delle istruzioni di xor con il TickCount. Quindi l'algoritmo di cui ci occupiamo arriva fino a prima della chiamata call j_GetTickCount. Dobbiamo andare a leggere i 12 byte dalla locazione 00403283 (compresa) e elaborarli nel seguente modo:

  • Li dividiamo in 3 gruppi di 4 bytes e ogni gruppo lo xoriamo con 01E960E4 (che è il valore equivalente ai tre xor indicati nel listato).
  • Ora xoriamo ogni singolo byte con 0EDh.

Valutiamo ogni byte ottenuto con il codice ASCII corrispondente e se non avete sbagliato i calcoli dovreste ottenere: Cr4nB&Rr13$ Nota: il dodicesimo byte vi risulta 0 ed è giusto; infatti le stringhe devono terminale con il carattere Null(=0).

Passiamo al seriale basato sul nickname: Inseriamo il nick (syscalo) e il seriale (1234567890) e premiamo il tasto register; sice poppa all'indirizzo 00401330 dove troviamo il seguente codice:


loc_401323:
push 14h // max chr da leggere
push offset unk_403249 // ind a cui salva il nome
push dword_402030 // passa handle prima casella

loc_401330:
call j_GetWindowTextA // legge nome
mov dword_403241, eax // salva il numero di chr letti effettivamente
cmp eax, 6 // confronta se il nick è almeno di 6 chr
jl loc_40141E se è minore salta (visualizza msg che avvisa che la
lunghezza deve essere almeno di 6 chr)

call j_GetTickCount // acquisisce valore casuale TickCount
mov dword_40323D, eax // salva il valore
xor ecx, ecx // azzera ecx
xor ebx, ebx // azzera ebx
mov edx, dword_40324A copia in edx 4 byte del nome partendo dal
secondo

mov eax, dword ptr unk_403249 copia in eax 4 byte del nome partendo
dal primo

imul eax, edx // moltiplica i due valori (salva in eax)
mov dword ptr unk_403249, eax // sovrascrive 4 byte del nome dal primo
xor ecx, ecx // azzera ecx
add ecx, 4 // somma 4 a ecx
dec dword_403241// decrementa la lunghezza del nome

loc_40136F:
inc ecx // incrementa ecx
mov al, byte ptr unk_403249[ecx] // copia in al i byte del nome (dal 6°)
add al, byte ptr dword_40324A[ecx] // somma i byte del nome (dal 6°+7°)
mov byte ptr unk_403249[ecx], al // sovrascrive i byte del nome (dal 6°)
cmp ecx, dword_403241 // confronta ecx con la lunghezza del nome
jl short loc_40136F // se minore esegue le istruzioni precedenti
mov eax, dword ptr unk_403249 // copia 4 byte del nome dal primo in eax
mov ebx, dword_40324A+3 // copia in ebx i secondi 4 byte del nome
imul eax, ebx // moltiplica i due valori (salva in eax)
mov dword ptr unk_403249, eax // sovrascrive 4 byte dal primo
inc dword_403241 incrementa la lunghezza del nome (riporta al valore
corretto)

xor ecx, ecx // azzera ecx
mov ecx, dword_403241 // copia la lunghezza del nome in ecx

loc_4013AB:
mov eax, dword ptr unk_403249[ecx]copia in eax 4 byte (la prima volta
quelli dopo la fine del nome-dovrebbero essere 0)

xor eax, 4CF580Fh xora con questo valore
mov dword ptr unk_403249[ecx], eax // sovrascrive i 4 byte
dec ecx // decrementa ecx (conteggio byte)
test ecx, ecx // verifica se vale 0
jnz short loc_4013AB // se non è zero esegue le istruzioni precedenti

dopo la seguente call andando a leggere i byte dal secondo del nome, troviamo la parte del seriale relativa al nome.(leggere un # di byte pari alla lunghezza del nome)


call sub_401438 // procedura di elaborazione del nome

le seguenti elaborazioni servono solo a mascherare i valori del nome e del seriale prima del confronto:

loc_4013C6
xor eax, eax // azzera eax
call sub_4013EF // procedura che elabora il TC
call sub_4014AE // critta il nome con il TC elaborato
push 14h // max chr da leggere
push offset unk_40325D // ind in cui memorizzare il seriale
push dword_402034 // handle seconda casella
call j_GetWindowTextA // legge seriale
call sub_40157A // critta il seriale con TC elaborato
call sub_4015EF verifica la coincidenza del seriale elaborato con il
nome elaborato (inoltre prosegue con la verifica del keyfile prima di
ritornare)

retn // ritorna dalla chiamata
sub_401206 endp // fine procedura

Qui riporto le procedure richiamate nella parte precedente del programma:

richiamata da call sub_401438; questa chiamata serve a portare in un range da 0 a 9 i caratteri del nome (in pratica si ottiene la parte del seriale che corrisponde all'elaborazione del nome)


sub_401438 mov ecx, dword_403241 copia in ecx la lunghezza del
nome

loc_40143E:
mov al, byte ptr unk_403249[ecx] copia i byte del nome dall'ultimo
al primo

loc_401444:
cmp al, 30h // confronta con 30h
jl short loc_401468 // se minore salta
nop
nop
nop
nop
cmp al, 39h // confronta con 39h
jg short loc_401470 // se maggiore salta
nop
nop
nop
nop
mov byte ptr unk_403249[ecx], al sovrascrive il byte elaborato del
nome

dec ecx // decremnta ecx (conteggio byte nome)
test ecx, ecx // testa se è 0
cmp ecx, 2 //confronta ecx con 2
jz short loc_401478 se è uguale salta (diversifica l'elaborazione
per i primi due byte)

nop
nop
nop
nop
jnz short loc_40143E salta all'inizio della procedura (passa al byte
succezzivo)


loc_401468:
add al, cl // somma cl ad al
cmp al, 30h // fino a quando al diventa maggiore di 30h
jl short loc_401468 // esegue le istruzioni precedenti
jmp short loc_401444 // salta a ricontrollare il valore

loc_401470:
sub al, cl // sottrae cl ad al
cmp al, 39h // fino a quando diventa minore di 39h
jg short loc_401470 // esegue le istruzioni precedenti
jmp short loc_401444 // salta a ricontrollare il valore

loc_401478:
mov al, byte ptr unk_403249[ecx]sovrascrive il byte del nome con
quello elaborato


loc_40147E:
cmp al, 30h // cofronta con 30h
jl short loc_40149E // se minore salta
nop
nop
nop
nop
cmp al, 39h // confronta con 39h
jg short loc_4014A6 // se maggiore salta
nop
nop
nop
nop
mov byte ptr unk_403249[ecx], al sovrascrive il byte del nome con
quello elaborato

dec ecx // decrementa ecx (conteggio byte nome)
test ecx, ecx // controlla se è 0
jnz short loc_401478 // se diverso salta (passa al prossimo byte)
jmp loc_4013C6 ritorna dalla procedura (ind istruzione successiva alla
call)


loc_40149E:
add al, 4 // somma 4 ad al
cmp al, 30h // fino a quando diventa maggiore di 30h
jl short loc_40149E // esegue le istruzioni precedenti
jmp short loc_40147E // salta a ricontrollare il valore

loc_4014A6:
sub al, 5 // sottrae 5 ad al
cmp al, 39h // fino a quando diventa minore di 39h
jg short loc_4014A6 // esegue le istruzioni precedenti
jmp short loc_40147E // salta a ricontrollare il valore
sub_401438 endp // fine procedura

richiamata da call sub_4013EF (elabora il TickCount):sub_4013EF

call j_GetTickCount // ritorna TickCount
mov dword_403235, eax // salva in 403235
mul dword_403239 // dx:ax=ax*1538h (403239 contiene il valore 1538h)
inc eax // incrementa eax
mov dword_403235, eax // salva il valore ottenuto
retn // ritorna dalla chiamata
sub_4013EF endp

richiamata da call sub_4014AE (critta il nome):


sub_4014AE xor ecx, ecx //azzera ecx
xor eax, eax // azzera eax

loc_4014B2: inc ecx // incrementa ecx (conteggio byte)
mov al, byte ptr unk_403249[ecx] // copia i byte del nome dal 2° in al
mov bl, byte ptr dword_403235 copia in bl il valore del TC elaborato
nella procedura precedente

xor al, bl // xora i due valori
mov byte ptr unk_403249[ecx], al // sovrascrive i byte del nome
cmp ecx, dword_403241 esegue le istr prec fino per tutti i byte del
nome (meno il primo)

jl short loc_4014B2 // esegue le istruzioni precedenti
retn // ritorna dalla chiamata
sub_4014AE endp

richiamata da call sub_40157A (critta il seriale):


sub_40157A xor ecx, ecx // azzera ecx
xor eax, eax // azzera eax

loc_40157E:
inc ecx // incrementa ecx
mov al, byte ptr unk_40325C[ecx] //copia in al il primo byte del seriale
mov bl, byte ptr dword_403235 copia in bl il valore del TC elaborato
nella procedura sub_4013EF

xor al, bl xora i due valori
mov byte ptr unk_40325C[ecx], al // sovrascrive i byte del seriale
cmp ecx, dword_403241 esegue le istruzioni precedenti per tanti byte
del seriale quanti sono quelli del nome

jl short loc_40157E // esegue le istruzioni precedenti
retn // ritorna dalla chiamata
sub_40157A endp

richiamata da call sub_4015EF (verifica il seriale basato sul nick e poi passa alla verifica del keyfile):


sub_4015EF push dword_402034 // passa l'handle della seconda casella
call j_GetWindowTextLengthA // ritorna la lunghezza del seriale
cmp eax, 0Fh // il seriale deve essere di 15 chr
jnz loc_401108 // se non sono 15 salta (seriale sbagliato)
mov esi, offset dword_40324A copia ind del secondo byte del nome (il
primo viene ignorato -qui-)

mov edi, offset unk_40325D // copia l'indirizzo del seriale
mov ecx, dword_403241 // copia in ecx la lunghezza del nome
rep cmpsb // confronta il nome elaborato con il seriale elaborato
jz loc_4014F4 se sono uguali salta (seriale giusto - slata al
controllo del keyfile)

mov dword_40329F, 91C2h // flag per la correttezza del seriale
jmp loc_4014FE // salta a proseguire l'esecuzione (controllo keyfile)
sub_4015EF endp

Generazione e verifica del keyfile: Il keyfile è generato interamente dal seriale basato sul nick; il programma procede nel seguente modo:

  • Elabora il seriale fino ad ottenere il keyfile di 700 bytes.
  • Critta i valori ottenuti con valori (circa) casuali.
  • Legge il keyfile e lo critta con gli stessi valori (circa) casuali.
  • Verifica che i byte così crittati coincidano.

Vediamo il disassemblato: La routine di generazione del keyfile è molto lunga (e noiosa ;) quindi prima di tutto vediamo come procurarci un keyfile valido. Per fare ciò dobbiamo addentrarci nella routine che elabora il seriale riportata in seguito.


loc_4014F4:
mov dword_40329F, 91B2h salva il flag per il seriale basato sul nick
(salta qui se il seriale basato sul nick è corretto)


loc_4014FE:
push 14h // passa # max chr da leggere
push eax // passa l'indirizzo a cui salvare i dati
push dword_402030 // passa l'handle della prima casella
call j_GetWindowTextA // legge il nick (prima casella)
mov dword_403241, eax copia il # di chr letti effettivamente in
dword_403241

mov eax, offset unk_402BFC copia in eax l'indirizzo a cui salvare il
seriale

add eax, 14h // somma un offset di 14h
push 14h // passa # max chr da leggere
push eax // passa l'ind a cui salvare il seriale
push dword_402034 // passa l'handle della seconda casella
call j_GetWindowTextA // legge il seriale (seconda casella)
call sub_40167F chiama la routine di elaborazione del seriale per il
confronto con il keyfile

call sub_401B79 // chiama routine di lettura del keyfile
call sub_401BCE // chiama routine di crittazione del keyfile
mov ebp, 4243484Bh // fa parte del trucco anti sice
mov ax, 4 // copia 4 in eax
int 3 chiama l'interrupt 3 (test anti SI-segnalato da Ida come Trap
to Debugger ;)

cmp al, 4 // controlla se in al c'è 4
jnz loc_4011EE // se è diverso salta (visualizza "msgbox chi usa SI")
mov esi, offset unk_402BFC // copia ind seriale elaborato in esi
mov edi, offset dword_4025F0 // copia ind dati keyfile in edi
mov ecx, dword_4031FC copia il numero di byte da confrontare in ecx
(700 bytes)

rep cmpsb // controlla i byte puntati da esi e edi
jz loc_401DE9 se sono uguali salta al controllo finale di tutti i
test


loc_401560:
mov dword_4032A3, 57A3h // salva flag keyfile (keyfile sbagliato)
jmp loc_401DF3 // salta al controllo finale di tutti i test

Routine richiamate dal codice precedente:

routine di elaborazione del seriale: per avere il keyfile è sufficiente eseguire il codice fino alla locazione 0040190Bh, dove termina l'elaborazione "non casuale" del seriale, ed andare a leggere 700 bytes a partire dalla locazione 00402BFCh. Questi 700 bytes vanno copiati così come sono in un file chiamato crykey.key; per fare ciò ci sono diversi metodi, dipende dal sw che usate; io ho usato il turbo debugger del tasm, eseguendo il programma fino all'indirizzo indicato e poi ho fatto un dump. Note: la scrittura *(423423h) sta ad indicare il valore della locazione all'indirizzo 423423h; la scritta (base)seriale indica l'indirizzo del primo byte dei 700 che verranno generati dal seriale.


sub_40167F mov ecx, 23h // copia 23h(=35) in ecx (fine seriale)
mov bl, 23h // copia 23h in bl
mov eax, offset unk_402BFC copia l'ind del seriale in eax

loc_40168B:
xor [eax+ecx-1], bl xora tutti i byte del seriale con 23h dal
penultimo (escluso lo 0) al primo

dec ecx // decrementa ecx
test ecx, ecx // controlla se è 0
jnz short loc_40168B // esegue le istr prec
mov ecx, offset unk_402BFC // copia in ecx l'ind del seriale elaborato
mov al, [ecx] // copia in al il primo byte del (base)seriale
mov ah, [ecx+14h] // copia in ah il 20° byte del (base)seriale
add al, ah // somma e sovrascrive al
mov edx, dword_403241 // copia in edx la lunghezza del nome
mov bl, [ecx+edx] copia in bl i byte del
seriale((base)seriale+lunghezzaNome)

mov bh, [ecx+23h] // copia in bh l'ultimo byte del seriale
add bl, bh // somma e sovrascrive bl
xor al, bl // xora e sovrascrive al
xor ecx, ecx // azzera ecx
mov ebx, offset unk_402BFC // copia ind (base)seriale

loc_4016B7:
xor [ebx+ecx], al // xora i byte da (base)seriale con al
inc al // incrementa al
inc ecx // incremento conteggio byte
cmp ecx, 23h // confronta ecx con 23h
jl short loc_4016B7 // se minore esegue le istr prec
mov al, [ebx+5] // copia in al il 5° da (base)seriale
add al, 0Fh // somma 0Fh ad al
xor ecx, ecx // azzera ecx
mov ecx, 23h // copia 23h in ecx

loc_4016CE:
xor [ebx+ecx], al // xora i byte dal 35° al 1° con al
dec ecx // decrementa ecx
test ecx, ecx // controlla se 0
jnz short loc_4016CE // esegue le istr prec
mov edi, offset unk_402BFC // copia in edi ind (base)seriale
mov al, [edi+4] // copia il 4° byte in al
mov ecx, 23h // copia 35 in ecx
mov edx, 46h // copia 70 in ecx

loc_4016E8:
mov bl, [edi+ecx] // copia in bl il byte *(edi+ecx)
xor bl, al // xora con al
mov [edi+edx], bl // sovrascrive *(edi+edx)
dec edx // decrementa edx
dec ecx // decrementa ecx
test ecx, ecx // azzera ecx
jnz short loc_4016E8 // esegue le istr prec
mov eax, offset unk_402BFC // copia l'ind (base)seriale
mov ecx, 2BCh // copia 2BCh(=700) in ecx
mov ebx, dword_403241 // copia in ebx la lunghezza del nome
add ebx, 0Fh // somma 0Fh a ebx

loc_401709:
xor [eax+ecx], bl // xora *(eax+ecx) con bl
dec ecx // decrementa ecx
test ecx, ecx // controlla se è 0
jnz short loc_401709 // esegue le istr prec
mov ebx, 0F1h // copia 0F1h in ebx

loc_401716:
xor byte ptr [eax+ecx], 0F1h // xora *(eax+ecx) con 0F1h
dec ebx // decrementa ebx
test ebx, ebx // controlla se è 0
jnz short loc_401716 // esegue le istr prec
mov ecx, 2BCh // copia 2BCh in ecx
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, 100D10Fh // copia 100D10h in ebx

loc_40172E:
xor [eax+ecx], ebx // xora 4 byte *(eax+ecx) con ebx
sub ecx, 4 // sottrae 4 a ecx
test ecx, ecx // controlla se è 0
jnz short loc_40172E // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
mov edx, eax // copia eax in edx
add edx, 100h // somma 100h a edx
mov ecx, 14h // copia 14h in ecx

loc_40174A:
mov bl, [eax+ecx] // copia in bl *(eax+ecx)
xor bl, [eax+ecx+0Ch] // xora bl con *(eax+ecx+0Ch)
mov [edx+ecx], bl // copia bl in *(edx+ecx)
dec ecx // decrementa ecx
test ecx, ecx // controlla se è 0
jnz short loc_40174A // esegue le istr prec
xor ecx, ecx // azzera ecx
mov eax, offset unk_402BFC // copia ind (base)seriale

loc_401760:
mov bl, byte ptr aHeadacheByQueq[ecx] // copia in bl i byte da copiare
mov [eax+ecx+28Ah],bl // copia bl in *(eax+ecx+28Ah)
inc ecx // incrementa ecx
cmp ecx, 15h // confronta con 15h
jnz short loc_401760 // se diverso esegue le istr prec
xor ecx, ecx // azzera ecx
mov eax, offset unk_402BFC // copia ind (base)seriale

loc_40177A:
and dword ptr [eax+ecx], 27934438h esegue l'and dei byte *(eax+ecx)
con 27934438h

add ecx, 4 // somma 4 a ecx
cmp ecx, 2BCh // confronta ecx con 2BCh(=700)
jnz short loc_40177A // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
xor edx, edx // azzera edx
mov cl, [eax]// copia *(eax) in cl

loc_401795:
rol dword ptr [eax+edx], cl // rol di [cl] volte la dword *(eax+edx)
inc edx // incrementa edx
cmp edx, 2BCh // confronta edx con 2BCh
jnz short loc_401795 // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
xor ecx, ecx // azzera ecx
mov edx, dword_402BF4 // copia dword *(402BF4h) in edx

loc_4017AE:
xor [eax+ecx], edx // xora *(eax+ecx) con edx
add ecx, 4 // somma 4 a ecx
cmp ecx, 2BCh // confronta ecx con 2BCh
jnz short loc_4017AE // esegue le istr prec
xor ecx, ecx // azzera ecx
mov edx, offset unk_402BFC // copia ind (base)seriale

loc_4017C3:
mov eax, [edx+ecx] // copia la dword *(edx+ecx) in eax
shr eax, 2 // shift a dx eax di 2
mov [edx+ecx], eax // sovrascrive *(edx+ecx) con eax
add ecx, 4 // somma 4 a ecx
cmp ecx, 2BCh // confronta con 700
jnz short loc_4017C3 // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, [eax+5] // copia in ebx *(eax+5)
add eax, 110h // somma 272 a eax
xor ecx, ecx // azzera ecx

loc_4017E6:
xor [eax+ecx], ebx // xora la dword *(eax+ecx) con ebx
add ecx, 4 // somma 4 a ecx
cmp ecx, 290h // confronta ecx con 656
jnz short loc_4017E6 // esegue le istr prec
xor ecx, ecx // azzera ecx
mov eax, offset unk_402BFC // copia ind (base)seriale
mov edx, offset unk_402BFC // copia ind (base)seriale
add edx, 0C0h // somma 192 a edx

loc_401806:
mov ebx, [eax+ecx] // copia in ebx la dword *(eax+ecx)
xor ebx, 0D468B23Ah // xora la dword con 0D468B23Ah
mov [edx+ecx], ebx // copia ebx in *(edx+ecx)
add ecx, 4 // somma 4 a ecx
cmp ecx, 40h // confronta con 40h
jnz short loc_401806 // esegue le istr prec
xor ecx, ecx // azzera ecx
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, offset unk_402BFC // copia ind (base)seriale
mov edx, offset unk_402BFC // copia ind (base)seriale
add ebx, 50h // somma 50h a ebx
add edx, 0A0h // somma 0A0h a edx

loc_401834:
mov edi, [eax+ecx] // copia in edi *(eax+ecx)
xor edi, [edx+ecx] // xora edi con *(edx+ecx)
mov [ebx+ecx], edi // copia edi in *(ebx+ecx)
add ecx, 4 // somma 4 a ecx
cmp ecx, 40h // confronta con 40h
jnz short loc_401834 // esegue le istr prec
xor ecx, ecx // azzera ecx
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, offset unk_402BFC // copia ind (base)seriale
mov edx, offset unk_402BFC // copia ind (base)seriale
add edx, 80h // somma 80h a edx
add ebx, 110h // somma 110h a ebx

loc_401862:
mov edi, [eax+ecx] // copia *(eax+ecx) in edi
mov esi, [edx+ecx] // copia *(edx+ecx) in esi
xor edi, esi // xora edi con esi
mov [ebx+ecx], edi // copia edi in *(ebx+ecx)
add ecx, 4 // somma 4 a ecx
cmp ecx, 28h // confronta con 28h
jnz short loc_401862 // esegue le istr prec
xor ecx, ecx // azzera ecx
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, offset unk_402BFC // copia ind (base)seriale
add ebx, 98h // somma 98h a ebx

loc_401887:
rol dword ptr [eax+ecx], 13h rotazione a sx 13h volte la dword
(eax+ecx)

rol dword ptr [ebx+ecx], 8 // rotazione a dx 8 volte la dword *(ebx+ecx)
add ecx, 4 // somma 4 a ecx
cmp ecx, 98h // confronta ecx con 98h
jnz short loc_401887 // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, offset unk_402BFC // copia ind (base)seriale
add ebx, 130h // somma 130h a ebx
xor edi, edi // azzera edi
xor ecx, ecx // azzera ecx
add edi, 130h // somma 130h a edi

loc_4018B4:
mov edx, [ebx+edi]// copia *(ebx+edi) in edx
xor edx, [eax+ecx]// xora edx con *(eax+ecx)
add ecx, 4 // somma 4 a ecx
sub edi, 4 // sottrae 4 a edi
cmp ecx, 130h // confronta ecx con 130h
jnz short loc_4018B4 // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
mov ebx, offset unk_402BFC // copia ind (base)seriale
add ebx, 140h // somma 140h a ebx
xor ecx, ecx // azzera ecx

loc_4018DA:
mov edx, [eax+ecx] // copia *(eax+ecx) in edx
xor edx, [ebx+ecx] // xora edx con *(ebx+ecx)
mov dword ptr unk_4032C5[ecx], edx copia edx in *(4032C5h+ecx) -
usata come locazione temporanea per l'elaborazione dei dati-

add ecx, 4 // somma 4 a ecx
cmp ecx, 140h // confronta con 140h
jnz short loc_4018DA // esegue le istr prec
mov eax, offset unk_402BFC // copia ind (base)seriale
add eax, 140h // somma 140h a eax

loc_4018FB:
mov edx, dword ptr unk_4032C5[ecx]// copia in edx *(4032C5h+ecx)
mov [eax+ecx], edx // copia edx in *(eax+ecx)
sub ecx, 4 // sottrae 4 a ecx
test ecx, ecx // controlla se è 0
jnz short loc_4018FB // esegue le istr prec
mov eax, offset unk_402BFC // fine elaborazione non casuale del seriale

da qui inizia la crittazione dei 700 byte ottenuti, per poi effettuare il confronto con il keyfile crittato. In sostanza tutte le istruzioni precedenti operano in modo da costruire 700 byte partendo dal seriale; questo viene realizzato elaborando fra loro i byte alle diverse locazioni in questa zona di memoria; se volete realizzare un generatore di key file dovete prendere queste istruzioni e tradurle in un linguaggio ad alto livello (come il C) oppure potete "costruirci intorno" un programma in assembler.

Routine di lettura del keyfile:


sub_401B79
push 2 // passa la modalità di apertura del keyfile
push offset dword_402050 // passa info sul tipo di file
push offset aCrykey_key passa ind in cui è memorizzato il nome del
keyfile

call j_OpenFile // apre il file
mov dword_402048, eax // salva l'handle del file
cmp eax, 0FFFFFFFFh // controlla se l'handle è nullo (=-1)
jz loc_401560 // se si salta
push 0
push 0
push dword_4032A7 // passa l'offset
push dword_402048 // passa l'handle del file
call j_SetFilePointer // setta il puntatore al file (come seek in C)
push 0 // passa ind struttura dati (0=nessuna)
push offset dword_402320 // passa ind a cui salvare # byte letti
push dword_4031FC // passa il # di byte da leggere
push offset dword_4025F0 // passa ind a cui salvare i byte letti
push dword_402048 // passa l'handle del file
call j_ReadFile // legge il file
retn // ritorna dalla chiamata
sub_401B79 endp // fine routine

Routine di crittazione dei dati del keyfile:

La crittazione del keyfile è identica a quella del seriale elaborato ed è effettuata con valori pseudo casuali. A mio parere non è molto importante vedere come viene effettuata visto che il keyfile si può già ottenere come visto sopra (potrebbe essere utile vederla per vedere un po' di assembler, ma io per adesso ne ho abbastanza ;-). Siamo quasi giunti al termine; resta solo da vedere la parte finale di verifica di tutti i check: durante i vari controlli vengono salvati dei valori diversi in base ai vari test. Qui vengono sommati tra loro e poi ad un altro valore dato dal numero di visualizzazioni della msgbox di about (deve essere visualizzata 4 volte prima del test del keyfile). Infine il valore così ottenuto viene xorato e rollato per giungere al valore finale di confronto.

loc_401DE9:
mov dword_4032A3, 57B3h // salva questo valore se il keyfile è giusto

loc_401DF3:
xor eax, eax // azzera eax (salta qui se il keyfile è sbagliato)
mov eax, dword_40329B copia in eax il flag di controllo del primo
seriale

mov ebx, dword_40329F copia in ebx il flag di controllo del seriale
basato sul nick

add eax, ebx // somma ebx a eax
mov ebx, dword_4032A3 // copia in ebx il flag di controllo del keyfile
add eax, ebx somma ebx a eax (in pratica eax contiene la somma dei
flag dei tre test)

mov ebx, dword_403431 trucco by que ;) il valore a questa locazione
viene incrementato ogni volta che viene visualizzata la msgbox di About. Per
individuare il significato di questo valore è sufficiente andare
a vedere dove viene scritto, provate

add eax, ebx // somma ebx a eax
mov ecx, 5723814Ah // copia il valore in ecx
xor eax, ecx // xora la somma dei flag (eax) con il valore (ecx)
rol eax, cl // rol a sx di 4Ah volte
cmp eax, 8982D55Ch // test finale
jnz loc_401108 se sono diversi salta (non viene visualizzato alcun
msgbox)

call sub_401B26 altrimenti visualizza la msgbox di congratulazioni,
finalmente ;-)

Ok, se avete resistito fino a qui sarete miei complici nella spedizione punitiva a Que per questa lezione ;))) A parte gli scherzi, io ho scoperto il suo indirizzo di casa!!!-) Ma va, e come? :) va bhe, è meglio che me ne vada....


Note Finali

È dura scrivere i tute dopo l'ultimo dell'anno.........


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.