Zoom Icon

Themida 2.0

From UIC

Themida 2.0 Unpack

Contents


Infos
Author: Root
Email: duat66@gmail.com
Website: none
Date: 21/07/2009 (dd/mm/yyyy)
Level: Quite hard
Language: Italian Image:Flag_Italian.gif
Comments: Formattazione Wiki: Antelox



Introduzione

Bene iniziamo a vedere questa protezione di cui avevo sentito parlare e letto qualcosa, ma su cui non avevo ancora voluto addentrarmi perché sapevo che non sarebbe stato semplice.


Tools

DarkOlly

StrongOD 0.2.5.388

Memory Manager

Target

Questi sono i tool indispensabili, gli altri li troverete nel corso del tutorial.


Primo avvio ( se si avvia… )

Lanciamo il programma con Olly e StrongOD settato in questo modo:

Image:StrongOD.JPG

Diamo uno sguardo iniziale al programma qui per esempio:

Image:Olly1.JPG

Image:Olly2.JPG

Image:Olly3.JPG

Come si vede il loader di Themida non carica la IAT per cui non si vedono le normali call[indirizzo iat] o jmp[indirizzo iat] ma vi sostituisce direttamente l’indirizzo dell’api per cui il dump in queste condizioni non sarebbe utilizzabile al prossimo riavvio o su un altro pc poichè la mappatura delle dll e quindi delle api non è su indirizzi fissi.

Ora diamo un’occhiata alla memoria:

Image:MemoryMap.JPG

Questa è la memoria che Themida carica per i sui scopi, tra cui vi sono tutte le sezioni necessarie per far funzionare la Virtual machine di Themida, che dovremo Dumpare come sezione unica ( io preferisco cosi che il dump di 6 sezioni ) che poi aggiungeremo al dump finale.


Dump Virtual Machine

Iniziamo proprio dalla VM, riavviamo Olly ed inseriamo un bph su VirtualAlloc ( gli deve pur allocare lo spazio necessario alle sezioni ) al primo Break guardiamo lo stack e vediamo:

Image:VirtualAlloc.JPG

Alloca 2000 byte, adesso premiamo Alt+F9 e ci troviamo qui:

Image:Jge.JPG

In Eax abbiamo l’indirizzo della memoria allocata il cui valore viene salvato alla riga 00624c4d, bene diamo Run ed al break Alt+F9 ci troviamo in questa parte interessante:

Image:Mov1.JPG

Viene allocata la memoria per altre 4 sezioni di cui quella alla riga 006f7593 più grande delle altre ( alloca 125000 byte ), se scorriamo il listato vediamo che dopo aver caricato i dati in queste sezione alloca spazio per un’altra sezione:

Image:Sezione1.JPG

Bene, adesso abbiamo tutte le sezioni della VirtuAl Machine di Themida, ricapitolando:


006F752F MOV SS:[EBP+7B58C0C],EAX(Vm Pointer) --- size 1000

006F754F MOV SS:[EBP+7B58C10],EAX(Vm Handler) --- size 1000

006F756F MOV SS:[EBP+7B58C14],EAX(Vm Entry) --- size 1000

006F7593 MOV SS:[EBP+7B58C1C],EAX(vm Code) --- size 125000

006F766C MOV SS:[EBP+7B50FDD],EAX(Vm Context) --- size 1000

00624C4D MOV SS:[EBP+7B549BA],EAX(Vm Stack) --size 2000

Come facciamo a mettere tutto questo in una sezione e poi fare il Dump della regione interessata? Bhè facile scriviamo un piccolo script come questo che ci aiuta:

var Vm_Pointer
Var Vm_Handler
var Vm_Entry
var Vm_Code
var Vm_Context
var Vm_Stack
var Vm_Sec_Count
var bpVirtualAlloc
var bpVmSection

mov Vm_Pointer, 02F00000
mov Vm_Handler, 02F01000
mov Vm_Entry,   02F02000
Mov Vm_Code,    02F03000
Mov Vm_Context, 03028000
Mov Vm_Stack,   03029000
Mov Vm_Sec_Count,0
 
gpa "VirtualAlloc", "Kernel32.dll"
mov bpVirtualAlloc, $RESULT
bphws  bpVirtualAlloc

Main_VM:
 run
 cmp eip,bpVirtualAlloc
 jne  Main_VM
 inc Vm_Sec_Count
 rtu
 jmp Fix_Vm

Fix_Vm:
 Vm Stack:
  Cmp Vm_Sec_Count,1
  jne Vm Pointer  
  find eip, #8985????????#
  mov pbVmSection,$RESULT
  bphws pbVmSection  
  run
  Mov eax, Vm_Stack  
  bphwc pbVmSection
  jmp Main_VM  
 Vm Pointer:
  Cmp Vm_Sec_Count,2  
  jne Vm Handler
  Mov eax, Vm_Pointer
  jmp Main_VM
 Vm Handler:
  Cmp Vm_Sec_Count,3
  jne Vm Entry
  Mov eax, Vm_Handler
  jmp Main_VM  
 Vm Entry:
  Cmp Vm_Sec_Count,4
  jne Vm Code
  Mov eax, Vm_Entry
  jmp Main_VM  
 Vm Code:
  Cmp Vm_Sec_Count,5
  jne Vm Context
  Mov eax, Vm_Code
  jmp Main_VM  
 Vm Context:
  Mov eax, Vm_Context  
  bphwc  bpVirtualAlloc

Lo script è abbastanza semplice l’unica cosa che non ho inserito nello script perchè complicava le cose inutilmente è quello di far allocare la memoria necessaria per le sezioni e poi mostrarmi quale indirizzo aveva utilizzato ( chi vuole può pure farlo… ).

Quindi per farlo funzionare basta utilizzare il Plug-In Memory Manager ed al System Break Point ( poichè dopo potremmo non avere la disponibilità dell’indirizzo di memoria che vogliamo usare ) agire in questo modo:

Image:MemoryAllocate.JPG

L’indirizzo è stato scelto a caso invece per la lunghezza è la somma della grandezza delle 6 sezioni + un pochino di spazio libero ( non si sa mai… ).


OEP

Per trovare l’Oep basta mettere un bph su GetProcessHeap ed al secondo break mettere un bpm on access sulla prima sezione e siamo sull’Oep . Come mai si è scelto questa Api? Vi chiederete ( ma anche no…haha ) , basta mettere un bph su GetProcAddress dopo che ha caricato le +6 sezioni della Vm e vedrete quali api carica perche servono al loader. Noterete che carica rtlAllocateHeap ( su Vista ) e questa è quella che ci fa steppare di meno , anzi per niente ( anche se non è sempre cosi ):

Image:Oep.JPG

Il nostro Oep è 005154e8, adesso però bisogna mettere in ordine la IAT e le call e jmp che non vi fanno riferimento come abbiamo visto all’inizio.


Fix Jmp&Call

In alcuni tutorial troverete che per sistemare la IAT a questo punto molti usano il tools UIF che carica la IAT o in una nuova sezione o dove c’è dello spazio libero sul file e fixa tutte le call e jmp, il problema che ho riscontrato con questo software e che la vm in alcune parti cerca le api all’indirizzo originale e non trovando nulla di valido va in errore, vabbè direte per una facciamo manualmente, e se invece fossero dieci? O se fossero in parti particolari del programma? Quindi la soluzione migliore e sistemare le cose a monte cioè dove Themida scrive quelle call e jmp , vediamo come procedere:

Image:Bph.JPG

Inseriamo un bph come vedete nell’immagine sulla prima call che troviamo e riavviamo il Olly.

Al primo break se guardiamo a quell’indirizzo vediamo che vi scrive dei nop:

Image:HexDumpNop.JPG

+ un nop prima ( per rispettare il size dell’istruzione originale... e per una sua verifica, ed a noi fa molto comodo… )

Al secondo break ci troviamo in queste condizioni:

Image:Sezione2.JPG

Image:HexDump.JPG

L’istruzione a 0079a532 stos byte [edi] inserisce il byte E8 ( call ) in edi, cioè nella nostra locazione di memoria. Adesso steppiamo con F8 e vediamo quando inserisce l’indirizzo della api. Se si trattava di un jmp inseriva E9 invece che E8:

Image:Stos.JPG

Image:Dispacement.JPG

Come vedete in questo punto scrive l’indirizzo o meglio il dispacement ( distanza ) dell’api. Continuiamo a steppare e controlliamo se notiamo qualche riferimento all’indirizzo della IAT che a noi serve per poterlo prelevare ( ci deve essere per forza ) quindi troviamo questo:

Image:Mov2.JPG

Image:Funzione.JPG

Scrive l’indirizzo della funzione nella IAT originale. Bene abbiamo tutto quello che ci serve in questo caso poiché la IAT non viene reindirizzata ( ma le cose non si complicavano di molto, bisognava cambiare qualche altra cosa sempre in questa routine ).

Adesso completiamo il nostro script con le informazioni trovate:

//variabili per la VM
var Vm_Pointer
Var Vm_Handler
var Vm_Entry
var Vm_Code
var Vm_Context
var Vm_Stack
var Vm_Sec_Count
var bpVirtualAlloc
var bpVmSection

// variabili per la IAT
var bpAddr_Api_Iat
var bpFix_Call_Jmp
var _eax
var oep
var iat_api_addrs

mov Vm_Pointer, 02F00000
mov Vm_Handler, 02F01000
mov Vm_Entry,   02F02000
Mov Vm_Code,    02F03000
Mov Vm_Context, 03028000
Mov Vm_Stack,   03029000
Mov Vm_Sec_Count,0
 
mov bpAddr_Api_Iat, 0079A41B              // 0079A41B MOV     DS:[EAX],ECX
mov bpFix_Call_Jmp, 0079A532              // 0079A532 STOS    BYTE PTR ES:[EDI]  
mov oep,                      005154e8              //  oep

gpa "VirtualAlloc", "Kernel32.dll"
mov bpVirtualAlloc, $RESULT
bphws  bpVirtualAlloc

Main_VM:
 run
 cmp eip,bpVirtualAlloc
 jne  Main_VM
 inc Vm_Sec_Count
 rtu
 jmp Fix_Vm

Fix_Vm:
 Vm Stack:
  Cmp Vm_Sec_Count,1
  jne Vm Pointer  
  find eip, #8985????????#
  mov pbVmSection,$RESULT
  bphws pbVmSection  
  run
  Mov eax, Vm_Stack  
  bphwc pbVmSection
  jmp Main_VM  
 Vm Pointer:
  Cmp Vm_Sec_Count,2  
  jne Vm Handler
  Mov eax, Vm_Pointer
  jmp Main_VM
 Vm Handler:
  Cmp Vm_Sec_Count,3
  jne Vm Entry
  Mov eax, Vm_Handler
  jmp Main_VM  
 Vm Entry:
  Cmp Vm_Sec_Count,4
  jne Vm Code
  Mov eax, Vm_Entry
  jmp Main_VM  
 Vm Code:
  Cmp Vm_Sec_Count,5
  jne Vm Context
  Mov eax, Vm_Code
jmp Main_VM  
 Vm Context:
  Mov eax, Vm_Context  
  bphwc  bpVirtualAlloc  
  jmp Fix_Iat


Fix_Iat:
 bphws bpAddr_Api_Iat
 bphws bpFix_Call_Jmp
 bphws oep

eob MainFixIat
run

MainFixIat:
 cmp eip, bpAddr_Api_Iat
 je h_Addr_Api_Iat
 cmp eip, bpFix_Call_Jmp
 je h_Fix_Call_Jmp
 cmp eip, oep
 je Fine
 run

h_Addr_Api_Iat:
mov iat_api_addrs, eax
 sti
 run

h_Fix_Call_Jmp:
 mov _eax, eax
 and _eax, 000000ff
 cmp _eax, 0e8
 je Fix_call
 cmp _eax, 0e9
 je Fix_jmp
 run

Fix_call:
 sub edi,1
 mov [edi], #ff150000#
 add edi, 2
 mov [edi], iat_api_addrs
 mov edi, 61b800
 add eip, 1
 run

Fix_jmp:
 sub edi, 1
 mov [edi], #ff250000#
 add edi, 2
 mov [edi], iat_api_addrs
 mov edi, 61b800
 add eip, 1
 run

Fine:
 Bphwc  bpAddr_Api_Iat
 bphwc  bpFix_Call_Jmp
 bphwc  oep
 msg "OEP raggiunto."
ret

Diciamo che l’unica cosa da precisare sullo script è quel ‘mov edi ,61b800’. Questo viene inserito per evitare che le nostre modifiche vengano sovrascritte quindi si sceglie un indirizzo vuoto ed accessibile per far scrivere al loader le sue istruzioni.

Bene adesso possiamo provare il tutto. Togliamo tutti i bp e riavviamo Olly al System BreakPoint inseriamo la nostra sezione con Memory Manager e avviamo lo script; dopo un po’ il risultato è questo:

Image:Call.JPG

Tutto ok sembrerebbe, vi è solo un’api non risolta, ma basta andare all’indirizzo segnato per capire che si tratta di wsprintfA

Image:Olly4.JPG

Quindi la IAT inizia a 53900 e finisce a 539cf0, quindi dump del file dump della sezione della vm. Avviamo ImpRec mettiamo a posto la IAT ed aggiungiamo la sezione della vm in questo modo:

Image:Cff.JPG

Come avrete notato io ho eliminato due sezioni che non mi servono più, ho rinominato qualche sezione ed ho sistemato il VA ( naturalmente ) della sezione della VM. Molto bene, adesso direte, è tutto sistemato…? Io vi dico no, manco per niente il programma va subito in errore all’avvio…uffffff adesso viene tutta la parte dei vari trick AntiDump.


AntiDump

Se adesso facciamo partire il programma dumpato va miseramente in errore ma cominciamo a guardare perché. Guardiamo la differenza che salta subito all’occhio quando siamo sull’OEP:

Image:AntiDump1.JPG

Image:AntiDump2.JPG

Come notate lo stack è sfalsato, ma non solo, la dword che si trova a 0012ff88 viene utilizzata come metodo antidump. Se inseriamo quella dword in quella posizione nel nostro file il programma parte ( da altri errori che vedremo dopo ) ma il controllo viene superato. Si potrebbe pensare di inserire quella dword con del codice prima di arrivare all’OEP ma appena riavviamo vedremo che và in errore nuovamente e nell’originale quella dword sarà diversa, allora che facciamo? Bhe io ho fatto il trace fino all’errore e poi mi sono salvato il file per analizzarlo con UltraEdit, ho messo una dword qualsiasi nella posizione 12ff88 e poi l’ho cercata nel trace per vedere di capire ed ho trovato questo, come dword ho inserito FFF5870A:

Image:Trace.JPG

In pratica fa lo xor tra la dword a 0012ff88 e la dword a 0012ff8c, molto bene allora noi che facciamo ? Noi avviamo l’originale e facciamo lo xor tra la dword a 0012ff88 e quella a 0012ff8c per noi il risultato rappresenta la costante ( anche per Themida ) e prima di arrivare all’OEP lo xoriamo con la dword che si trova a 0012ff8c praticamente inseriamo questo:

Image:Insert.JPG

Ancora il programma va in errore; un altro fix facile da trovare.

E’ il controllo relativo al Header del PE. A noi basta mettere un bpm on access sulla sezione del PE per vedere cosa modificare . Themida non controlla tutti i campi dell’header del PE ma soltanto il campo AddressOfEntryPoint che sul programma originale quando si trova sull’OEP è settato a 0 e noi faremo lo stesso in questo modo:

Image:AddressOfEntryPoint.JPG

La funzione VirtualProtect non è presente nella IAT del programma, noi ci ricorderemo di aggiungerla prima di dumpare il file e prima di avviare ImpRec ( aumentando anche la lunghezza della stessa ) in questo modo:

Image:VirtualProtect.JPG

Proviamo ad avviare il programma ma ancora non và. Prima di procedere una piccola nota per dire che in questi trick in effetti non vi è proprio nulla di complicato ma è stata solo fatta una buona opera di mascheramento niente di più ( il trucco più vecchio del mondo, nascondere le cose ). Anche per i prossimi trick utilizzo il run trace per trovare le parti da sistemare perché, se usato bene, aiuta molto, logicamente si deve cercare di tracciare il meno possibile poichè la VM è ricorsiva si arriva facilmente a trace di 40, 50 Mb ma con un po’ di astuzia si riesce a risolvere questi trick guardando dove l’originale e il dump prendono valori diversi:

Image:Dump1.JPG

Come si vede prende il valore contenuto in 0061cff9 che nel caso del dump è 77099433 invece nel caso del file originale corrisponde a LoadLibraryA quindi noi sistemiamo così:

Image:LoadLibraryA.JPG

Poi noteremo che ogni tanto va in errore in questa call:

Image:CallError.JPG

Che nell’originale corrisponde alla funzione Sleep. Per fixarlo basta vedere da dove preleva l’indirizzo, in questo caso da qui:

Image:Sleep1.JPG

Noi sistemiamo in questo modo:

Image:Sleep2.JPG

Quindi ricapitolando le modifiche che dobbiamo effettuare prima di arrivare all’entry point sono queste:

Image:Sub.JPG

Ok siamo quasi alla fine mancano altri due fix, che non credo dipendano propriamente da Themida ma da come è strutturato il programma, anche se Themida fa del suo meglio per incasinare le cose. Si tratta di trovare due costanti:

Image:Dump2.JPG

Un fix è questo, nella locazione 0061dffd si trova un puntatore ad una sezione non presente nel dump ( mica possiamo dumpare tutte le sezioni ) a sua volta questo puntatore punta ad una costante che Themida utilizza quindi noi sistemiamo così:

Image:Dump3.JPG

Come avrete notato nei casi più ostici io inserisco i trace del dump e dell’originale nello stesso file cosi mi riesce più facile capire le cose.

Adesso manca un ultimo fix che io ho sistemato cosi:

Image:Fix1.JPGImage:Fix2.JPG

Per trovare la costante ho sempre utilizzato il metodo del run trace anche se in questo ultimo caso vi sono degli xor che confondono un pochino le cose.

Ok adesso abbiamo finito ed il file funziona perfettamente:

Image:Olly5.JPG

Logicamente il file è solo unpackato... ( solo questo era il nostro scopo… )

Se in Themida cambieranno alcuni trick noi in ogni caso avremo gli strumenti per provare a risolvere senza stare li ed affidarsi a cose che ti dicono gli altri. Io ho cercato di risolvere le cose senza tener conto di tutto quello scritto in giro perché altrimenti si corre il rischio di rimanere bloccati non appena quelli della Oreans cambiano una virgola.

Nota: Nei trace se vi trovate in difficoltà e non riuscite a capire cercate sempre [Edi+70] Edi è il VM_Context ed a +70 si trova eFlags, se il programma prende una strada diversa lo vedete subito da qui ,quindi andate a ritroso nel trace.

Ciao.


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.