Easy CD-DA Exctractor 7.5.0 build 1

(vestirsi della corteccia di Armadillo)

Data

by Zarathustra

 

14/11/2004

UIC's Home Page

Published by Quequero

Ci sono 10 tipi di persone, quelli che capiscono i numeri binari...
 

Grazie tante zara, ottimo tute

e quelli che non li capiscono.

....

Per qualunque chiarimento contattatemi sul forum della UIC 

....

Difficoltà

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

 
 

Introduzione

Questo e' il primo tutorial della mia vita, spero che lo troviate interessante e soprattutto ne possiate trarre spunti per approfondire la tecnica che ho utilizzato per squoiare armadillo da questo target. Non a caso il titolo da me scelto indica proprio come sia possibile integrare parte del codice di armadillo per l'abbattimento di una delle piu' ostiche trappole tese al reverser da questo animaletto fastidioso: le nanomites (battezzate alla UIC da Saturn con il nome di nanometti). Io personalmente mi sono divertito tanto in questo mio primo 'orgasmo informatico'.

Ma bando alle ciancie ed iniziamo con le cose serie.
Un punto sicuramente importante e' da dove parte questo tutorial. Dato che mi sembra fuori luogo oltreche' ripetitivo illustrare quanto fatto da altri in altra sede a proposito delle n-mila protezioni introdotte nelle ultime versioni di armadillo, non spieghero' nei minimi dettagli queste ultime ma mi limitero' a dire come bypassarle. Piuttosto mi soffermero' sul mio metodo di fissare una di queste protezioni: i nanometti.
Il tutorial e' volutamente non per newbies. Illustrare per filo e per segno tutti gli schemi di protezione contro l'unpacking di armadillo richiederebbe molto tempo. L'ho scritto principalmente per tenere traccia del lavoro che ho svolto in questo periodo e perche' possa servire come base di partenza per studi piu' approfonditi sull'argomento. In ogni caso se qualcosa non fosse chiaro allora contattatemi sul forum della UIC cerchero' di rispondervi prima possibile. Ancora, vi consiglio di scaricare subito il programma dal sito perche' il tutorial e' legato alla versione corrente in maniera molto pesante.
 
 

Tools usati


OllyDebug
1.10 + plugins
ImpRec
LordPE Deluxe
WinHex 11.xx
The brain (non mi risulta possibile inserire qui il collegamento a questo tool, l'ho perso subito dopo aver scritto il tutozzo...)
Lettore CD con su 'CENTURY CHILD' dei Nightwish
Il tutorial preve una certa dimistichezza con l'uso dei suddetti tools. Daro' per scontate certe cose, come cosa e' una IAT, come e' strutturato un PE, come si puo' aggiungere una entry alla IAT o una sezione ad un eseguibile o, ancora, come riallineare le sezioni di un eseguibile dumpato (tutte operazioni che comunque sono rese molto piu' semplici con i tools a nostra disposizione).

 

URL o FTP del programma

www.poikosoft.com 

Notizie sul programma

Il target in esame e' un programma che serve per effettuare il grab delle tracce audio di un cd, ma permette anche di effettuare conversioni di file audio nei formati piu' disparati. In realtà non l'ho provato in tutte le sue funzionalità, il punto importante e' che il programma e' stato protetto con una delle piu' recenti versioni di armadillo.

Essay

La procedura seguita normalmente quando si ha a che fare con un programma compattato/criptato e' quella di identificarne il packer e vedere se esiste già una cura. Usando PeID ci si accorge subito che il target fa uso di armadillo (cosa deducibile da tanti altri indizi come il nag screen iniziale ed il reminder in pieno stile armazio). Ad ogni modo PeID fallisce nel determinarne la versione esatta, ed io personalmente non mi sono impegnato piu' di tanto nel cercarla (anche se devo ammettere che la tecnica utilizzata da Mephisto per la ricerca della versione di armadillo, in questo caso non funziona).
Prima di iniziare va ribadito un punto importante: armazio introduce parecchio overhead nei programmi che protegge. Tale overhead si traduce nella compilazione di tabelle e nella scompattazione di sezioni di dati/codice. L'approccio che ho seguito non e' ottimo, nel senso che il dump ottenuto prescinde dal cercare di riciclare il codice di armadillo per fargli creare tali sezioni automaticamente anche nel programma dumpato. Il dump ottenuto mira ad essere prima funzionante e poi ottimo. Ecco perche' le tabelle ed il codice di cui sopra sono praticamente incluse nel dump stesso in nuove sezioni dell'eseguibile.
Tale scelta e' stata motivata dal fatto che mi premeva tirare fuori al piu' presto un tutorial sui nanometti. L'approccio seguito e' questo: dumpare e fissare il dumpato dandogli cio' di cui ha bisogno. Cio' richiede una attenta opera di debug e di confronto del tracing del programma orginale e del dumpato.

I passi che seguiremo nell'ottenimento del nostro dump funzionante sono i seguenti:

1) Trovare l'OEP del programma.

2) Aggiustare la IAT del programma che armazio ha vilmente ridotto ad un colabrodo.

3) Fissare i salti ad una sezione del packer generata dinamicamente dal fetido.

4) Fissare i nanometti tramite l'installazione di un SEH ad esso dedicato che tuttavia fa uso del codice di arma stesso.


Per capire cosa sta dietro al punto 1) un must sono sicuramente i tutes di ricardo narvaja & c. Se non avete mai sentito parlare di come trovare l'entry point effettivo di un programma protetto con armadillo allora dovete necessariamente leggere uno dei suddetti tutorials.

Per una comprensione del meccanismo di protezione Debug Blocker di armadillo e di come i due processi creati quando viene usato questo metodo di protezione interagiscono si rimanda ai due tutorials di Faina sulla UIC. Da qui in avanti ci si riferira' con il nome di processo padre al processo debugger, e con il nome di processo figlio al processo debuggato di cui nei suddetti tutorial. Prima degli studi di Faina (esimio collega), si riteneva a torto che non si potesse effettuare il detach dal padre (debugger) del processo figlio (debugged), se non lavorando su WinXP ed utilizzando la API DebugAcriveProcessStop. In realtà Faina ha scoperto il metodo della OpenMutex/CreateMutex secondo cui il processo figlio puo' partire senza necessariamente avere a che fare con il padre (almeno per quanto riguarda la parte iniziale). Questo apre interessanti e rapide prospettive per trovare l'entry point del programma anche se il programma utizza la protezione Debug Blocker. In effetti il nostro target utilizza tale protezione e di cio' ce se ne puo' rendere conto considerato che nel Task Manager ci sono due processi attivi con lo stesso nome, pur avendo lanciato una sola copia del programma.

In questo tutorial si utilizza OllyDebug come debugger. E' importante assicurarsi di avere installati almeno i seguenti plugins: OllyDump,HideDebuggerm CommandLine. Questi plugins, che potete trovare facilmente in rete nelle loro ultime versioni sono indispensabili soltanto per seguire questo tutorial passo passo, ma non sono strettamente necessari alla fine della risoluzione del nostro problema.

Dopo avere installato i precedenti plugins apriamo il programma target con Olly. Assicurarsi che tutte le eccezzioni vengano passate al programma da Olly. Se nel corso del run se ne presenteranno altre aggiungerle alla lista, almeno in questa prima fase.

Con una Search for -> intermodular calls (tasto destro del mouse sul codice assembly) cerchiamo le chiamate alla OpenMutexA di kernel32.dll.

Due sono i punti in cui tale procedura viene chiamata : 0x74ec9a e 0x74f09c

Per bypassare la protezione DebugBlocker e' sufficiente invertire i salti dopo la test eax,eax che si trovano immediatamente a tali call. In questo caso basta fare :


0074ECA2 74 04 JE SHORT ezcddax.0074ECA8       diventa -> JNE SHORT 74ECA8

0074F0A4 0F85 98020000 JNZ ezcddax.0074F342    diventa -> JZ 74f342

Dopo cio' se il programma (non fatelo) venisse fatto partire il processo che verrebbe avviato sarebbe il processo figlio e non il padre. Per trovare l'entry point del processo figlio basta mettere un breakpoint sulla API CreateThread e trovare quando dopo che un po' di sezioni di codice vengono scompattate il punto in cui si ha il salto alla sezione di codice effettiva del programma. Pertanto mettiamo un bp sulla call a CreateThread (bp CreateThread sulla commandLine di Olly). Runniamo con F9 (se si prensentano delle eccezzioni passiamole al programma con Shift+F9) e la terza volta che breakkiamo sulla CreateThread, siamo in prossimità dell'entry point. Ritornando al codice del main thread (CTRL+F9) un po' piu' giu' c'e' una CALL EDI, tale chiamata porta dritto dritto all'entry point del programma. Nel nostro caso la abbiamo :


020BA42F FFD7 CALL EDI ; ezcddax.00401780 

Quindi l'entry point del programma e' proprio questo: 0x401780

Siamo pronti per effettuare il dump del programma. Useremo il plug-in di Olly (ollydump) per effettuare il dump a partire dall'entry point dove dovremmo trovarci a questo punto(ricordo che si trova all'indirizzo 0x401780). Prima di effettuare il dump e' possibile mettere a posto un paio di cosette che sono state messe male. In particolare il PE Header del programma (che intuitivamente si trova all'indiirizzo 0x400000) presenta qualche problema. Di cio' ci si puo' rendere conto andando all'indirizzo 0x400000 nella finestra di dump e selezionando l'opzione Special-> PE Header. Tale opzione ci fa vedere i bytes del dump con un layout di tipo PE Header, quindi se qualcosa va storto ce ne accorgiamo subito. In effetti l'offset alla PE Signature e' sbagliato (0x3DF142). Per trovare l'offset corretto facciamo Search For -> Binary String nella finestra di dump e cerchiamo la stringa 'PE' ovvero la sequenza esadecimale 50 45 . L'offset giusto e' 0x200. Sostituiamo tale valore al 0x3DF142 che troviamo all'indirizzo 0x40003C. Adesso tutta la PE Signature viene effettivamente visualizzata da Olly in maniera corretta. Bene ora possiamo fare il dump con ollydump. Assicuriamoci che la casella di spunta "rebuild import" sia deselezionata (la IAT la fisseremo con imprec) e che la "Fix Raw Offset ... " sia selezionata (quest'ultima opzione consente di avere un dumpato con le sezioni già allineate, deselezionando questa opzione l'operazione di riallineamento va fatta manualmente). Otteniamo cosi' il dumpato (che salveremo come dumped.exe) che ovviamente per forza di cose non funzionerà. Lasciamo aperto Olly con il programma a 0x401780

 
Le sezioni del dumpato devono essere opportunamente allineate. Apriamo il dumpato con il PeEditor di LordPE ed assicuriamoci che tutti gli header delle sezioni siano tali che VSize = RSize e Voffset = ROffset (queste dovrebbero gia' essere state allineate da Olly). Oltre a cio' per evitare sgradevoli messagebox da parte di Olly nel prosieguo, mettiamo a posto la BaseOfCode (0x1000). Se abbiamo fatto le cose per bene windows dovrebbe mostrare correttamente l'icona del programma. Adesso fisseremo la IAT. Infatti runnando il dumpato non parte manco a mazzate. Il fixing della IAT prevede tre passi:
1) Localizzazione della IAT nel programma.
2) Patching del programma originale per l'ottenimento di una IAT papabile.
3) Sostituzione o aggiunta della IAT papabile al programma dumpato.
 
Per questi punti lo strumento principe e' imprec. Lanciamo imprec e selezioniamo il programma attualmente debuggato con olly. Per far cio' selezioniamo tale programma in imprec in "attach to active process". Sostituiamo l'OEP con 0x1780 e tramite IAT autosearch abbiamo trovato la IAT del programma. Ora e' evidente che la IAT trovata e' completamente bucherellata dato che ci sono un sacco di funzioni unresolved (le entries della IAT le si ottengono tramite il pulsante GetImports) tutte le altre appartengono solo a kernel32.dll (strano no!). Bisogna patchare il programma originale per ottenere una IAT decente. E' cio' che faremo. Segnamoci l'offset della IAT trovata con ImpRec (0x0026A560).
Da questo momento riavvieremo un paio di volte il programma ed ogni volta bisogna patchare la OpenMutex di cui prima. Olly fortunatamente mantiene traccia delle patches applicate pero' vanno di nuovo applicate. Quindi riavviamo il programma con Olly (debug->restrart) e con Ctrl-p applichiamo di nuovo le patch (assicuratevi che siano "active"). Mettiamo anche un HW Breakpoint on write sull'indirizzo (0x0026A560+ 0x400000 = 0x66A560). Otteniamo cosi' il punto dove viene scritta la IAT.
Il punto dove avviene la scrittura della IAT e che si ottiene la seconda volta che il programma brekka e' il seguente:

 

020B722E MOV EAX,DWORD PTR SS:[EBP-37FC] ; ezcddax.0066A564 020B7234 83C0 04 ADD EAX,4
020B7237 MOV DWORD PTR SS:[EBP-37FC],EAX
020B723D JMP 020B6F10
020B7242 FF15 9C020C02 CALL DWORD PTR DS:[20C029C] ; KERNEL32.GetTickCount
 
 

Come suggerito da SaturN sul forum per ottenere una IAT corretta bisogna guardare un po' prima e patchare un jnz


 


020B70A5 FFB5 54C2FFFF PUSH DWORD PTR SS:[EBP-3DAC]
020B70AB FF15 5C030C02 CALL DWORD PTR DS:[20C035C] ; MSVCRT._stricmp
020B70B1 59 POP ECX
020B70B2 59 POP ECX
020B70B3 85C0 TEST EAX,EAX
020B70B5 75 11 JNZ SHORT 020B70C8     --> diventa Jmp
 

La patch indicata tuttavia non potra' essere applicata subito ma e' necessario far ripartire il programma perche' le prime entry della tabella sono ormai state scritte. Quindi mettiamo un hw breakpoint alla locazione 0x020B70B3. Inoltre bisogna mettere un hw breakpoint un po' piu sotto
dopo che tutta la IAT e' stata scritta, cio' ci serve per evitare che il programma vada a nanna a causa di un CRC che viene fatto subito dopo. Il punto dove mettere il secondo hw breakpoint e' all'indirizzo 0x20B7337.
Adesso possiamo riavviare il programma con Olly. Quando il programma si ferma su 0x20B70B3 patchiamo come su indicato, togliamo questo hw breakpoint e continuiamo. Il programma si ferma sul secondo breakpoint : 0x20B7337.
Se andiamo a guardare all'indirizzo della IAT essa e' corretta, o quasi!! Ci sono ancora delle fake entries che separano le entries buone. Pero' non e' un problema. Con imprec apriamo il programma attualmente debuggato, inseriamo 1780 nell'OEP, premiamo IAT autosearch. Seguiamo il consiglio di ImpRec -> cioe' mettiamo RVA = 0x0026A000 e size 0x4000
Otteniamo cosi' la IAT corretta.
Fissiamo il programma dumped.exe con FixDump (aggiungendo una nuova sezione all'eseguibile).
Proviamo ad eseguire il programma dumpato e fissato e vediamo cosa succede: dannazione, non si avvia! Il motivo?
Un'altra delle protezione di armadillo! I salti ad una sezione del packer generata dinamicamente.
Illustrero' di seguito un modo per fissarli che e' consistente con quanto detto all'inizio a proposito della metodologia utilizzata in questo tute.
Ci sono tuttavia altri modi per risolvere il problema (v. Tutorial di Eggi).
A questo punto dovreste avere ancora la copia del programma aperta con olly che si trova all'oep. Non chiudetela!
Avviamo il programma dumpato con olly, e deselezioniamo le access violation tra le eccezzioni passate al progamma. Dopo avere avviato il programma troveremo una violazione di accesso ad un indirizzo che nel mio caso e' 0x3000000, dico nel mio caso perche' in realta' la sezione dove avvengono i fantomatici salti viene generata dinamicamente. Bene', il programma vuole tale sezione? Allora diamogliela. Dumpiamo nella prima istanza di olly tale sezione. Con Alt+M vediamo la mappa di memoria, selezioniamo la sezione che ci interessa e dumpiamola (tasto destro sulla sezione e dump). Salviamola su un file (backup-> save data to file).
Chiudiamo l'instanza di olly con cui abbiamo aperto il dumped.exe. E apriamo dumped.exe con il peeditor di lordpe aggiungendo la sezione caricata da disco. Assicuriamoci inoltre che tutto sia a posto giusto. In particolare, la sezione viene aggiunta alla fine del dumpato, subito dopo la iat che ha creato imprec. E' importante cambiare la virtualsize della sezione creata da imprec al valore corretto (allineata) e inoltre e' importante che il virtual offset della nuova sezione sia pari a 0x3000000 (nel mio caso) - 0x00400000.
Se abbiamo fatto le cose per bene, provando ad avviare il programma non partira' ancora, ma apparirà una graziosa MessageBox che ci informa del fatto che si e' incontrata una eccezzione di tipo int3.
Eccoci finalmente al piatto forte di questo tutorial: le nanomites o nanometti volgarmente detti. I nanometti sono delle istruzioni int3 piazzate nel codice del programma orginale al posto di salti condizionali e non, che vengono risolte run time dal processo padre. Praticamente quello che fa il processo padre e' di debuggare il processo figlio e ogni volta che si presenta un evento di debug che e' un int3, ricava il contesto del processo figlio da cui risale a come alterarne il Program Counter per farlo riprendere dal punto corretto. Nel corso del tempo tale protezione e' andata via via migliorandosi, all'inizio tali int3 erano risolti direttamente attraverso una tabella ed addirittura corrispondevano a salti non condizionati. Poi sono stati aggiunti come salti condizionati, poi e' stata introdotta la criptazione delle informazioni che permettono di risalire al punto dove saltare data una int3. Tale evoluzione non ha permesso di elaborare dei metodi fissi nella risoluzione dei nanometti. Inizialmente quando alla UIC abbiamo cominciato ad affrontare il problema ci siamo subito accorti della mancanza di un metodo generale per la risoluzione dei nanometti oltreche' delle grosse difficoltà di reversare l'algoritmo che sta dietro alla crittazione/decrittazione delle tabelle (cio' non e' impossibile, ma se cambiano il come generare le tabelle siamo al punto di prima). La cosa che mi era sembrata piu' ovvia era quella di effettuare un log del contesto (o delle parti piu' interessanti di esso, quali l'eip e i registri di flags ed ecx) del processo figlio, prima e dopo che un nanometto fosse risolto. Ho anche codificato un tools che patcha il programma dumpato cercando di capire quali sono le istruzioni di salto "rubate" al programma originale. Tale tool tuttavia presentava grosse limitazioni legate all'environment in cui un programma viene eseguito ed ai flussi seguiti dal programma stesso. Era necessario un approccio piu' sistematico ed esaustivo. Dopo un po' di rimurginamento mi decisi a codificare un SEH che risolvesse le nanomites sfruttando lo stesso codice del processo padre che guarda caso abbiamo gia' nel dumpato! Per chi si fosse sganciato brevemente: il programma da noi dumpato e' un singolo processo costellato di int3. Per risolverle o le si sostituisce con le istruzioni originali, cosa abbastanza difficile da trovare per i motivi su esposti, oppure? Oppure ad ogni int3 mettiamo una chiamata al codice originale del processo padre (che guarda caso sta già nel dumpato) che le risolve. Come fare una chiamata ad una funzione a partire da una int3? semplice, installando un SEH, cioe' un gestore delle eccezzioni che guarda l'eip dove e' stata incontrato l'interrupt e restituisce il controllo del processo al punto corretto. Il punto piu' naturale dove collocare il nostro SEH e' proprio dove il processo padre effettua tale elaborazione. Ogni volta che il processo padre deve gestire un evento di debug, guarda se si tratta di una int3 e se e' cosi' recupera il contesto del figlio con la GetThreadContext, risolve il nanometto, riaggiusta il contesto del figlio con la SetThreadContext e lo fa ripartire dal punto giusto. Il SEH deve riciclare il codice del padre che sta subito dopo la chiamata a GetThreadContext, pertanto lo metteremo proprio in quel punto. Cerchiamo con Olly tutte le intermodular calls e nella lista trovata verranno visualizzate alcune GetThreadContext.
Quella corretta e' quella che si trova a 0x75468d. Questo si vede perche' un po' piu' sotto si trova la chiamata a SetThreadContext. Poiche' assembleremo un po' di istruzioni con Olly prima dell'utilizzo del codice effettivo dopo la GetThreadContext, dovremmo metterci un po' prima della GetThreadContext. Cio' in realtà non e' necessario perche' armadillo e' talmente stupido da inserire spesso del codice di offuscamento che e' del tutto inutile e che pertanto sovrascriveremo tranquillamente :-))) In particolare si vede subito che il codice che va da 0x754693 a 0x7546d6 e' perfettamente inutile! pertanto selezioniamolo con olly e noppiamolo cosi' da avere una lavagna bianca su cui divertirci a fare i nostri disegni.
Va da se cmq che il codice che assembleremo starà un pò prima perche' le istruzioni che ci servono sono tante. A conti fatti un buon punto da dove iniziare ad assemblare e' l'indirizzo 0x754675
 
Prima di proseguire con la codifica del SEH, e' necessario capire come installarlo perche' dovremo testarlo ed e' da questa opera di debug che potremo capire cosa e' necessario dumpare e quando per farlo funzionare. Avviamo il dumped.exe con olly facendo in modo che olly non passi le eccezzioni al programma. F9 e troviamo il punto in cui c'e' la prima int3: 0x434a81. Vediamo quale SEH sta attualmente servendo le eccezzioni in questo punto del programma. Andiamo nella finestra di dump di olly e selezioniamo goto-> Expression inseriamo fs:[0] come indirizzo. Selezioniamo la prima DWORD della finestra dump e col tasto destro ->follow dword in dump. Si vede chiaramente che il SEH attualmente installato e' all'indirizzo 0x5540e9 (per chi non avesse idea di cosa sto dicendo vada a leggersi il tutozzo del que sui SEH). Tale SEH e' quello maligno, cioe' quello che non sa' risolvere gli int3. Andiamo all'indirizzo 0x5540E9 noteremo che la prima istruzione che viene eseguita e' una salto non condizionato all'indizzo 0x5C6514. Questo e' l'indirizzo dell'SEH effettivo!!!! Dico cio' perche' nel programma si possono notare diverse installazioni di SEH (ad indirizzi differenti) che iniziano sempre con un salto a tale locazione. Cio' in realta' e' il frutto di una certa opera di analisi, in ogni caso se il nuovo SEH rimpiazzasse quello a ox5540e9 e non quello a 0x5c6514 ci si renderebbe presto conto che le cose non andrebbero come vorremmo! Quello che faremo e' pertanto sostituire le istruzioni a 0x5c6514 con un salto al nostro SEH, che inoltre dovra' 'emulare' quelle che siamo stati costretti a togliere.

In particolare, sostituiamo queste istruzioni con un salto al nostro SEH, e ricordiamoci che nel caso in cui il nostro SEH non sia in grado di gestire l'eccezzione (in quanto non e' una int3) dovra' prima emularle e poi ritornare alla istruzione che immediatamente le segue.

005C6514 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
005C6518 F740 04 06000000 TEST DWORD PTR DS:[EAX+4],6

 
 
 
Vediamo a questo punto come e' strutturato il nostro SEH (si ricorda che assembliamo a partire da 0x754675) :
 1) L'SEH e' di tipo per-Thread handler.
Questa scelta e' stata motivata da iniziali inghippi che mi hanno impedito di utilizzarlo come final. Da una successiva lettura del codice sono convinto che si potrebbe studiare anche quest'ultima soluzione, lasciata ai piu' volenterosi. Cio' premesso la prima cosa da fare nel nostro SEH e' vedere se l'eccezzione che ha portato alla sua chiamata e' una eccezzione di tipo int3, se non lo e' allora passiamo la stessa agli altri della catena. Altrimenti andiamo al punto 2.
Il codice che segue implementa quanto detto, assembliamolo direttamente in Olly:

 
00754675 PUSH EBX
00754676 PUSH EDI
00754677 PUSH ESI
00754678 MOV EDX,DWORD PTR SS:[EBP+8]
0075467B POP ESI
0075467C POP EDI
0075467D POP EBX
0075467E CMP DWORD PTR DS:[EDX],80000003 ; L'eccezione e' un int3 ?
00754684 JE SHORT dumped_.00754696
00754686 MOV EAX,DWORD PTR SS:[ESP+4]          ; emulazione delle due istruzioni originali
0075468A TEST DWORD PTR DS:[EAX+4],6    ; -----------------------------------------
00754691 JMP dumped_.005C651F 
 

2) A questo punto bisogna ricopiare il contesto che si ottiene in seguito alla int3 nel punto dove se lo aspetterebbe il padre subito dopo la GetThreadContext. Le istruzioni che seguono adempiono a tale compito:

00754696 PUSHAD
00754697 PUSHFD
00754698 MOV EAX,DWORD PTR SS:[ESP+30]
0075469C PUSH EBP 0075469D 8BEC MOV EBP,ESP
0075469F PUSH 0D0
007546A4 PUSH EAX
007546A5 MOV EAX,ESP
007546A7 SUB EAX,1460
007546AC PUSH EAX
007546AD CALL MSVCRT.memcpy
007546B2 ADD ESP,0C
007546B5 MOV EAX,DWORD PTR SS:[EBP-13B0] ; preleviamo il program counter dove e' avvenuta
007546BB INC EAX                                    ; l'eccezzione e lo incrementiamo di uno
007546BC MOV DWORD PTR SS:[EBP-13B0],EAX ; proprio come se lo aspetta il codice originale

Si noti la chiamata alla memcpy. Tale funzione non figura nella import table del programma dumpato vedremo successivamente come fissarla
per far si che il programma funzioni su tutte le piattaforme.
Il punto giusto dove copiare il contesto lo si deduce con un po' di debugging del programma originale. Un metodo semplice e' quello di analizzare il codice con Olly a run time. Basta mettere in relazione il buffer fornito alla GetThreadContext con il valore di ebp. Oppure lo si puo' dedurre da come il codice di risoluzione dei nanometti interagisce con il contesto fornito dalla GetThreadContext. E' da notare inotre che il contesto fornito dalla GetThreadContext e' leggermente diverso da quello che si ottiene quando viene chiamato l'SEH a causa di una eccezzione. In particolare l'EIP in quest'ultimo caso e' inferiore di 1 rispetto al caso della GetThreadContext e questo e' il motivo per cui l'ho incrementato di uno.
3) Segue tutto il codice che serve alla decrittazione delle tabelle.
Tale codice va lasciato essenzialmente inalterato e si estende fino alla SetThreadContext all'indirizzo 0x754A31.
Ovviamente a noi interessa uscire dal SEH subito dopo che il programma ha calcolato correttamente il nuovo program counter cui saltare dopo la int3. Effettuando un po di tracing nel codice del debugger si deduce che la parte di codice che va da 0x754964 a 0x754989 e del codice di offuscamento che puo' essere noppato con olly. Inoltre l'istruzione
007549BE 8995 50ECFFFF MOV DWORD PTR SS:[EBP-13B0],EDX

non fa altro che copiare il nuovo valore del program counter nel contesto. Ottimo! E' qui che terminera' il nostro SEH. 

007549C4 MOV ESP,EBP
007549C6 POP EBP
007549C7 POPFD
007549C8 POPAD
007549C9 MOV EAX,DWORD PTR SS:[ESP-13D8]
007549D0 MOV ECX,DWORD PTR SS:[ESP+C]
007549D4 ADD ECX,0B8
007549DA MOV DWORD PTR DS:[ECX],EAX
007549DC MOV EAX,0 007549E1 C3 RETN

Tale codice non fa altro che copiare nel contesto il valore corretto del Program Counter che armadillo ha teneramente calcolato. Assembliamo tale codice con olly. Un altro punto di uscita del codice che risolve le nanomites si ha all'indirizzo 0x754a3d. Per tale motivo l'istruzione successiva va assemblata per uscire dal SEH come segue:
 
00754A43 JMP dumped_.007549C4

Il nostro SEH non funzionerà ancora tuttavia. Perche' tale codice fa uso di tabelle create a runtime nel padre. Questo significa che il figlio dovrebbe ricrearsele in qualche modo. Si aprono allora due scenari possibili:

a) cercare di riciclare il codice originale del padre per fargli generare le tabelle

b) dumpare direttamente tali tabelle dal padre ed attaccarle nel figlio come nuove sezioni o semplicemente inserirle nelle sezioni eventualmente già presenti. Io ho seguito la strada b), ma proprio come ho detto all'inizio del tutorial questa soluzione non e' a mio parere la piu' elegante e lascio ai posteri il compito e la voglia di studiare la a). Come ho fatto? semplice lancio il programma e se c'e' qualcosa che non va faccio un po' di tracing e mi aiuto con il programma originale che lanciato normalmente funziona come padre. Non scoraggiatevi se siete arrivati a questo punto, abbiamo quasi finito!

Per capire cosa dumpare e come, seguiamo sempre la procedura del confronto diretto tra il codice per come si comporta nel programma originale e quello che si ottiene nel programma da noi dumpato con il SEH appena installato. Chiudiamo le diverse istanze di olly attualmente in uso, ed apriamone due: una con il dumped.exe, ed una con il programma originale. In quest'ultimo ci interessa tracciare il codice del debugger o padre pertanto le patch alla OpenMutex non vanno applicate. In entrambe mettiamo un breakpoint all'indirizzo 0x7546D7 che e' il punto in cui cominciamo le effettive elaborazioni del contesto del programma all'atto dell'eccezzione. Effettuiamo lo step passo passo in entrambi i programmi cominciando a vedere cosa c'e' di sospetto che puo' provocare problemi. Si ottiene immediatamente che la seguente istruzione:

0075473C . 8B148D 88E9780>MOV EDX,DWORD PTR DS:[ECX*4+78E988]

fa riferimento ad una tabella che nel nostro dump e' vuota, mentre nel programma originale il debugger ha già riempito. Faremo il dump di tale sezione e siccome e' già presente nel dumpato la piazzeremo direttamente nell'eseguibile a mano con WinHex. Per capirne di piu' mettiamo un hardware breakpoint on write all'indirizzo 0x78e988 e vediamo quando tale tabella viene scritta. Praticamente dal codice si vede facilmente che il processo padre la riempie dopo aver decrittato alcune sezioni del processo figlio che legge attraverso la chiamata a ReadProcessMemory. Siccome in questo momento non mi va di studiare come effettivamente funziona tale codice (sarà oggetto di studio magari di prossimi tutorials), effettuo un semplice dump della sezione che contiene la tabella come anzi detto: alt-m in olly e dump della sezione che ha base pari a 0x788000 (.data1). Inoltre tale tabella contiene dei chiari riferimenti alle sezioni 0x02a00000 e 0x01e70000 che dumpiamo pure.

La sezione .data1 e' gia' presente nel nostro dumped.exe pertanto e' necessario copiare i dati dumpati con olly in tale sezione. Io ho effettuato tale copia con WinHex, usate pure quale tool desiderate. Dopo aver chiuso l'istanza di olly attualmente in uso con dumped.exe effettuiamo pertanto l'operazione suddetta. Inoltre con LordPE aggiungiamo due sezioni con i dati caricati dalle due sezioni 0x2a00000 e 0x1e70000, assicuriamoci che i virtual offset di tali sezioni siano corretti. Le due sezioni salvate sono precedenti all'ultima che avevamo aggiunto, quindi e' necessario prima togliere questa (una copia dell'ultima sezione dovreste averla già sul disco, quella a 0x3000000 per intenderci).

Riavviando il programma non parte ancora, l'ultima operazione di tracing ci fa capire che il nostro SEH ha bisogno di alcuni dati che vengono resi disponibili all'inizio attraverso il codice che va da 0x00753F5D a 0x00754105. Pertanto assembliamo una RETN a 0x00754105 ed una call 0x753F5D all'indirizzo 0x754979. Anche qesta scelta non e' il massimo dal punto di vista dell'efficienza dato che la call che abbiamo appena assemblato verrà chiamata ogni volta che viene incontrata una nanomite; poco male, tuttavia dato che parecchio dell'overhead introdotto da armadillo e' stato eliminato, primo fra tutti la presenza di due processi a favore di uno solo.

 Et voilà il gioco e' fatto.

Il programma parte in tutta tranquillità.

Ultimo tocco finale. Avevamo assemblato una memcpy all'inizio del nostro SEH. Se si assembla una call in Olly, essa verrà assemblata facendo riferimento all'indirizzo virtuale che il loader le ha già assegnato, pertanto il programma dumpato, se lasciamo tutto in questo modo, funzionerà olo sul vostro OS!. Inoltre la memcpy non e' presente tra le funzioni attualmente importate dal dumped.exe. Pertanto aggiungiamo alla import table una entry e un riferimento alla memcpy di msvcrt.dll. Cio' si fa in maniera immediata con LordPE. (PeEditor -> Directories -> Import Table -> add import....), segnamoci il ThunkRVA fornito da lordpe sommiamo la base 0x400000 e modifichiamo l'istruzione   call msvcrt.memcpy in CALL DWORD PTR [0xIndirizzo].

Riavviamo il programma ed otteniamo un dumpato perfettamente funzionante su cui ci si puo' ora divertire per scopi non puramente accademici.

                                                                                                                 Zarathustra

Note finali

La tecnica utilizzata non rappresenta sicuramente un capolavoro di reverse engineering, in quanto non vengono direttamente investigati in modo approfondito alcuni meccanismi intrinseci del codice di armadillo, ma semplicemente viene dumpato il programma. Essa mira a mostrare come l'uso di un apposito SEH spesso possa risolvere molti problemi nel MUP di target ostici come questo, oltre che a violare la protezione per il gusto di farlo a tutti i costi. Sicuramente qualcosa da fare e' quello di rivedere tutto il dump eliminando eventualmente sezioni inutili o altro overhead introdotto da armadillo. Per esempio la parte relativa ai salti al codice del packer potrebbe essere sostituita con del codice ad hoc che evita tali salti inutili. Ma questo e' un altro film.

A questo lavoro hanno partecipato indirettamente diverse persone che ringrazio per l'aiuto datomi e per le informazioni che mi hanno passato sul forum della UIC, fra tutti ringrazio in particolare SaturN, faina_mdc e LittleLuk.

Ringrazio, anche se probabilmente non lo saprà mai, Oleh Yuschuk, il papà di Olly. E' uno strumento fantastico, immediato e potente.

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.