Il nostro secondo Crack - Update al 10/2003
(Bentornati, siete pronti per la vostra seconda scorribanda?)

Data

by "Quequero"

 

04/Oct/2003

UIC's Home Page

Published by Quequero

Una guida è in realtà qualcuno a cui NON chiedi di insegnarti quello che sa o quello che fa; vuoi solo che lo faccia
per te. Colui cui chiedi di trasmetterti insegnamenti è un maestro, non una guida;

Bentornati, se siete qui e' perche' il primo tutorial vi e' piaciuto, benissimo, allora andiamo avanti col secondo :)

e spesso accade che un maestro sia chiamato a svolgere anche
il ruolo di guida, ma mai il contrario.

Olga (aka Alga)... Una donna unica e speciale sotto ogni aspetto, ciao Alguzza :***

....

Home page: http://quequero.org
E-mail: quequero (at) bitchx (dot) it
Nick: Quequero, #crack-it irc.azzurra.org

....

Difficoltà

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

 
Video

Introduzione

Nel corso del primo tutorial, grazie ad uno sporco trucco :), sono riuscito a farvi trovare il seriale del programma piuttosto che lasciarvelo crackare, stavolta invece niente sporchi trucchi... Lo crackiamo davvero.

Tools usati

Windows XP/2k/2k3
OllyDbg

Command Line Plugin nell'archivio c'e' una dll, mettetela nella dir dove si trova anche ollydbg.exe
HexWorkshop ma qualunque editor esadecimale va piu' che bene.

URL o FTP del programma

Winimage v6.1

Notizie sul programma

Il programma che andremo a crackare e' WinImage 6.1, serve a fare delle immagini di dischi ma non c'importa... Crackiamolo :)

Essay

Prima di iniziare ricapitoliamo quello che abbiamo imparato nel precedente tutorial:
Sui nostri processori sono presenti 4 General Purpose Registers (GPR) che si chiamano:
- EAX, EBX, ECX, EDX, sono grandi 32-bit (4 byte) e possono contenere tutti numeri fino a 32-bit di grandezza.
- I GPR possono essere suddivisi in sottoelementi da 16 e 8 bit: EAX -> AX (16-bit), AH (8-bit), AL (8-bit). EBX -> BX, BH, BL etc...
- Gli altri registri: ESI, EDI, ESP, EBP, sono grandi 32-bit e possono essere suddivisi solo in sottoelementi da 16-bit: ESI -> SI, ESP -> SP etc...
- Se diciamo che un registro "punta al seriale", vuol dire che contiene un numero che indica l'indirizzo di memoria al quale si trova il seriale.
- Se vogliamo prelevare un byte dall'indirizzo al quale punta un registro e metterlo altrove, dobbiamo usare le [], esempio: MOV AL, [EBX].
- MOV copia un valore in memoria o in un registro: MOV EAX, 2 -> Copia 2 in EAX, MOV AL, [ECX] -> Copia in AL il byte puntato da ECX
- TEST EAX, EAX o anche TEST ECX, ECX... Mette a (setta) 1 lo ZeroFlag se il registro e' uguale a 0.
- CMP EAX, EBX confronta EAX con EBX (potete usarlo su ogni registro) e mette lo ZeroFlag a 1 se i due operandi sono uguali.
- JNZ/JNE JZ/JE effettuano un salto rispettivamente se: lo ZeroFlag e' 1, lo ZeroFlag e' 0. JMP salta sempre.
- CALL esegue una sottoroutine (in pratica esegue una funzione).
- ADD EAX, 2 aggiunge 2 a EAX; SUB EDX, 3 sottrae 3 a EDX.
- Il breakpoint indica al Debugger dove fermarsi all'interno del codice del programma.
- Premendo F8 all'interno di OllyDbg siamo in grado di eseguire una alla volta le istruzioni che vediamo (Stepping).

A questo punto le fondamenta dell'assembly sono piantate, mancano due istruzioni fondamentali che avremo modo di vedere piu' tardi, ora crackiamo questa bestiola.
Unzippate WinImage, aprite OllyDbg e caricate WinImage.exe, premete il pulsantino play una volta, vi apparira' un NagScreen col pulsante OK... Premete anche quello, clickate su Options e poi Registering, ci viene chiesto un nome e un codice di registrazione. Mettete il solito nome e codice fittizi (io usero' Quequero e 666111666) ma non premete ok.
Allora, come si fa ad entrare nel programma? Stavolta utilizzeremo un approccio differente dal primo tutorial, l'altra volta abbiamo detto al debugger di fermarsi su MessageBox e siamo tornati indietro, stavolta invece faremo il contrario: diremo di fermarsi subito e proseguiremo fino alla messagebox :).
Le caselle in cui scrivete il testo si chiamano EditBox e si usano le solite API per prendere il testo da li, queste API in genere sono: GetWindowText e GetDlgItemText... Proviamo con la prima, premete Alt+F1 per aprire la CommandLine di Olly (se non fosse gia' aperta) e scrivete:
bpx GetWindowTextA (ricordate di stare attenti alle maiuscole e di inserire la A finale), appen fatto clickate su OK... Uhm... Appena clickate sulla finestra di WinImage il debugger non brekka... Strano vero? Ok, allora premete Alt+B e con Del (o Canc) cancellate tutti i breakpoint presenti nella BreakPoints window, premete Play per riavviare l'esecuzione del programma e tornate alla CommandLine, fate: bpx GetDlgItemTextA, tornate nella finestra di registrazione e premete OK.
Ottimo il debugger brekka, se si apre la finestra Intermodular Calls, non vi preoccupate... Chiudetela e tornate alla finestra principale col codice.
Bene ci siamo fermati un attimo prima che il nostro nome venga prelevato, se tutto e' andato bene dovreste star qui:

00422045 PUSH 101 ; Count = 101 (257.)
0042204A MOV EBX, winimage.00453B90
0042204F PUSH EBX ; Buffer = winimage.00453B90
00422050 PUSH 816 ; ControlID = 816 (2070.)
00422055 PUSH [ARG.1] ; hWnd = 002A0406 ('WinImage Registration',class='#32770',parent=00620070)
00422058 CALL ESI ; GetDlgItemTextA (noi siamo fermi qui)

Andiamo quindi sul solito msdn.microsoft.com o sulla vostra apireference (win32.hlp) e vediamo come funziona l'API GetDlgItemText:

UINT GetDlgItemText(
HWND hDlg,
int nIDDlgItem,
LPTSTR lpString,
int nMaxCount
);


Allora, il primo parametro e' l'handle del DialogBox, ovvero il numero che identifica in memoria la finestra.
Il secondo parametro e' il numero che identifica la nostra EditBox (se abbiamo due EditBox in una finesta, magari una si chiama 1 e l'altra 2, questo parametro serve a indicare da quale editbox vogliamo prelevare il testo), il terzo e' il buffer che conterra' il testo prelevato (fondamentale!) e il quarto e' il numero massimo di caratteri da prelevare, la nostra chiamata tradotta in C sarebbe:

GetDlgItemText(0x002A0406, 0x816, 00453B90, 0x101);

I numeri preceduti dal '0x' sono intesi in esadecimale, dovete pero' sapere che gli handle in linea di massima variano di continuo, quindi se ora l'handle della finestra e' 0x002A0406 al prossimo avvio quasi certamente sara' diverso!
Bene, adesso con F8 andiamo avanti di una riga e facciamo all'interno della CommandLine: "d ebx", per verificare cosa e' stato messo nel buffer... Guardiamo in basso e troviamo:

00453B90 51 75 65 71 75 65 72 6F 00 00 65 00 72 00 6F 00 Quequero..e.r.o.

Bene, la chiamata preleva il nome, ho evidenziato un byte per un motivo preciso, dovete sapere che per convenzione le stringhe vengono terminate dal byte 0, quindi il primo 0 che incontrate di sicuro termina la stringa (sotto dos si usava terminarle col carattere $ detto proprio terminatore). Ok, steppiamo con F8 fino alla seconda GetDlgItemText:

0042205A PUSH 7F  ; Count = 7F (127.)
0042205C MOV EDI, winimage.00453F00
00422061 PUSH EDI ; Buffer = 0012F038
00422062 PUSH 817 ; ControlID = 817 (2071.)
00422067 PUSH [ARG.1] ; hWnd = 002A0406 ('WinImage Registration',class='#32770',parent=00620070)
0042206A CALL ESI ; GetDlgItemTextA (se siete stati bravi... Vi trovate qui :)
 
 
Ok stavolta il buffer sta in EDI che punta all'indirizzo: 0012F038 (non vi preoccupate se da voi e' diverso), quindi steppate una riga dopo l'indirizzo 0042206A e dumpate EDI con: d edi, ci troverete:

00453F00 36 36 36 31 31 31 36 36 36 00 00 00 36 00 36 00 666111666...6.6.

Come sospettavamo... Viene prelevato il seriale (nel caso aveste dei dubbi sappiate che il primo parametro: 00453F00 e' l'indirizzo di memoria al quale si trova il primo byte del seriale, i numeri 36 36 36 etc sono la rappresentazione esadecimale del nostro seriale e 666111666 e' il nostro seriale in ASCII standard). Una curiosita': perche' invece di CALL GetDlgItemTextA c'e' un CALL ESI? Perche' ESI guardacaso punta proprio all'indirizzo della nostra API quindi se conosciamo l'indirizzo dell'API possiamo metterlo in un registro e chiamarla cosi invece che col suo nome esteso, carino vero? Ok a questo punto esaminiamo il codice:

0042206C PUSH winimage.0045386C ; Dovreste trovarvi qui
00422071 PUSH EDI
00422072 PUSH EBX
00422073 CALL winimage.0044208A ; Calcola il Seriale
00422078 MOV ECX, DWORD PTR [45386C]
 
Steppate fino all'indirizzo 00422073 e fate: "d edi" e poi "d ebx" e subito dopo c'e' una chiamata ad una subroutine... Come potete vede edi contiene il nostro seriale e ebx il nostro nome... Vuoi vedere che quella CALL calcola il numero che ci interessa??
E' giunto allora il momento di spiegare le ultime due istruzioni fondamentali: PUSH e POP :)
La maggior parte delle CALL ha bisogno di "argomenti" per essere eseguita, nel caso di GetDlgItemText sono 4: l'handle, il buffer etc... Vi faccio un esempio, devo realizzare una call che faccia la somma di due numeri, la progetto in modo che abbia bisogno di due parametri, il primo e il secondo addendo, lei poi tornera' (attenti a questo termine) il risultato, immaginatevi che sia fatta cosi:
Somma(1, 4);
La chiamata ha quindi bisogno di due parametri, nel nostro caso 1 e 4 e nessuno ci vieta di passarle 6 e 5... Dovete sapere che esistono due modi per passare parametri ad una sottoroutine, uno e' metterli nei registri... Ma se i registri non bastano?? Allora mettiamo i dati in memoria e la sottoroutine li prendera' dalla memoria e li elaborera', quando avra' finito dovra' pur mettere il risultato da qualche parte e questo risultato finira' o in EAX oppure anch'esso in memoria, ma non abbiate paura e' facile capire dove finisce.
Nel 99% dei casi i parametri saranno passati tramite lo stack, cioe' la memoria.
Quando eseguite un programma il sistema operativo apre il file, lo copia tutto nella RAM (se ci entra) e lo esegue.. Questo perche' l'harddisk e' estremamente lento mentre la ram e' piu' veloce, la memoria si chiama Stack che significa Pila... Come la pila dei piatti, e questo per un motivo: i dati entrano nell'ordine inverso in cui escono, pensate ai piatti, se ho 10 piatti e li metto uno sopra all'altro il primo che potro' prendere sara' l'ultimo che ho messo giusto?

PiattoTerzo
PiattoSecondo
PiattoPrimo

Il "disegno" dovrebbe chiarivi i dubbi :). Questo tipo di "pila" viene detta LIFO, ovvero: Last In First Out (l'ultimo che entra e' il primo che esce), ok e per mettere dati dentro la memoria usiamo un'istruzione: PUSH, per togliere dati invece usiamo POP, facile. Attenzione pero', essendo lo Stack una Pila, dobbiamo salvare i dati nell'ordine inverso... Immaginate che io debba salvare: 1, 2 e 3 su una pila, se facessi PUSH 1, PUSH 2 e PUSH 3 in memoria starebbero cosi:

3
2
1

Perche' lo stack e' LIFO, allora non potrei recuperarli in ordine, se pero' facessi: PUSH 3, PUSH 2, PUSH 1, allora avrei:

1
2
3

Potrei allora POParli fuori in ordine, mi rendo conto che non e' semplice... Ma voi pensate ai piatti :)
Ok, finiamo qui la digressione, avrete tempo e modo di capire lo stack piu' in la, riprendiamo dunque il nostro codice:

0042206C PUSH winimage.0045386C ; Dovreste trovarvi qui
00422071 PUSH EDI ; EDI punta al seriale (sullo stack viene messo il valore che contiene EDI ovviamente)
00422072 PUSH EBX ; EBX punta al nome
00422073 CALL winimage.0044208A ; Calcola il Seriale
00422078 MOV ECX, DWORD PTR [45386C]


Vengono salvati sullo stack tre argomenti, un indirizzo di memoria che non sappiamo cos'e' (sono 4 byte a 0, forse serve come appoggio per qualche calcolo), l'indirizzo contenuto in EDI (che punta al seriale) e quello contenuto in EBX (che punta al nome). Tanto per complicarvi la vita sappiate che lo stack cresce al contrario, cioe' cresce verso valori numericamente minori, vuol dire che quando e' totalmente vuoto lo stack inizia a 0xFFFFFFFF (che e' il valore piu' grande che puo' assumere un registro a 32-bit), quando e' tutto pieno finira' invece a 0x00000000, non ve lo dico per confondervi ma devo spiegarvi cosa succede. Immaginate un lampadario attaccato al soffitto tramite un elastico, la cima dello stack sara' il lampadario, la base dello stack sara' dove il cavo si infila nel muro :), se tirate il lampadario (fate crescere lo stack) la CIMA si avvicinera' a terra e il lampadario sara' cresciuto in altezza verso il basso :)... Ora sappiamo che:

EDI = 00453F00 (ovvero l'indirizzo di memoria dove si trova il seriale)
EBX = 00453B90 (l'indirizzo di memoria dove si trova il nome)

Questi numeri non li sto tirando a caso, potete vederli nella finestra di destra di Olly :))). Un'altra novita', il registro ESP punta alla CIMA dello stack (SP sta infatti per Stack Pointer), e' per questo che vi ho fatto l'esempio del lampadario proprio per introdurvi il registro ESP. Quindi steppiamo con F8 fino all'indirizzo 00422073, in questo preciso istante lo stack sara' cosi formato:

ESP+8 -> 0045386C ; Il buffer
ESP+4 -> 00453F00 ; Il Serial
ESP -> 00453B90 ; Il nome

Se non ci credete dumpate lo stack, "d esp":

0012EFA8  90 3B 45 00 00 3F 45 00 6C 38 45 00 38 F0 12 00  ;E..?E.l8E.8ð.


Uhm non li vedete? Beh in realta' ci sono... Ma sono scritti ribaltati (si tanto per complicarvi la vita, ma non date la colpa a me datela alla Intel :), questa e' detta notazione LittleEndian, cioe' il byte meno significativo e' il primo :) percio' se salviamo sullo stack:

12 34 56 78 -> diventera': 78 56 34 12

Farete l'abitudine anche a questo col passar del tempo. Ok quindi abbiamo sullo stack tre parametri, non ci importa che sono messi ribaltati tanto quando andremo a riprenderli dallo stack saranno dritti, ora dobbiamo entrare nella subroutine chiamata all'indirizzo 00422078, e questo e' quantomai semplice, se ancora non ci siete portatevi con F8 fin sopra la CALL e quindi premete F7, zap, tramite un warp siamo arrivati qui:

0044208A PUSH EBP ; Voi siete qui
0044208B MOV EBP, ESP
0044208D SUB ESP, 200
00442093 PUSH ESI ; USER32.GetDlgItemTextA
00442094 MOV ESI, [ARG.3]
00442097 TEST ESI, ESI ; USER32.GetDlgItemTextA
00442099 PUSH EDI ; winimage.00453F00


Finiamo quindi di spiegare la call, immaginate questo pezzo di codice:

00400000 CALL 00400006
00400004 MOV EBX, EAX
00400006 MOV EAX, EBX
00400008 RET

Arrivati all'indirizzo 00400000 il processore salva sullo stack il registro EIP (che ' un registro non modificabile dagli utenti e che contiene sempre l'indirizzo dell'istruzione che si sta eseguendo, provate a guardarlo dentro OllyDbg mentre steppate), quindi salta all'indirizzo 00400006, esegue l'istruzione e appena arriva sull'istruzione RET preleva dallo stack l'EIP che avevamo salvato prima, lo incrementa e fa un jump sul nuovo EIP (ovviamente EIP viene incrementato altrimenti il processore salterebbe di nuovo sulla call e la eseguirebbe all'infinito), il percorso sarebbe quindi: 00400000 -> 00400006 -> 00400008 -> 00400004, e poi ipotizziamo che si fermi. La CALL quindi e' un biglietto di andata e ritorno (perche' sa sempre dove tornare) mentre il JMP e' di sola andata... Perche' salta e non torna.
Come vedete trovare la fine di una subroutine e' facile in quanto basta trovare il RET. Ok, a questo punto, senza steppare, guardate un pochetto il codice, dovreste trovare delle call a strcmp, questa funzione (che non e' un'API ma una funzione standard del C) serve a confrontare due stringhe tra loro, e mette EAX = 1 se le stringhe sono diverse, altrimenti mette EAX = 0 se le stringhe sono uguali, ora che sappiamo come funziona una call andiamo a vedere cosa viene confrontato, steppate con F8 (F7 si usa solo quando dovete ENTRARE in una call) fino all'indirizzo 004420DE:

004420DB POP ECX
004420DC POP ECX
004420DD PUSH EAX
004420DE CALL <JMP.&CRTDLL.strcmp> ; strcmp, voi siete qui

Uhm, qualcosa non quadra, vi avevo detto che strcmp prendeva due parametri, la prima e la seconda stringa... Qui vediamo un push solo prima della chiamata... Ci sta fregando? Verifichiamo, EAX viene salvato sullo stack, vediamo cosa contiene: "d eax":

0012EDA0 45 31 38 46 33 31 00 00 40 10 01 A5 00 00 00 00 E18F31..@¥...

Un numerello... Non ci interessa molto, per quello che e' scritto in verdino sbiadito ;ppp, e l'altro parametro? Beh a rigor di logica se non c'e' un push stara' gia' sullo stack giusto? Verifichiamo: "d esp+4":

0012ED94 A0 EE 12 00 00 3F 45 00 8D D7 CF 77 45 31 38 46 î..?E.×ÏwE18F

Vi dice niente quel numerello?? Provate a ribaltarlo e dumpatelo: "d 0012EEA0":

0012EEA0 36 36 36 31 31 31 36 36 36 00 CD 77 00 00 00 00 666111666.Íw....

Eh eh massi e' un nostro caro amico... Il serial fittizio (speriamo non se la prenda a male se lo chiamiamo cosi :)), bene, il nostro serialone viene confrontato con un numerino verde-sbiadito... E poi che succede?

004420DE CALL <JMP.&CRTDLL.strcmp> ; strcmp, voi siete qui
004420E3 TEST EAX, EAX
 ; Se EAX = 0, cioe' se le due stringhe sono uguali...
004420E5 POP ECX ; 0012EDA0
004420E6 POP ECX ; 0012EDA0
004420E7 JE winimage.0044218D
 ; ... Salta all'indirizzo 0044218D

C'e' un jump-uzzo, quindi se strcmp ritorna 0 in eax (ovvero le due stringhe sono uguali), salta... Vuoi vedere che se cambiamo quel JE in JMP abbiamo una registrazione bella e pronta??? Facciamo una prova, steppiamo fino a 004420E7 (o selezionate quella riga col mouse), premiamo la barra spazio e scriviamo: JMP 0044218D, assicuratevi che la casella "Fill with NOP's" sia checkata (NOP non lo conoscete? Argh ci vorrano almeno 500 pagine per spiegarvi cosa fa... Dunque... Avete presente i politici in italia? Ecco, cosa fanno quelli? Nulla? Esatto! NOP e' come loro non fa assolutamente nulla, significa infatti: NO oPeration, non fa niente quindi ;p) e come per magia il codice da:

004420E7 0F84 A0000000 JE winimage.0044218D

Diventera':

004420E7 E9 A1000000 JMP winimage.0044218D
004420EC 90          NOP

Bene, premete Play e vedete cosa succede.. Ihihih siamo registrati :)))... Ma fermi un secondo... Se disabilitate i breakpoints e riavviate il programma.. Siamo di nuovo in modalita' UNregistered, perche'? Beh il programma all'avvio ha ricontrollato il seriale, ha visto che l'avevamo fregato e ci ha beccati... E poi la modifica che abbiamo fatto era in memoria non sul file vero percio', sperando che la routine di controllo sia solo una (ma tanto volte non e' cosi), andiamo a modificarla per davvero, come si fa? Presto detto, aprite winimage.exe con il vostro HexEditor preferito, dobbiamo trovare gli opcode che abbiamo cambiato ovvero: 0F84 A0000000, ma se cercate nell'hexeditor ce ne saranno a palate, percio' facciamo una cosa, prendiamo un po di opcodes prima e un po' dopo :), quindi all'indirizzo 004420E7 clickate col tasto destro del mouse e fate "Follow in Dump | Selection", in basso vedrete i byte che si trovano a quell'indirizzo:

004420E7 0F 84 A0 00 00 00 8D 85 00 FF FF FF 50 8D 87 48 „ ...….ÿÿÿP‡H
004420F7 19 05 14 50 8D 85 00 FE FF FF 50 E8 37 FF FF FF P….þÿÿPè7ÿÿÿ
00442107 59 59 50 E8 6B 02 00 00 85 C0 59 59 74 78 8D 85 YYPèk..…ÀYYtx.


Copiate quelli che ho evidenziato... Dovrebbero essere abbastanza per essere unici all'interno del file, pastateli nel search (levate gli spazi mi raccomando) e premete Search (se usate HexWorkshop assicuratevi che facciate la ricerca in "Hex Values" e quando vi chiede di fare il backup rispondete di si), chiudete OllyDbg altrimenti non potrete salvare il file, e cambiate quel: 0F84A0000000 in E9A100000090, raga mi raccomando... Dovete cambiare i byte non INSERIRNE di nuovi :) altrimenti non funzionera' piu' nulla :))).
A questo punto se avete fatto tutto bene... Il programma e' crackato, potete utilizzarlo come volete... Ma questo seriale davvero non vogliamo trovarlo??? Eddai non fate i pigri :)... Se riflettete vengono fatti un sacco di strcmp con vari numeri, vuoi vedere che alla fine di tutto verra' confrontato il nostro seriale con quello giusto? Boh proviamo, arrivate fin qui:

004421AC POP ECX
004421AD POP ECX
004421AE PUSH EAX ; s1 = "2E7DBCB"
004421AF CALL <JMP.&CRTDLL.strcmp> ; strcmp, voi siete qui!

Guardate, non dobbiamo neanche scomodarci a cercarlo, ce lo dice direttamente Olly, fate: "d eax":

0012EDA0 32 45 37 44 42 43 42 00 00 10 01 A5 00 00 00 00 2E7DBCB..¥....

Eh eh :) levate tutti i breakpoint, rimettete il file originale e mettete questo seriale (ovviamente col vostro nick sara' diverso!)... Wow registrati senza neanche crackare il programma... Che storia 'sto Reversing :). Ora siete pronti per guardare altri tutorial, buona fortuna!

Quequero

Note finali

Prima di tutto grazie ad evilcry per avermi suggerito in MailingList questo programma come target del tutorial, un saluto a tutte le MailingLists della UIC, a AndreaGay, Pincopall, Fobbo, Iro, Mary :*, Artemis :***, Spider, korn, DJK, s1mo e gli altri che non faccio a tempo a salutare perche' devo letteralmente fuggire ciaooooooooo....

Disclaimer

Vorrei ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo che ogni sviluppatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.

Reversiamo al solo scopo informativo e per migliorare la nostra conoscenza del linguaggio Assembly.