Imagine Crackme Tutorial |
||
Data |
by BLACKDEATH |
|
Gennaio 2002 |
Published by Quequero |
|
..... |
Grazie black, spider ne sara' felice :P |
..... |
.... |
Home page: http://blackdeath2002.cjb.net E-mail: [email protected] Server IRC:
irc.azzurra.it |
.... |
Difficoltà |
(X)NewBies ( )Intermedio ( )Avanzato ( )Master |
Spider's Image Crackme
(For Newbies)
Written by
Blackdeath
Introduzione |
Questo crackme scritto in Assembly da Spider è stato concepito in un modo del tutto diverso rispetto agli altri crackmes, poiche' come vedremo tra un po' fa uso di una tecnica insolita per fare il check(controllo) sul seriale inserito......cmq......non ci perdiamo in chiacchiere e passiamo a questo crackme davvero sfizioso :)
Ah.....dimenticavo..... nel file Leggimi.txt accluso al crackme, si dice che non è consentito usare il patching , ma qualsiasi altro tools :)
Tools usati |
- SoftIce 4.05 (in gergo "Sice")
- IceDump 6.023
I tools necessari li trovate sul mio sito.
Essay |
Come dicevo nell'introduzione al tutorial questo crackme sfrutta un tipo di check particolare sul serial......voi vi starete chiedendo....ma cosa avrà di tanto insolito questo check!? Beh....la risposta è: lo capirete in seguito :)
Prima di tutto carichiamo il crackme e con il mouse selezioniamo Help->Registra.
A questo punto ci ritroveremo davanti una bella finestra che ci chide il fatidico serial. La prima cosa che ci viene in mente è quella di piazzare un bel breakpoint su funzioni del tipo : GetWindowText, GetWindowTextA, GetDlgItemText, GetDlgItemTextA (sono le funzioni che di solito usano i programmi per catturare il testo dalla finestra e farci i controlli dovuti :-P ) , beh.....proviamo allora......settiamo i seguenti breakpoints in Sice, premiamo Ctrl-D e scriviamo :
BPX GetWindowText
BPX GetWindowTextA
BPX GetDlgItemText
BPX GetDlgItemTextA
A questo punto non ci resta altro da fare che uscire da Sice premendo di nuovo Ctrl-D oppure il tasto F5.Una volta usciti proviamo ad inserire un serial qualsiasi nella finestra (nel mio caso "1234567").....ora se il crackme fara' uso di qualcuna di queste API per leggere il seriale dalla finestra ci ritroveremo in Sice non appena vi sara' una chiamata ad una qualsiasi delle funzioni su cui abbiamo settato i breakpoints. Per vedere se i breakpoints che abbiamo settato fungono, premiamo il pulsante OK, beh......NULLA!.....ed ora che facciamo????
Mhhhh.....un po' di Zen e ci ricordiamo della API HMemCpy che viene usata per copiare dati nella memoria, a questo punto settiamo un bel PBX(breakpoint) su questa API, ovvero entriamo in Sice, premiamo Ctrl-D e scriviamo BPX HmemCpy, poi usciamo premendo F5. A questo punto premiamo di nuovo il tasto OK e ...porkkk...niente da fare nemmeno questa API funge :P
Ma non diamoci per vinti! Proviamo quindi un altro tipo di approccio al problema J. Entriamo in Sice , cancelliamo i breakpoints precedenti con “ BC * ” piazziamo un breakpoint su MessageBoxA (è una API che serve per visualizzare messaggi) ed usciamo da Sice, fatto cio’ premiamo il tasto OK ed in un batter d’occhio ci troveremo in Sice! FINALMENTE! J
A questo punto premiamo F12 e ci comparirà la finestra col messaggio di errore :P , clicchiamo sul tasto OK della Messagebox di conferma e ci ritroveremo in Sice all’interno del codice del nostro crackme al punto successivo dove c’è stata la chiamata alla funzione MessageBoxA.
Ed ora viene il bello! Hihihih J
Analizziamo un po’ il codice precedente alla chiamata alla MessageBoxA premendo i tasti Ctrl-PageUP…….notiamo una chiamata alla funzione grafica GetPixel…..cosaaaaaa GetPixel???….e perché mai una chiamata a questa funzione grafica? Mhhh…….procediamo nell’analisi del codice precedente notiamo una caterva di chiamate a questa funzione…..mhhh….a questo punto vi starete chiedendo il perché dell’uso di questa funzione grafica, beh questa è la tecnica particolare di cui vi parlavo nell’intro, Spider anziché creare un algoritmo di generazione/controllo serial su caratteri, fa addirittura un controllo sui PIXEL del testo immesso nella finestra. Mi spiego meglio.
La finestra in cui immettiamo il testo puo’ essere vista come un grigliato XY ed i caratteri come un’insieme di pixel neri (sono i # del grigliato seguente), i numeri sotto in orizzontale rappresentano la coordinata X e quelli in verticale la coordinata Y.
0 |
# |
# |
# |
# |
# |
# |
# |
|||||||||||
1 |
# |
# |
# |
# |
# |
# |
||||||||||||
2 |
# |
# |
# |
# |
# |
|||||||||||||
3 |
# |
# |
# |
# |
# |
|||||||||||||
4 |
# |
# |
# |
|||||||||||||||
5 |
# |
# |
# |
|||||||||||||||
6 |
# |
# |
# |
# |
||||||||||||||
7 |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
# |
|||||
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Bene, a questo punto penso proprio che vi sia chiaro il tipo di check che il crackme fa sul serial inserito, vero? J Della finestra di immissione del serial vengono controllati determinati pixel e se questi risultano di un certo colore si passa al controllo di un altro pixel, altrimenti si salta alla messagebox di errore, tutto chiaro ora?
Cerchiamo nell’API reference la funzione GetPixel (se non ce la avete procuratevela! E’ fondamentale per capire quali parametri vengono passati ad una funzione e cosa viene restituito come risultato della chiamata) che ha la seguente sintassi :
The GetPixel function retrieves the red, green, blue (RGB) color value of the pixel at the specified coordinates.
COLORREF GetPixel(
HDC hdc, // handle of device context
int XPos, // x-coordinate of pixel
int nYPos // y-coordinate of pixel
);
Parameters
hdc
Identifies the device context.
nXPos
Specifies the logical x-coordinate of the pixel to be examined.
nYPos
Specifies the logical y-coordinate of the pixel to be examined.
I parametri da passare alla funzione sono:
1) L’handle device context
2) La coordinata X del pixel
3) La coordinata Y del pixel
e restituisce in uscita in EAX il colore del pixel in formato RGB (Red/Green/Black) in esadecimale (es: per il bianco è R=FF, G=FF, B=FF, quindi EAX=00FFFFFF), nel nostro caso evidentemente il controllo verrà fatto unicamente sul colore bianco o nero, vi pare? J. Ora il problema è : su quali pixel viene fatto il controllo? Come fare a controllare tutti i pixel? Una soluzione sarebbe quella di controllare tutte le chiamate alla GetPixel ed identificare i pixel sui quali viene fatto il check ed il colore richiesto affinchè il controllo abbia esito positivo. State pensando di controllare tutti i pixel sui quali viene fatto il check? Nooooooooo…vero? Sarebbe da pazzi o quanto meno da veri “lameri” steppare tutte le chiamate alla GetPixel (avete fatto caso a quante sono??? :P), quindi adotteremo un piccolo stratagemma per aggirare l’apparente problema. La funzione GetPixel fa parte della libreria GDI32.DLL e serve per fare un controllo sul colore di un pixel, ora vi pare che non vi sia una funzione che invece di ritornare il colore di un pixel permetta di assegnarne il colore? SI che esiste….infatti se provate a fare un’anteprima (solo su Win9x) della libreria GDI32.DLL troverete nella marea di funzioni messe a disposizione dei programmatori , la funzione SetPixel, che come suggerisce il nome serve proprio per settare il colore di un pixel (ci tornerà molto utile). J
La sintassi usata per la SetPixel è la seguente (si trae sempre dall’API reference):
The SetPixel function sets the pixel at the specified coordinates to the specified color.
COLORREF SetPixel(
HDC hdc, // handle of device context
int X, // x-coordinate of pixel
int Y, // y-coordinate of pixel
COLORREF crColor // pixel color
);
Parameters
hdc
Identifies the device context.
X
Specifies the x-coordinate, in logical units, of the point to be set.
Y
Specifies the y-coordinate, in logical units, of the point to be set.
crColor
Specifies the color to be used to paint the point.
Notiamo che questa volta i parametri passati alla funzione sono ben 4 e non 3 come la precedente,
1) L’handle of device context
2) La coordinata X del pixel
3) La coordinata Y del pixel
4) Il colore da impostare al pixel J
L’approccio da me utilizzato per trovare il serial esatto è quello di sfruttare le chiamate alla GetPixel anziché per fare dei check sul colore dei pixel, per farci colorare di nero i pixel che vengono controllati! :))) In questo modo verranno visualizzati i pixel incriminati automaticamente e come vedremo sara’ visualizzato per intero il serial esatto!!!
Per sfruttare le chiamate alla GetPixel per far colorare in nero i pixel sui quali viene fatto il check, dovremo modificare in memoria il codice di quest’ultima.
Prima di procedere carichiamo le Exports (funzioni esportate) della libreria GDI32 in questo modo: carichiamo il Symbol Loader di Sice , premiamo il pulsante “Load Exports” e selezioniamo il file seguente:
c:\windows\system\gdi32.dll
dopodiche’ carichiamo l’IceDump, entriamo in Sice e scriviamo:
U GetPixel <- Disassembla il codice della funzione GetPixel
Otterremo il seguente codice :
GDI32!GetPixel
0167:BFF22332 B15D MOV CL,5D
0167:BFF22334 55 PUSH EBP
0167:BFF22335 8BEC MOV EBP,ESP
0167:BFF22337 51 PUSH ECX
0167:BFF22338 83EC3C SUB ESP,3C
0167:BFF2233B 66FF7508 PUSH WORD PTR [EBP+08]
0167:BFF2233F 66FF750C PUSH WORD PTR [EBP+0C]
0167:BFF22343 66FF7510 PUSH WORD PTR [EBP+10]
0167:BFF22347 FF159617F2BF CALL [BFF21796]
0167:BFF2234D C1E010 SHL EAX,10
0167:BFF22350 0FACD010 SHRD EAX,EDX,10
0167:BFF22354 C9 LEAVE
0167:BFF22355 C20C00 RET 000C
------------------- ------------------- -----------------------------------------
indirizzi opcodes codice assembly
L’indirizzo di partenza della funzione è BFF22332 e quello di fine funzione è BFF22355 (gli indirizzo potrebbero essere viversi dai vostri).La funzione occupa 38 bytes (26h in HEX), il numero di bytes occupati si calcola contando il numero di opcodes del codice cioè le coppie di caratteri presenti nella colonna che ho chiamato “opcodes” e scritti in corsivo.
Le chiamate alla GetPixel sono del tipo seguente:
0167:004011EF 6A06 PUSH 06 <-- Carica la coordinata Y sullo stack
0167:004011F1 6A06 PUSH 06 <-- Carica la coordinata X sullo stack
0167:004011F3 53 PUSH EBX <-- Carica l’handle of device context sullo stack
0167:004011F4 E8AF0C0000 CALL GDI32!GetPixel <-- Chiama l’API GetPixel
0167:004011F9 3BC6 CMP EAX,ESI <-- Confronta EAX(colore del pixel selezionato) con ESI
0167:004011FB 0F84550C0000 JZ 00401E56 <-- Se i colori sono diversi salta alla messagebox di errore
0167:00401201 6A07 PUSH 07
0167:00401203 6A05 PUSH 05
0167:00401205 53 PUSH EBX
0167:00401206 E89D0C0000 CALL GDI32!GetPixel
0167:0040120B 3BC6 CMP EAX,ESI
0167:0040120D 0F84430C0000 JZ 00401E56
…………………………………………………………….
0167:00401E56 6A30 PUSH 30 <|
0167:00401E58 6879304000 PUSH 00403079 |
0167:00401E5D 6826304000 PUSH 00403026 | MessageBox di
0167:00401E62 FF7508 PUSH DWORD PTR [EBP+08] | errore
0167:00401E65 E880000000 CALL USER32!MessageBoxA <|
0167:00401E6A 61 POPAD
0167:00401E6B C9 LEAVE
0167:00401E6C C21000 RET 0010
0167:00401E6F C9 LEAVE
0167:00401E70 C21000 RET 0010
0167:00401E73 55 PUSH EBP
0167:00401E74 8BEC MOV EBP,ESP
0167:00401E76 837D0C10 CMP DWORD PTR [EBP+0C],10
0167:00401E7A 750C JNZ 00401E88
Dal codice possiamo vedere in che modo i 3 parametri vengono passati alla GetPixel (vengono caricati nello stack nell’ordine inverso specificato nella API reference, poiché ci dobbiamo ricordare che lo stack funge con una logica di tipo LIFO (Last In First Out) , ossia l’ultimo parametro caricato sarà il primo ad essere scaricato dallo stack, in tal modo quando verra’ chiamata la funzione GetPixel i parametri caricati nello stack saranno prelevati nell’ordine giusto. Riporto una figura per rendere più chiare le idee:
………. |
|
|
………. |
|
|
Indirizzo dello stack pointer [ESP] -----------> |
F9114000 |
Indirizzo di ritorno della GetPixel corrente |
Indirizzo precedente + 4 bytes [ESP+04h]---> |
7EA00000 |
Valore dell’handle device context |
Indirizzo precedente + 8 bytes [ESP+08h]---> |
06000000 |
Coordinata X del pixel |
Indirizzo precedente + 12 bytes [ESP+0Ch] > |
06000000 |
Coordinata Y del pixel |
Fig.1 - La figura si riferisce ai parametri caricati nello stack a partire dall’indirizzo 004011EF fino alla chiamata della funzione GetPixel.
Nota: Quando si carica qualcosa nello stack, contrariamente a quanto si potrebbe pensare l’indirizzo del puntatore alla cima dello stack viene decrementato (e non incrementato), viceversa quando si preleva.
Prima di passare alla modifica della GetPixel facciamo una copia del codice della funzione originale sull’HDD. Per far cio’ assicuriamoci prima di tutto di aver caricato l’IceDump, entriamo in Sice e scriviamo :
/dump BFF22332 26 c:\getpixel.dat
------- -- ---------------
Indirizzo di partenza da cui iniziare a salvare. |
Numero di bytes da salvare |
Percorso del file di destinazione |
Con questo comando moooolto utile dell’IceDump abbiamo fatto una copia della funzione sul nostro HDD.
Ora arriva il bello!Si passa alla modifica della famosa funzione! :)))
Entriamo in Sice, cancelliamo tutti i breakpoint precedenti con “ BC * “ e settiamo un BPX sulla GetPixel, usciamo da Sice e premiamo il pulsante OK della finestra del serial.Ci troveremo così in Sice e potremo iniziare a modificare il codice.
Una volta entrati nel debugger scriviamo :
A (e poi invio)
così facendo il debugger si porterà nella modalità che ci permetterà di modificare il codice. Quello che vedremo visualizzato sara’ :
GDI32!GetPixel-----------------------------
0167:BFF22332 B15D MOV CL,5D
0167:BFF22334 55 PUSH EBP
0167:BFF22335 8BEC MOV EBP,ESP
0167:BFF22337 51 PUSH ECX
0167:BFF22338 83EC3C SUB ESP,3C
0167:BFF2233B 66FF7508 PUSH WORD PTR [EBP+08]
0167:BFF2233F 66FF750C PUSH WORD PTR [EBP+0C]
0167:BFF22343 66FF7510 PUSH WORD PTR [EBP+10]
0167:BFF22347 FF159617F2BF CALL [BFF21796]
0167:BFF2234D C1E010 SHL EAX,10
0167:BFF22350 0FACD010 SHRD EAX,EDX,10
0167:BFF22354 C9 LEAVE
0167:BFF22355 C20C00 RET 000C
----------------------------GDI32.text+....---------------------------
:break due to BPX GDI32!GetPixel (ET=…… secs)
:A
0167:BFF22332 ………. <---- Da questo punto in poi inseriremo il nostro codice come di seguito
0167:BFF22332 83C410 ADD ESP,10 <- Incrementa lo stack di 16 byte
0167:BFF22335 8B4424FC MOV EAX,[ESP-04] <-Salva la coordinata Y dallo stack
0167:BFF22339 8B7C24F8 MOV EDI,[ESP-08] <-Salva la coordinata X dallo stack
0167:BFF2233D 8B7424F0 MOV ESI,[ESP-10] <-Salva l’indirizzo di ritorno (indirizzo seguente alla chiamata di GetPixel)
0167:BFF22341 56 PUSH ESI <-Carica l’indirizzo di ritorno nello stack
0167:BFF22342 6A00 PUSH 00 <-Carica il valore 000000 nello stack (nero in RGB)
0167:BFF22344 50 PUSH EAX <-Carica la coordinata Y nello stack
0167:BFF22345 57 PUSH EDI <-Carica la coordinata X nello stack
0167:BFF22346 53 PUSH EBX <-Carica l’handle device context nello stack
0167:BFF22347 E8BDFFFFFF CALL GDI32!SetPixel <-Chiamata alla SetPixel (che ci svelerà il seriale)
0167:BFF2234C C3 RET <-Uscita dalla GetPixel e ritorno all’indirizzo successivo a quello ove era stata chiamata la funzione.
Bene a questo punto il lavoro è finito J Cancelliamo ogni breakpoint settato, F5…e...tadaaa vedremo comparire la messagebox di congratulazioni ed il seriale giusto nella finestra (AD8578FZZ7).
Nota: Per leggere il seriale cancelliamo il testo precedentemente introdotto, lasciando la finestra vuota in modo da poter visualizzare quello giusto.Il seriale sembra non leggersi del tutto per il semplice motivo che Spider non ha implementato un controllo su tutti i pixel che compongono i caratteri e quindi non vengono visualizzati del tutto :) ( quello giusto è AD8578FZZ7).
A questo punto, ottenuto il seriale giusto, consiglio di sostituire il codice da noi modificato con quello originale per evitare che qualche altro programma che fa uso della GetPixel vi faccia andare in crash il PC. Per ripristinare il codice originale entrate in Sice e digitate:
/load BFF22332 26 c:\getpixel.dat
Beh siamo così giunti alla conclusione di questo tutorial, spero di essere stato abbastanza chiaro. J
Chiunque abbia commenti in proposito, suggerimenti, critiche, dubbi o quant’altro mi invii un’e-mail : [email protected]
Ciauz alla prossima!
Blackdeath
Note finali |
Saluti a Spider, ph0b0s, Quequero, AndreaGeddon, Yado, kill3xx, case, albe,
Pbdz, TheMR, bubbo, deim0s, dades, DsE, Cieli Sereni, warfare, Master_Shadow,
[cHr]... e tutti gli altri frequentatori del chan #crack-it.
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 immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.