Zoom Icon

Corso UIC Newbies 05

From UIC

Corso newbies 5° - Troviamoci il seriale

Contents


Corso UIC Newbies 05
Author: AndreaGeddon
Email: Email
Website: http://andreageddon.8m.com
Date: 12/06/2000 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments: Qualche mio eventuale commento sul tutorial :)))
Qualche MIO eventuale commento sui tuoi eventuali commenti!!!


Introduction

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

Softice


Link e Riferimenti

Questo è il Corso UIC Newbies n°05 disponibile alla pagina Corsi UIC Newbies


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


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

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.