Zoom Icon

Lezione8 SepticConvulsion solution

From UIC Archive

Soluzione Lezione 8 for Newbie 2009

Contents


Lezione8 SepticConvulsion solution
Author: SepticConvulsion
Email: [email protected]
Website:
Date: 12/12/2008 (dd/mm/yyyy)
Level: Luck and skills are required
Language: Italian 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.

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 = 12fPer 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 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.