Zoom Icon

Corso UIC Avanzato 05 Syscalo

From UIC Archive

Corso UIC Newbies 02 Jnzero

Contents


Corso UIC Avanzato 05 Syscalo
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 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 malevole 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.