Soluzione al 5° corso newbies Troviamoci il seriale


12/06/2000

by "AndreaGeddon "

 

 

UIC's Home Page

Published by Quequero

Il motto dei miei neuroni:...

Qualche mio eventuale commento sul tutorial :)))

Qualche MIO eventuale commento sui tuoi eventuali commenti!!!

... "Ne rimarrà soltanto uno!"
UIC's form
Home page:      www.andreageddon.8m.com
E-mail:    andreageddon@hotmail.com
IRC chan:   #crack-it  /  #crack-it2
UIC's form

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 

Eccovi la soluzione di questo semplice crackme. Vediamo come trovare il seriale. Vi consiglio di provare a trovarlo da soli il seriale, e di ricorrere a questo tutorial solo se proprio non ce la fate.


Soluzione al 5° corso newbies
Troviamoci il seriale
Written by AndreaGeddon

Introduzione

Eccoci arrivati al quinto capitolo per aspiranti reverser. Nell'ultimo meeting era stato chiesto di essere più esplicativi nei tutorial, per cui cercherò di essere il più dettagliato possibile.

Tools usati

-  SoftIce

URL o FTP del programma

www.uic-spippolatori.com

Notizie sul programma 

E' un semplice crackme con la richiesta di un nome e un seriale. Il seriale è generato da un semplice algoritmo che andremo a reversare.

Essay

Iniziamo il reversing con la parte più importante: l'osservazione. Cerchiamo di carpire più informazioni possibili sul crackme. Iniziamo battendo tante lettere nel nome e nel serial, per vedere se sono limitati: noterete che l'edit box del nome è limitato a 30 caratteri, e l'edit box del serial è limitato a 60 caratteri. Uhm... e questo già ci fa pensare che il serial deve essere lungo il doppio dei caratteri del nome. Inoltre l'edit box per il seriale va solo in upper char, ma questo non ci aiuta molto. Il testo richiede esplicitamente un minimo di 6 caratteri. Ormai sappiamo già tutto! Iniziamo ad avere una vaga idea dell'algoritmo. Adesso so che vi prudono le mani, quindi passiamo alla parte di reversing.

Lanciamo il crackme, richiamiamo il nostro caro SoftIce (CTRL+D). Che dobbiamo fare? Dobbiamo arrivare nel punto più vicino alla protezione, quindi con il nostro softice impostiamo un BPX (breakpoint on execution) sulla API GetDlgItemTextA. Lo facciamo inserendo il comando:

bpx getdlgitemtexta

Che vuol dire? Abbiamo settato un punto di interruzione sulla funzione GetDlgItemTextA. Questa funzione è una API (application programming interface), cioè una funzione che il sistema operativo ci mette a disposizione. Ma perchè proprio quella? Perchè tale funzione serve per prendere il testo da un controllo del'applicazione (nel nostro caso i due edit box) e immagazzinarlo in una variabile. Il nostro intento è quello di freezare il programma mentre prende il nome e serial, e vedere passo per passo cosa ci fa. Ovviamente dovete conoscere le API più adatte per ogni occasione. Ci sono molte guide che hanno la lista con la descrizione completa, cercate in rete e scaricatevele. Le trovate anche come guide sui CD dei compilatori tipo Visual C etc. Varie API utili quando dovete breakare nei casi di protezioni tipo Nome/Serial sono: GetDlgItemTextA, GetWindowTextA, HMEMCPY. Proseguiamo. Inserite un nome e un serial, io inserisco AndreaGeddon e 112233445566. Premete il pulsane te Check... e apparirà il softice. Dove ci troviamo di preciso? Siamo nel codice della funzione GetDlgItemTextA. Potete vedere nella barra sotto la Window Code il nome USER32.text, il che vuol dire che siamo nel processo di USER32.DLL. Tutte le API del windows si trovano in alcune dll, che sono KERNEL32 , USER32, GDI32 e qualcun'altra che non ricordo ora. Effettivamente quando un programma usa un API non fa altro che richiamare la funzione di una di queste librerie. Questo permette di risparmiare molto codice e di rendere standard le applicazioni. Allora, il softice era appena apparso e ora ci troviamo nel processo USER32. Noi dobbiamo tornare al processo del programma che vogliamo crackare, possiamo farlo in un paio di modi: il primo è premendo F12 per 2 volte. Premendolo una volta sola finiamo nel processo di MFC42, che è un altra libreria, se lo premiamo di nuovo vedremo sulla barra del processo il nome del crackme 5NEWBIES.text.

Breve parentesi sui tasti funzione.

A cosa servono questi tasti funzione? F10 server per steppare, cioè seguire linea per linea il programma, quando vi troverete su una call con F10 non entrerete nella call, ma passerete all'istruzione successiva. F8 è uguale a F10 solo che entra nelle call. F12 server per scorrere i processi: quando lo premete il softice vi riporta al processo che ha chiamato il codice in cui vi trovavate, ecco perchè quando breakate e vi ritrovate nello user dovete usare F12. F11 ha funzione simile: vi fa uscire dalla call in cui vi trovate. Ad esempio, se siete entrati in una call, con F11 verrete riportati fuori da questa all'istruzione successiva. Con F11 rimanete nello stesso processo.

Fine Parentesi.

Un altro modo per tornare al processo del crackme è quello di steppare tutti i vari RET (return from call). Nel nostro caso dopo aver breakato iniziamo a steppare il user fino ad arrivare al RET, così torneremo al processo di MFC42, e anche qui continuiamo a steppare fino al prossimo RET: adesso vi trovate nel processo del crackme. Beh questo modo è decisamente un pò più noioso, meglio usare F12 :-)

Oki, ora siamo arrivati al processo del crackme, non ci rimane altro che steppare e cercare di capire cosa fa il codice. Innanzitutto ora ci troviamo subito dopo la call a getdlgitemtexta. Però nel programma ci sono due edit box, quindi la funzione verrà chiamata un'altra volta. Allora, dopo aver breakato la prima volta, premete CTRL+D per ritornare al programma, ma il softice immediatamente riapparirà. Premete F12 due volte e siamo di nuovo al processo del crackme però OLTRE le due chiamate a getdlgitemtexta. Siamo alla linea 004017D5, ed iniziamo a steppare. Per prima cosa dobbiamo localizzare la routine del controllo del serial. Il primo passo che vi consiglio è di steppare fino ad incontrare la messagebox di errore. La incontrate alla linea 00401870. Ovviamente steppate con F10. Quindi adesso abbiamo dato due linee di confine: la prima è 004017D5 e la seconda è 00401870. Nell'intervallo di codice contenuto da queste due righe s'è sicuramente la routine di calcolo del serial. I furbetti avranno già notato che alla linea 00401870 c'è una call, e che qualche linea prima c'è un jump condizionato: vuoi vedere che invertendo quel jump posso registrarmi? Il ragionamento è giusto, ma vedrete che occorrerà cambiare un altro jump per registrarvi: noi qui ci occupiamo del serial fishing quindi lascio a voi il compito di trovare il jump :-). Adesso iniziamo lo studio vero e proprio della routine che abbiamo localizzato:

 

 

qui è dove arriviamo dopo aver breakto 2 volte

:004017D5   xor esi, esi                                  azzera esi

:004017D7   xor edi, edi                                 azzera edi (saranno usati come contatori)

:004017D9   mov al, byte ptr [ebp+esi-00000088]       mette in al il char n-esimo del nome

:004017E0   cmp al, 00                                  controlla se il char è zero

:004017E2   je 0040184A                              se lo è il nome è finito, quindi salta

:004017E4   mov cl, byte ptr [ebp+esi-00000040]        mette in cl un valore

:004017EB   xor al, cl                                     xor di al con cl

:004017ED   mov cl, byte ptr [ebp+esi-00000020]       mette in cl un altro valore

:004017F4   xor al, cl                                     altro xor di al con cl

:004017F6   mov cl, byte ptr [ebp+esi-00000060]        di nuovo mette in cl un valore

:004017FD   xor al, cl                                    di nuovo xor di al con cl

notate che i valori caricati in cl sono diversi per ogni iterazione

 

:004017FF   mov bl, byte ptr [ebp+edi-000000D0]    mette in bl l'n-esimo char del serial

:00401806   sub bl, 30                                                gli sottrae 30h

:00401809   cmp bl, 09                                               controlla se è 9

:0040180C   jle 00401811                                          se è minore o uguale salta

:0040180E   sub bl, 07                                                altrimenti sottraigli 07

:00401811   mov dl, byte ptr [ebp+edi-000000CF]    mette in dl il char successivo del serial

:00401818   sub dl, 30                                                 ripeti le sottrazioni

:0040181B   cmp dl, 09                                              poi vedremo a cosa servono

:0040181E   jle 00401823

:00401820   sub dl, 07

:00401823   shl bl, 04                                                 sposta bl di 04 a sinistra

:00401826   add dl, bl                                                 adesso ha ottenuto come byte il valore formato dai due char del serial

questo pezzo serve a prendere due char del serial e a trasformarli nel relativo byte: ad esempio, se inserisco nel serial AB, il loro significato ascii è 41 42, quindi gli sottraggo 30h se sono numeri, o 37h se sono cifre, in modo che otterrò il valore corrispondente in hex: in questo esempio sottraggo 37 a 41 e 42, e ottengo 0A 0B, poi shifto a sinistra di 4 il primo byte e ottengo A0, gli sommo il secondo byte ed ottengo in hex il valore AB in un byte

 

:00401828   inc esi                                                      incrementa i contatori

:00401829   inc edi                           edi lo incrementa 2 volte perchè il seriale viene lavorato una coppia alla volta,

:0040182A   inc edi                         quindi ad ogni iterazione deve puntare non al char successivo, ma alla coppia successiva

:0040182B   cmp al, dl                       confronta il char xorato del nome con il char del serial

:0040182D   je 004017D9                    se sono uguali continua a calcolare i prossimi char

:0040182F   mov [ebp-01], FF                 altrimenti metti FF nella locazione ebp-04

:00401833   mov al, byte ptr [ebp+esi-00000088]   adesso metti il prossimo char in al

:0040183A   inc esi                              incrementa il contatore

:0040183B   cmp al, 00                           controlla se il char è zero

:0040183D   jne 00401833                         se non lo è continua con il prossimo char

:0040183F   cmp esi, 00000006                    controlla se il contatore è minore di 6

:00401842   jge 00401857                         se lo è non saltare

:00401844   mov [ebp-01], DD                     metti DD in ebp-01

:00401848   jmp 00401857

:0040184A   mov [ebp-01], CC                     metti CC in ebp-01

:0040184E   cmp esi, 00000006                    controlla che il contatore sia maggiore di 6

:00401851   jge 00401857

:00401853   mov [ebp-01], DD

:00401857   mov bl, byte ptr [ebp-01]

:0040185A   cmp bl, FF                            controlla bl

:0040185D   jne 00401893                         e salta se non è FF

:0040185F   mov esi, dword ptr [ebp-64]

:00401862   push 00000010

:00401864   push 004030D4

:00401869   push 004030B4

:0040186E   mov ecx, esi

:00401870   Call 00401B40                         chiamata ad Errore

 

bene, dovrebbe essere tutto chiaro. Vediamo tutte le parti in dettaglio ora. La prima parte, quella con i tre xor di al con cl. Vedete che viene preso un carattere del nome ad ogni iterazione e viene xorato con tre valori diversi. Questi valori che vengono caricati in cl sono diversi ad ogni iterazione. Dopo i tre xor otteniamo in al un valore, che sarà quello che verrà usato nel confronto finale. Quindi dobbiamo sapere dove prende i valori che usa per xorare le lettere del nome. Bene, adesso steppate fino alla linea  004017E4

:004017E4   mov cl, byte ptr [ebp+esi-00000040]

e qui cerchiamo di capire che succede. Viene richiamato un valore dalla locazione ebp+esi-40. Ricordate che esi è usato come contatore? Quindi ebp conterrà una tabella di valori, e di volta in volta viene caricato il successivo. Visualizzate il contenuto della locazione con

d  ebp+esi-40

potete anche visualizzare con

d ebp-40

così siete sicuri che punterete al primo elemento della tabella. Vi verrà mostrata la locazione 0063F7E4 in cui sono contenuti tanti valori! Il seriale può essere massimo di 30 char, quindi i valori di questa tabella saranno 30, e precisamente sono

10, 12, 14, ..., 64, 66, 68

e quindi il primo verrà usato per lo xor con il primo carattere, il secondo per lo xor con il secondo carattere, etc.. Analogamente, visto che gli xor sono 3, ci ricaviamo le altre due tabelle: andiamo sulla linea

:004017ED   mov cl, byte ptr [ebp+esi-00000020]

e se visualizzate ebp+esi-20 otterete la seconda tabella valori

11, 13, 15, ..., 65, 67, 69

che fantasia! Ehi Que, ma a chi li fai fare 'sti crackme? Dove li prendi 'sti beoti???

E prendiamo la terza tabella: andate alla riga

:004017F6   mov cl, byte ptr [ebp+esi-00000060]

e anche qui visualizzate ebp-60. Otterete la terza tabella:

FF, FE, FD, ... , BE, BD, BC, BB

perchè tutto questo? Avendo tutti i valori delle tre tabelle possiamo calcolarci il serial: infatti ogni char del nome viene xorato con il rispettivo char di ogni tabella, e quello che otteniamo è il valore che viene usato per il compare che decide se il seriale è giusto o sbagliato. Quindi, adesso prendo la calcolatrice del winsucks e mi calcolo un seriale giusto per il mio nome (Andrea):

A  =  41

41 xor 10 = 51

51 xor 11 = 40

40 xor FF = BF

 

n  =  6E

6E xor 12 = 7C

7C xor 13 = 6F

6F xor FE = 91

 

d  =  64

64 xor 14 = 70

70 xor 15 = 65

65 xor FD = 98

 

r  =  72

72 xor 16 = 64

64 xor 17 = 73

73 xor FC = 8F

 

e  =  65

65 xor 18 = 7D

7D xor 19 = 64

64 xor FB = 9F

 

a  =  61

61 xor 20 = 41

41 xor 21 = 60

60 xor FA = 9A

 

i numeri in grassetto sono quelli che compongono il serial esatto: BF91988F9F9A. Abbiamo trovato il serial! Comunque, continuiamo l'analisi della routine per capirla fino in fondo. Abbiamo visto come viene generato il serial. Vediamo come  il prog prende il serial. Il problema è che se il serial giusto deve essere 123456, quando inseriamo le cifre noi effettivamente non inseriamo 123456 ma inseriamo i valori ascii 31,32,33,34,35,36. Dalla linea 004017FF comincia la conversione del serial inserito, cioè in al abbiamo il serial esatto ottenuto dal nome, ad esempio 12, e nel serial abbiamo i corrispondenti byte 3132. Quindi ecco come il prog converte da ascii a dec:

-prende il numero 31h

-gli sottrae 30h ed ottiene 01, valore hex desiderato

-se invece del numero avevamo inserito una lettra, ad esempio A, per ottenere il corrispondente hex avremmo dovuto sottrargli ancora 07. Conoscete la tabella ascii vero?

-dopo aver ottenuto 01, prende il char successivo, cioè 32h

-gli sottrae 30h ed ottiene 2 (stesso discorso di prima, se inserivate una lettera sottraeva altri 7)

-adesso ho due byte: 01, 02. Fa uno shift a sinistra del primo byte in modo che diventa 10h

-somma i due byte, 10h + 02 = 12h che è il valore che volevamo ottenere

e quindi confronta questo valore con il valore ottenuto tramite i calcoli sul char del nome. Se sono uguali, allora continua il confronto con il prossimo char del nome, altrimenti vuol dire che il serial è errato, quindi prendo FF come messaggio di errore ed esco dalla routine (il jump alla linea 0040182D). Poco dopo tale jump, c'è un pezzo di codice (da 00401833 a 00401842) che conta tutti i caratteri del nome. Se sono più di sei, salta verso l'errore tenendo il valore FF. Se sono meno di sei, allora carica il valore DD e salta di nuovo verso errore. Inoltre, se abbiamo inserito un serial esatto, il prog salta alla linea 0040184A, dove viene preso il valore CC. La linea successiva viene controllato il contatore esi, e se è minore di 6 vuol dire sì che il serial era esatto, ma era minore di 6 char, quindi carica il valore DD come errore. Quindi, ricapitolando:

il valore FF è l'identificativo dell'errore di " Serial Errato "

il valore DD è l'identificativo dell' errore di " Nome minore di 6 char "

infine il valore CC non ha importanza come valore identificativo, ma come valore diverso dai primi due: infatti il programma valuta le due precedenti condizioni con un AND, in alto livello sarebbe

if (errore != 0xFF AND errore != DD)

{

       serial corretto

}

ecco perchè modificando un solo jump non potevate registrarvi!

E con questo si conclude il 5° corso newbies. Se ci sono problemi o passaggi che non avete capito chiedete in ML o alla mia mail.

Ricordatevi che l'unico modo per imparare è arrivarci da soli!

Ciauuuuuuuuuuuuuuuuuuuuuz

AndreaGeddon

 

                                                                                         Note finali

Un saluto a tutta la ML. Ciauuuz ! Un saluto al mio 24 in algebra! Un saluto alla donzella nuova iscritta (e anche uno alle donzelle che già c'erano). Un salutino ino ino a Que, uno a cod, uno a Syscalo etc. etc. ciauz! AndreaGeddon

Disclaimer

Queste informazioni sono solo a scopo puramente didattico. Ricordate di comprare il crackme se volete continuare ad usarlo dopo 15 giorni :-P

                                                                          UIC's page of reverse engineering, scegli dove andare:

Home   Anonimato   Assembly    CrackMe   ContactMe   Forum   Iscrizione      
       Lezioni    Links   Linux   NewBies   News   Playstation        
  Tools   Tutorial   Search   UIC Faq

                                UIC