Zoom Icon

Lezione8 SepticConvulsion solution

From UIC

Soluzione Lezione 8 for Newbie 2009

Contents


Infos
Author: SepticConvulsion
Email: septicconvulsion@gmail.com
Website:
Date: 12/12/2008 (dd/mm/yyyy)
Level: Luck and skills are required
Language: Italian Image:Flag_Italian.gif
Comments:



Introduzione

Bel tute quello di §-Death_Reaver-§. Ho iniziato a leggerlo sbruffoneggiando e sono finito a fare il crackme prendendomi due o tre attacchi di cuore. Cosi' imparo a fare il figo :P
Il codice che segue e' praticamente pieno di label ... che vi devo dire, io le uso sempre e penso aiutino molto :)


Tools

OllyDBG 1.10
Filemon 7
Regmon 7
Guida API


Link e Riferimenti

Questo è la soluzione del CrackmeLezione8 disponibile alla pagina Lezione_8_File_and_Registry_Keys


Essay

Come prima operazione e' saggio mettere una label sugli indirizzi di "fallimento". Ovvero: andare all'indirzzo in cui tutti i jump mandano quando un test e' fallito e metterci una bella label che vi ricordi che queste istruzioni non sono da eseguire. Questo vi permettera' di capire a priori quali sono i salti condizionali che NON vanno fatti e, di conseguenza, quali sono le condizioni da soddisfare per passare i vari test. Nel mio caso ho labellato le istruzioni 004001556 e 0040154A come "fine_check" e "closehandle_fine": saltare a queste istruzioni significa che il requisito 3 non era stato assolto. Nella figura seguente vedete come appaiono i jump che non vanno presi.

Image:Sol7jpg.JPG

Iniziamo ad analizzare il codice come suggeritoci da §-Death_Reaver-§

00401297   > /8D81 6C304000 LEA EAX,DWORD PTR DS:[ECX+<stringa3>]     ;  mod3_1
0040129D   . |8A10          MOV DL,BYTE PTR DS:[EAX]                  ;  DL = stringa3(ECX)
0040129F   . |F6D2          NOT DL
004012A1   . |FEC2          INC DL                                    ;  DL = not( stringa3(ECX) ) + 1
004012A3   . |41            INC ECX                                   ;  ECX++
004012A4   . |83F9 12       CMP ECX,12                                ;  ECX == 18.?
004012A7   . |8810          MOV BYTE PTR DS:[EAX],DL
004012A9   .^\7C EC         JL SHORT Lezione7.00401297
004012AB   .  803D 6C304000>CMP BYTE PTR DS:[<stringa3>],31           ;  sempre vero
004012B2   .  75 1C         JNZ SHORT <Lezione7.mod3_3>               ;  mod3_2
004012B4   .  33C0          XOR EAX,EAX                               ;  EAX = 0
004012B6   >  8080 6C304000>ADD BYTE PTR DS:[EAX+<stringa3>],0F       ;  stringa(EAX) += 0Fh
004012BD   .  40            INC EAX                                   ;  EAX++
004012BE   .  83F8 12       CMP EAX,12                                ;  EAX == 18.?
004012C1   .^ 7C F3         JL SHORT Lezione7.004012B6
004012C3   .  803D 76304000>CMP BYTE PTR DS:[<check3_2>],58           ;  sempre vero
004012CA   .  0F85 7A020000 JNZ <Lezione7.closehadle_fine>
004012D0 > >  33C9          XOR ECX,ECX
004012D2   .  894C24 20     MOV DWORD PTR SS:[ESP+20],ECX
004012D6   >  8D05 6C304000 LEA EAX,DWORD PTR DS:[<stringa3>]         ;  mod3_3
004012DC   .  034424 20     ADD EAX,DWORD PTR SS:[ESP+20]             ;  EAX => stringa3(ECX)
004012E0   .  C008 02       ROR BYTE PTR DS:[EAX],2                   ;  ror stringa(ECX)
004012E3   .  80B1 6C304000>XOR BYTE PTR DS:[ECX+<stringa3>],63       ;  stringa(ECX) XOR 63h
004012EA   .  41            INC ECX                                   ;  ECX++
004012EB   .  83F9 12       CMP ECX,12                                ;  ECX == 12h?
004012EE   .  894C24 20     MOV DWORD PTR SS:[ESP+20],ECX
004012F2   .^ 7C E2         JL SHORT Lezione7.004012D6

Bene, il codice qui sopra consiste principalmente in 3 cicli for che mofidicano tutti i caratteri di "stringa3". Ognuno appare con una sintassi diversa, ma tutti e tre usano ECX come contatore dei cicli fatti e EAX per puntare alle varie entry dell'array. Notare che inizialmente stringa3 contiene dei valori che sembrano insensati.
Il primo for (00401297) usa EAX per puntare ogni elemento dell'array: la mette in DL, la nega, ci aggiunge uno e quindi lo riscrive. Il secondo prende un byte per volta e ci aggiunge 0F. Il terzo for (004012D0) sembra apparentemente diverso, ma in realta' il funzionamento e' sempre lo stesso! In questo caso viene utilizzato lo stack all'indirizzo "ESP+20" per fare puntare EAX alle entry dell'array.
Dopo aver analizzato bene questo codice, ci rendiamo conto che non c'e' necessita' intervenire qui: non c'e' nessuna possibilita' ne di alterare la stringa iniziale, ne possibilita' di fallimenti (infatti non ci sono jmp a inidirizzi di fallimento). Dobbiamo semplicemente fare eseguire questi tre cicli e, partendo da una sequenza di caratteri incomprensibili, ritroveremo in "stringa3" il seguente path "setup\texture.dat". In pratica i cicli serivano a decrittare la stringa originale! Andando avanti con il disassemblato troviamo:

004012F4   .  6A 1E         PUSH 1E                                  ; /n = 1E (30.)
004012F6   .  53            PUSH EBX                                 ; |c
004012F7   .  57            PUSH EDI                                 ; |s
004012F8   .  E8 45030000   CALL <JMP.&MSVCR80.memset>               ; \memset
004012FD   .  6A 0A         PUSH 0A                                  ; /n = A (10.)
004012FF   .  53            PUSH EBX                                 ; |c
00401300   .  68 C0344000   PUSH OFFSET <Lezione7.buf_reg2>          ; |s = OFFSET <Lezione7.buf_reg2>
00401305   .  E8 38030000   CALL <JMP.&MSVCR80.memset>               ; \memset
0040130A   .  83C4 18       ADD ESP,18                               ;  azzera buffer_reg e buffer
0040130D   .  E8 C3020000   CALL <Lezione7.trova_driver>             ;  AL = 0, no driver
00401312   .  84C0          TEST AL,AL
00401314   .  0F84 3C020000 JE <Lezione7.fine_check>

I due memset servono ad azzerare i dati contenuti nei due buffer. In 0040130D abbiamo una chiamata alla funzione "trova_driver" seguita da un test sul valore restituito. Da qui e' gia' presumibile che la funzione debba restituire un valore diverso da zero se non si vuole causare la fine del test.

Analizziamo il codice di "trova_driver":

004015D5 >/$  803D 68304000>CMP BYTE PTR DS:[<driver>],5A                    ;  EAX = buffer_reg, drive == Z?
004015DC  |.  56            PUSH ESI
004015DD  |.  77 20         JA SHORT <Lezione7.non_trovato_drive>            ;  critted_path2(0) > 5A salta
004015DF  |.  BE 68304000   MOV ESI,OFFSET <Lezione7.driver>                 ;  ASCII "H:\\"
004015E4  |>  FE05 68304000 /INC BYTE PTR DS:[<driver>]                      ;  critted_path2(0)++ = cambia la lettera del drive
004015EA  |.  56            |PUSH ESI                                        ; /RootPathName
004015EB  |.  FF15 30204000 |CALL DWORD PTR DS:[<&KERNEL32.GetDriveTypeA>]   ; \GetDriveTypeA
004015F1  |.  83F8 05       |CMP EAX,5                                       ;  5 = cd_rom?
004015F4  |.  74 13         |JE SHORT <Lezione7.trovato_drive>
004015F6  |.  803D 68304000>|CMP BYTE PTR DS:[<driver>],5A                   ;  drive == 'Z'?
004015FD  |.^ 76 E5         \JBE SHORT Lezione7.004015E4
004015FF >|>  FE05 68304000 INC BYTE PTR DS:[<driver>]
00401605  |.  32C0          XOR AL,AL
00401607  |.  5E            POP ESI
00401608  |.  C3            RETN
00401609 >|>  56            PUSH ESI                                         ; /src
0040160A  |.  BE F4344000   MOV ESI,OFFSET <Lezione7.path3_valido>           ; |
0040160F  |.  56            PUSH ESI                                         ; |dest => OFFSET <Lezione7.path3_valido>
00401610  |.  E8 27000000   CALL <JMP.&MSVCR80.strcpy>                       ; \strcpy
00401615  |.  68 6C304000   PUSH OFFSET <Lezione7.stringa3>                  ; /src = "setup\\texture.dat"
0040161A  |.  56            PUSH ESI                                         ; |dest
0040161B  |.  E8 28000000   CALL <JMP.&MSVCR80.strcat>                       ; \strcat
00401620  |.  83C4 10       ADD ESP,10
00401623  |.  B0 01         MOV AL,1
00401625  |.  5E            POP ESI

Si comincia controllando se la variabile "driver" sia uguale a 'Z'. In talcaso si restituisce 0 e finisce tutto li. Altrimenti si continua e si finisce nel ciclo for all'inirizzo 004015E4.

004015E4  |>  FE05 68304000 /INC BYTE PTR DS:[<driver>]                      ;  critted_path2(0)++ = cambia la lettera del drive
004015EA  |.  56            |PUSH ESI                                        ; /RootPathName
004015EB  |.  FF15 30204000 |CALL DWORD PTR DS:[<&KERNEL32.GetDriveTypeA>]   ; \GetDriveTypeA
004015F1  |.  83F8 03       |CMP EAX,5                                       ;  5 = cd_rom?
004015F4  |.  74 13         |JE SHORT <Lezione7.trovato_drive>
004015F6  |.  803D 68304000>|CMP BYTE PTR DS:[<driver>],5A                   ;  drive == 'Z'?
004015FD  |.^ 76 E5         \JBE SHORT Lezione7.004015E4

Il ciclo scorre tutte le lettere a partire da 'H:' a 'Z:' (incrementando "driver") e, tramite la API "GetDriveTypeA", ottiene in EAX la tipologia del drive corrispondente: se si tratta di un cd-rom (EAX==5) si passa alla seconda parte della funzione. Altrimenti, una volta giunti al driver 'Z:', la funzione restituisce 0 e il check fallisce.

00401609 >|> \56            PUSH ESI                                     ; /src
0040160A  |.  BE F4344000   MOV ESI,OFFSET <Lezione7.path3_valido>       ; |ASCII "C:\\setup\\texture.dat"
0040160F  |.  56            PUSH ESI                                     ; |dest => OFFSET <Lezione7.path3_valido>
00401610  |.  E8 27000000   CALL <JMP.&MSVCR80.strcpy>                   ; \strcpy
00401615  |.  68 6C304000   PUSH OFFSET <Lezione7.stringa3>              ; /src = "setup\\texture.dat"
0040161A  |.  56            PUSH ESI                                     ; |dest
0040161B  |.  E8 28000000   CALL <JMP.&MSVCR80.strcat>                   ; \strcat
00401620  |.  83C4 10       ADD ESP,10
00401623  |.  B0 01         MOV AL,1
00401625  |.  5E            POP ESI
00401626  \.  C3            RETN

Questa parte copia l'identificativo del drive cd-rom trovato in "path3_valido" e ci incolla dopo la stringa che e' stata decrittata da 00401297 a 004012F2 ("striga3"). Infine restituisce 1 (good!).
Riflettiamo su quanto si deve fare per passare questo check: lui cerca un drive cd-rom e, come vedremo dopo, cerchera' di leggerci qualche file. Quindi in teoria si dovrebbe avere un drive cd-crom assegnato ad una lettera da 'I:' a 'Z:' con dei dati scritti dentro. Il problema puo' essere risolto in molti modi (per esempio usare dei virtual drive, masterizzarsi dei cd), ma io da buon pigrone ho deciso semplicemente di modificare il programma nel seguente modo:

004015D5 803D 68304000 CMP BYTE PTR DS:[<driver>],5A

in

004015D5 C605 68304000 42   MOV BYTE PTR DS:[<driver>],42

In questo modo al posto di partire da 'H:', noi forziamo il ciclo for ad iniziare da 'B:' (42h = 'B' in ascii), che poi verra' incrementato a 'C:' prima della chiamata a GetDriveTypeA. Ho scelto di sovrascrivere proprio quel CMP perche' era l'unica istruzione di una lunghezza adatta che si trovasse prima della chiamata a GetDriveTypeA. Poi ho noppato il JA all'indirizzo 004015DD (tanto senza il CMP non serve piu'). Infine, un' ultima modifica:

004015F1  |.  83F8 05            |CMP EAX,5                                   ;  5 = cd_rom?

in

004015F1  |.  83F8 03            |CMP EAX,3                                   ;  3 = hd?

Con le precedenti modifiche la funzione cerchera' un hart-disk (codice 03) e, come primo tentativo, trovera' 'C:'. Cosi' posso tranquillamente lavorare sul mio hd normale senza tirare di mezzo cd e mazzi vari :) Quindi in "path3_valido" viene storato, nel mio caso, "C:\\setup\\texture.dat". Non so se andare a pathcare un crackme sia una cosa molto elegante, ma onestamente non avevo la minima voglia di tirare in mezzo cd e virtual drive e questa soluzione mi dava molto piu' controllo sul file da inserire.

Torniamo al codice principale.

0040131A   .  53                 PUSH EBX                                     ; /hTemplateFile
0040131B   .  68 80000000        PUSH 80                                      ; |Attributes = NORMAL
00401320   .  6A 03              PUSH 3                                       ; |Mode = OPEN_EXISTING
00401322   .  53                 PUSH EBX                                     ; |pSecurity
00401323   .  53                 PUSH EBX                                     ; |ShareMode
00401324   .  68 00000080        PUSH 80000000                                ; |Access = GENERIC_READ
00401329   .  68 F4344000        PUSH OFFSET <Lezione7.path3_valido>          ; |FileName = ""
0040132E   .  FF15 34204000      CALL DWORD PTR DS:[<&KERNEL32.CreateFileA>]  ; \CreateFileA
00401334   .  83F8 FF            CMP EAX,-1
00401337   .  A3 DC334000        MOV DWORD PTR DS:[<office_hand>],EAX
0040133C   .  0F84 14020000      JE <Lezione7.fine_check>
00401342   .  53                 PUSH EBX                                     ; /pOverlapped
00401343   .  8D4C24 18          LEA ECX,DWORD PTR SS:[ESP+18]                ; |
00401347   .  51                 PUSH ECX                                     ; |pBytesRead
00401348   .  6A 14              PUSH 14                                      ; |BytesToRead = 14 (20.)
0040134A   .  57                 PUSH EDI                                     ; |Buffer
0040134B   .  50                 PUSH EAX                                     ; |hFile
0040134C   .  FF15 10204000      CALL DWORD PTR DS:[<&KERNEL32.ReadFile>]     ; \ReadFile
00401352   .  57                 PUSH EDI                                     ; /s
00401353   .  E8 DE020000        CALL <JMP.&MSVCR80.strlen>                   ; \strlen
00401358   .  83F8 10            CMP EAX,10
0040135B   .  59                 POP ECX
0040135C   .  0F85 E8010000      JNZ <Lezione7.closehadle_fine>

Si richiama CreateFileA per aprire un file, il cui path e' salvato in "path3_valido". Se non trova nulla (EAX = -1) ci si fotte. Altrimenti in 0040134C si fa una ReadFile e si mette il contenuto in "buffer". La strlen seguente e il CMP fanno capire chiaramene che il file deve contenere almeno 16 caratteri (10h): metteteli!

A seguire ... un bellissimo delirio di test sulla stringa appena immessa! Nel codice seguente tutti gli indirizzi si riferiscono ad entry di "buffer". Io ho labellato ogni byte in oridine crescente, in modo da rendere piu' facile la lettura. Per es. bu12 = 12a entry di buffer. Con un po' di occhio si capisra' che, per ottenre un codice valido, non dobbiamo fare altro che risolvere un sistema di equazioni lineari: le incognite saranno le entry di "buffer" e le uguaglianze sono imposte dai vari CMP.

00401362   . |803D F8334000 73   CMP BYTE PTR DS:[<buffer>],73               ;  stringa_file3(0) == 73h = 's'
00401369   . |0F85 DB010000      JNZ <Lezione7.closehadle_fine>
0040136F   . |A0 04344000        MOV AL,BYTE PTR DS:[<bu12>]
00401374   . |3805 FE334000      CMP BYTE PTR DS:[<bu6>],AL
0040137A   . |0F85 CA010000      JNZ <Lezione7.closehadle_fine>
00401380   . |8A0D 06344000      MOV CL,BYTE PTR DS:[<bu14>]
00401386   . |3AC1               CMP AL,CL
00401388   . |0F85 BC010000      JNZ <Lezione7.closehadle_fine>
0040138E   . |8A15 FF334000      MOV DL,BYTE PTR DS:[<bu7>]
00401394   . |3A15 03344000      CMP DL,BYTE PTR DS:[<bu11>]
0040139A   . |0F85 AA010000      JNZ <Lezione7.closehadle_fine>
004013A0   . |803D FA334000 66   CMP BYTE PTR DS:[<bu2>],66
004013A7   . |0F85 9D010000      JNZ <Lezione7.closehadle_fine>
004013AD   . |803D 07344000 72   CMP BYTE PTR DS:[<bu15>],72
004013B4   . |0F85 90010000      JNZ <Lezione7.closehadle_fine>
004013BA   . |803D 02344000 72   CMP BYTE PTR DS:[<bu10>],72
004013C1   . |0F85 83010000      JNZ <Lezione7.closehadle_fine>
004013C7   . |803D FC334000 69   CMP BYTE PTR DS:[<bu4>],69
004013CE   . |0F85 76010000      JNZ <Lezione7.closehadle_fine>

Un modo veloce per capire il valore corretto di ogni entri e' vedere i CMP. Questi ci dicono che:

bu0 = 73h
bu12 = bu6 = bu14
bu7 = bu11
bu2 = 66h
bu15 = 72h
bu10 = 72h
bu4 = 69h.

Beeene, ora:

004013D4   .  0FB6C9             MOVZX ECX,CL
004013D7   .  0FB6C0             MOVZX EAX,AL
004013DA   .  03C8               ADD ECX,EAX
004013DC   .  0FB605 FE334000    MOVZX EAX,BYTE PTR DS:[<bu6>]
004013E3   .  03C8               ADD ECX,EAX
004013E5   .  81F9 2F010000      CMP ECX,12F
004013EB   .  0F85 59010000      JNZ <Lezione7.closehadle_fine>

L'operazione fatta e':

bu12 + bu14 + bu6 = 12f
Per ora due delle tre variabili sono ancora libere.
004013F1   .  0FB605 01344000    MOVZX EAX,BYTE PTR DS:[<bu9>]               ;  EAX = (09)
004013F8   .  8D48 A0            LEA ECX,DWORD PTR DS:[EAX-60]
004013FB   .  83F9 0F            CMP ECX,0F
004013FE   .  0F85 46010000      JNZ <Lezione7.closehadle_fine>

Si inizia con EAX = bu9. Poi si prende l'indirizzo puntato da bu9-60h e lo si mette in ECX. ECX deve essere uguale a 0Fh. In pratica si fa

bu9 - 60 = 0F -> bu9 = 6F
.
00401404   .  0FB60D F9334000    MOVZX ECX,BYTE PTR DS:[<bu1>]
0040140B   .  2BC1               SUB EAX,ECX
0040140D   .  0F85 37010000      JNZ <Lezione7.closehadle_fine>
00401413   .  33C9               XOR ECX,ECX
00401415   .  33C0               XOR EAX,EAX
00401417   >  0FB690 F8334000    MOVZX EDX,BYTE PTR DS:[EAX+<buffer>]
0040141E   .  03CA               ADD ECX,EDX
00401420   .  40                 INC EAX
00401421   .  83F8 11            CMP EAX,11
00401424   .^ 7C F1              JL SHORT Lezione7.00401417
00401426   .  81F9 A4060000      CMP ECX,6A4
0040142C   .  0F85 18010000      JNZ <Lezione7.closehadle_fine>

La sub fa in modo che:

bu1 - bu9 = 0 -> bu1 = 6F

Poi c'e' un for che fa la somma di tutti i valori contenuti in buffer: tale somma deve essere 64A.

Ci sono vari modi per risolvere questo sistema: uno e prendere tutte le equazioni, buttarle dentro un programma di soluzione matematica e vedere cosa ci dice. Molto probabilmente vi dira' che il sistema ha infinite soluzioni, in quanto ci sono diverse variabili libere (se non sbaglio sono 4 o 5). Se non sapete cosa vuol dire andate a farvi un giro su wikipedia. In ogni caso io una soluzione adatta l'ho trovata

73 6F 66 71 69 70 65 58 70 6F 72 58 65 70 65 72
Tu, caro lettore, puoi trovartene una da te :D Vedremo dopo che conviene mettere tutti valori esadecimali corrispondenti a lettere valide dell'alfabeto in ascii (quindi tutte quelle da 'a' a 'z' e da 'A' a 'Z' ovvero da 61 a 7A e da 41 a 5A).
00401432   .  68 D8334000        PUSH OFFSET <Lezione7.handle_reg>
00401437   .  68 19000200        PUSH 20019
0040143C   .  53                 PUSH EBX
0040143D   .  68 54214000        PUSH Lezione7.00402154                       ;  ASCII "perche\\qualcuno\\le_ha\\colpite"
00401442   .  56                 PUSH ESI
00401443   .  FFD5               CALL EBP
00401445   .  85C0               TEST EAX,EAX
00401447   .  0F85 FD000000      JNZ <Lezione7.closehadle_fine>
0040144D   .  8D4424 18          LEA EAX,DWORD PTR SS:[ESP+18]
00401451   .  50                 PUSH EAX                                     ; /pBufSize
00401452   .  BE C0344000        MOV ESI,OFFSET <Lezione7.buf_reg2>           ; |
00401457   .  56                 PUSH ESI                                     ; |Buffer => OFFSET <Lezione7.buf_reg2>
00401458   .  8D4424 24          LEA EAX,DWORD PTR SS:[ESP+24]                ; |
0040145C   .  50                 PUSH EAX                                     ; |pValueType
0040145D   .  53                 PUSH EBX                                     ; |Reserved
0040145E   .  57                 PUSH EDI                                     ; |ValueName
0040145F   .  FF35 D8334000      PUSH DWORD PTR DS:[<handle_reg>]             ; |hKey = 44
00401465   .  C74424 30 23000000 MOV DWORD PTR SS:[ESP+30],23                 ; |
0040146D   .  FF15 04204000      CALL DWORD PTR DS:[<&ADVAPI32.RegQueryValueE>; \RegQueryValueExA
00401473   .  85C0               TEST EAX,EAX
00401475   .  0F85 CF000000      JNZ <Lezione7.closehadle_fine>
0040147B   .  FF35 D8334000      PUSH DWORD PTR DS:[<handle_reg>]             ; /hKey = 00000044 (window)
00401481   .  FF15 08204000      CALL DWORD PTR DS:[<&ADVAPI32.RegCloseKey>]  ; \RegCloseKey
00401487   .  837C24 1C 03       CMP DWORD PTR SS:[ESP+1C],3
0040148C   .  0F85 B8000000      JNZ <Lezione7.closehadle_fine>

Questo codice e' gia' stato praticamente spiegato nella lezione 7. In 00401443 apriamo la key "perche\\qualcuno\\le_ha\\colpite" con una chiamata a OpenKeyRegExA (il suo indirizzo e' salvato alla base dello stack). Poi chiamiamo RegQueryValueExA per leggere un valore dal registri: il nome del valore e' proprio la stringa che abbiamo messo in "buffer". Allora basta prendere i valori ascii di "buffer" e creare un valore (di tipo binario, vedi 00401487) con tale nome. Il contenuto del valore del registro verra' messo in buf_reg2. Con il mio codice, fatto apposta per avere tutti caratteri ascii validi, il valore si chiamera' "sofqipeXporXeper".

Bene, ora passiamo ai check sul contenuto del registro.

00401492   .  B8 10354000   MOV EAX,OFFSET <Lezione7.buf_reg2_1>           ;  EAX = 0
00401497   .  8BF8          MOV EDI,EAX
00401499   .  A5            MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]     ;  [ESI] = [EDI]
0040149A   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
0040149C   .  BE C6344000   MOV ESI,Lezione7.004034C6
004014A1   .  BF 18354000   MOV EDI,OFFSET <Lezione7.buf_reg2_2>
004014A6   .  A5            MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004014A7   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
004014A9   .  BE CC344000   MOV ESI,Lezione7.004034CC
004014AE   .  BF 20354000   MOV EDI,OFFSET <Lezione7.buf_reg2_3>
004014B3   .  A5            MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004014B4   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
004014B6   .  BE D2344000   MOV ESI,Lezione7.004034D2
004014BB   .  BF 28354000   MOV EDI,OFFSET <Lezione7.buf_reg_2_4>
004014C0   .  A5            MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004014C1   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
004014C3   .  BD 30354000   MOV EBP,OFFSET <Lezione7.buf_reg2_5>
004014C8   .  BE D8344000   MOV ESI,Lezione7.004034D8
004014CD   .  8BFD          MOV EDI,EBP
004014CF   .  A5            MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004014D0   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]

Orbene, questa parte di codice sposta gruppi di 6 byte da buf_reg2 alle varie locazioni identificate con buf_reg2_x. Se eseguidte direttamente a 004014D0 e fate un dump della memoria nelle vicinanze di buf_reg2_1, vedrete che ha semplicemente riscritto il contenuto di buf_reg2 spaziando ongi 6 byte con 2 byte a 00.

004014D8   .  50            PUSH EAX                                                   ; /s => "sucate"
004014D9   .  FFD6          CALL ESI                                                   ; \atoi
004014DB   .  A3 E4334000   MOV DWORD PTR DS:[<atoi1>],EAX
004014E0   .  C70424 183540>MOV DWORD PTR SS:[ESP],OFFSET <Lezione7.buf_reg2_2>        ;  ASCII "000000"
004014E7   .  FFD6          CALL ESI
004014E9   .  A3 F4334000   MOV DWORD PTR DS:[<atoi2>],EAX
004014EE   .  C70424 203540>MOV DWORD PTR SS:[ESP],OFFSET <Lezione7.buf_reg2_3>        ;  ASCII "089434"
004014F5   .  FFD6          CALL ESI
004014F7   .  A3 E0334000   MOV DWORD PTR DS:[<atoi3>],EAX
004014FC   .  C70424 283540>MOV DWORD PTR SS:[ESP],OFFSET <Lezione7.buf_reg_2_4>       ;  ASCII "000000"
00401503   .  FFD6          CALL ESI
00401505   .  55            PUSH EBP
00401506   .  A3 F0334000   MOV DWORD PTR DS:[<atoi4>],EAX
0040150B   .  FFD6          CALL ESI

Ora si prendono tutti i gruppi di 6 byte e li si trasformano da stringhe di caratteri ascii a degli interi (usando atoi), e li si scrivono rispettivamente nelle DWORD atoiX (X tra 1 e 5). Se la stringa da trasformare non rappresenta un numero viene scritto 0.

0040150D   .  803D 35354000>CMP BYTE PTR DS:[<buf_reg2_ultimalettera>],5C              ;  ultimo sibolo = '\'?
00401514   .  59            POP ECX
00401515   .  59            POP ECX
00401516   .  A3 E8334000   MOV DWORD PTR DS:[<atoi5>],EAX
0040151B   .  75 2D         JNZ SHORT <Lezione7.closehadle_fine>
0040151D   .  8B15 E0334000 MOV EDX,DWORD PTR DS:[<atoi3>]
00401523   .  8B0D F4334000 MOV ECX,DWORD PTR DS:[<atoi2>]
00401529   .  2BD1          SUB EDX,ECX
0040152B   .  81C2 C4080000 ADD EDX,8C4
00401531   .  81FA 1E660100 CMP EDX,1661E                                              ;  a3-a2+8C4h==1161Eh
00401537   .  75 11         JNZ SHORT <Lezione7.closehadle_fine>
00401539   .  8B15 F0334000 MOV EDX,DWORD PTR DS:[<atoi4>]
0040153F   .  03C2          ADD EAX,EDX                                                ;  a4+a5==a2
00401541   .  3BC1          CMP EAX,ECX
00401543   .  75 05         JNZ SHORT <Lezione7.closehadle_fine>

Ok, qui si arriva effettivamente al controllo. 0040150D controlla che il trentesimo carattere (buf_reg2[29]) sia '\'. Vuol dire che il contenuto del registro deve essere di 29 caratteri piu' '\' finale. Dopo, il valore convertito dell'ultimo gruppo di 6 caratteri viene messo in "atoi5". Le istruzioni da 0040151D a 00401531 e da 00401539 a 00401541 ci dicono:

atoi3 - atoi2 + 8C4 = 1661E
atoi5 + atoi4 = atoi2

Anche in questo caso ci troviamo davanti ad un sistema lineare con molte soluzioni valide (quattro variabili su due equazioni). Io per pigrizia ho fatto scegliere

atoi5 = atoi4 = atoi2 = 0
atoi3 = 1661E - 8C4 = 15D5A = 089434 (in decimale)

Atoi1 non viene controllata, quindi si puo' mettere quello che si vuole, cosi' come per atoi5, che conta solo che l'ultimo carattere sia '\'.

La parte seguente di disassemblato punta semplicemente a fare apparire i check nella finestra degli obbiettivi del programma. Quindi il crackme e' finito!


Note Finali

Se avete commenti e/o correzioni contattatemi pure via mail o in pm sul forum. Un grosso ringraziamento alla UIC per il corso: continuate cosi! Aspetto con impazienza le prossime lezioni.


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 malevoli 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.