Imagine Crackme Tutorial

Data

by BLACKDEATH

 

Gennaio 2002

UIC's Home Page

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
Canale:  #crack-it
Nickname: _Blackdeath_

....

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.