AUTOMATIC UNPACKING: VBox 4.6 |
||
Data |
by Ntoskrnl |
|
28/12/2003 |
Published by Quequero |
|
Bello riempire gli spazi vuoti! |
Ci
sono voluti 27 minuti di CPU per convertire da word html a html standard...
E andrea nel frattempo mi ha chiesto almeno 36 volte: hai uppato? hai uppato?
:P |
Bello riempire gli spazi vuoti! |
.... |
Home page se presente: http://pmode.cjb.net/ E-mail: [email protected] |
.... |
Difficoltà |
( )NewBies ( )Intermedio (*)Avanzato ( )Master |
AUTOMATIC UNPACKING: VBox 4.6
AUTOMATIC UNPACKING: VBox 4.6
Written by Ntoskrnl
Introduzione |
Per scaricare l'allegato al tutorial clickate qui.
Un bel giorno (9 dicembre), senza alcun preavviso, ad alcuni noi, di #crack-it,
ci arriva la seguente mail:
Salve gente,
Come ho gia' detto a andrea mi piacerebbe per Natale uscire conun xmas pack,
vale a dire facciamo tipo un tutorial a testa (logicamente inedito) che verra'
pubblicato la sera di natale, proprio in stile old school :))
Dunque andrea ha detto che puo' darmi il tute sull'asprotect, io comincio dopo
il 19 perche' ho due esami prima di allora quindi ancora non so cosa faro'.
Se voi altri foste interessati a partecipare al xmas pack fatemi sapere, 5 tute
dovrebbero essere carini da tirar su, ovviamente non siete obbligati ne spinti
a farlo, se avete idee e riuscite a concretizzarle per natale fatemi sapere
altrimenti non fa nulla :), rispondetemi cmq :)
ciauz gentaccia!
-=Quequero=-
....... Ma bene, andre (e anche spider in verità) parte già avvantaggiato dato
che deve finire un tutorial e noi veniamo avvertiti così tardi!!!!!! Que dove
hai la testa!!!!!!
E quanto predichi, io ho fatto il mio tute in due giorni
eppure mica mi sono lamentato cosi tanto! :) NdQue
A me rimangono 14 giorni che sarebbero largamente sufficienti per qualsiasi
tutorial, salvo per il fatto che:
- ho da andare a scuola
- ho da fare compiti in classe/a casa per la scuola
- ho lo sport
- i week-end non mi trovo a casa
- imprevisti vari ed eventuali
- non ho quasi nessuna idea per il tutorial
- le idee che ho non sono realizzabili nel tempo a disposizione
- mentre scrivo il tutorial, i giorni rimasti sono 5 dato che si è deciso di
far uscire lo xmas-pack a capodanno
Insomma ho sacrificato le feste di natale (che tanto mi fanno schifo) del 24,
25, 26. Poi bisogna dire una cosa, ci sono tante cosine che mi piacerebbe scrivere
ma che non hanno nessuna vera utilità ed io in genere cerco di evitare questo
tipo di cose (sebbene mi piaccia farle). Ho pensato di scrivere magari qualcosa
system related ma mi sono venute in mente solo idee banali.
Vabbè pazienza, alla fine m'è venuto in mente un tutorial sull'automatic unpacking
e siccome in Italia non ne ho visti di tutorial simili, m'è parsa un'idea carina.
Molto meglio scrivere un tutorial sull'automatic unpacking che sul manual unpacking
(che non amo per niente se fine a se stesso). In questo tutorial insomma vedremo
come fare un piccolo unpacker per vbox. Eh vabbè vbox, non ho il tempo di farne
uno per chissà cosa, in ogni caso vedrete che non sarà poi così semplice come
unpackare vbox.
Bah voialtri continuate a farvi le vostre seghe mentali col manual unpacking.
Ma ditemi voi che senso ha, la fatica che si fa è poco maggiore per l'automatic
unpacking, però a conti fatti ogni volta che ritroverete un programma packato
con la stessa versione del packer per cui avete scritto l'unpacker non farete
nessun faticoso manual unpacking, vi limeterete a qualche click. Anche a cambi
di versione spesso vi limiterete a qualche cambiamento nel vostro codice...!
Sappiate inoltre che questo tutorial è ideato per newbies (sì, ma non newbies
in tutto) e non per chi sa già unpackare asprotect, non vedo il motivo di scrivere
un tutorial per chi già sa fare certe cose; mi aspetto però che sappiate reversare,
conosciate approssimativamente bene il formato PE e la programmazione Win32
(questi sono veramente requisiti minimi). Quindi proprio perché ideato per newbies
il vbox come target va più che bene, comunque anche confrontandosi con packers
più ardui si potrà sempre fare ricorso alle idee esposte in questo tutorial,
che in ogni caso vuol trattare l'argomento in maniera generale.
Vediamo il programma vittima: Photoshop 7.0 (7.0.1 per la precisione). E' veramente
un bel programma e da quando mi sono dato alla grafica per il ritocco delle
immagini (modestia a parte sono bravo) lo uso parecchio. La Adobe è così gentile
da fornirci un programma completo che impone però una scadenza di 30 giorni...
Be' noi di giorni ne abbiamo anche meno (per finire il tutorial) quindi direi
che siamo a cavallo.
Ah tra l'altro nella cartella di photoshop ci sono due programmi photoshop.exe
e imageready.exe, sono entrambi packati con lo stesso vbox e quindi non fa alcuna
differenza quale andremo ad usare per analizzare il packer: potrebbe forse tornare
comodo usare ImageReady perché più piccolo e quindi di caricamento più veloce,
io infatti ho lavorato su quello. Quindi ogni offset si riferirà sempre a Image
Ready, ricordatelo (anche se non so cosa vi dovrebbe importare dei pochi offset
che vedrete nel tutorial).
Dunque per tracciare il codice ho fatto uso delle debug api che come tutti sapete
non sono proprio il massimo della potenza, in ogni caso non ho il tempo di scrivere
anche un debug engine, inoltre per vbox le dbg api sono più che sufficienti.
Insomma mi aspetto che sappiate usare le dbg api (che tra l'altro sono semplicissime)
e vedrete che nonostante tutto non conta che metodo per tracciare ho usato.
Essay |
Prima di iniziare un'ultimissima cosa, il packer realizzato in questo tutorial
l'ho scritto giustappunto per il vbox 4.6 usato da photoshop e imageready, non
l'ho provato su altre versioni di vbox né su altri programmi, quindi non lo
so se funge sempre, diciamo che non ho avuto il tempo per assicurarmene. Però
questo non ci interessa in verità, al massimo ci sarà da modificare qualcosina
ed in ogni caso appena ho tempo magari m'impegno a perfezionare l'unpacker in
modo che sia veramente distribuibile senza rischi (sarebbe il caso dato che
di unpacker completi per vbox 4.6 non ne ho visti). Inoltre l’unpacker funge
solo su sistemi NT, questo perché ho fatto uso delle psapi, magari poi lo riscriverò
in modo che faccia uso sia delle toolhelp api che delle psapi in modo che funga
anche su sistemi 9x/ME.
INIZIAMO AD UNPACKARE
Il modus operandi che in questo tutorial seguirò sarà quello di commentare pezzo
per pezzo il codice dell'unpacker e capire come sono arrivato a scriverlo.
Vi stupirete di una cosa: ho fatto uso di debugger solo per vedere come vbox
risolveva la IAT, sono giunto all'Original Entry Point senza analizzare una
sola riga di codice. Da questo potete capire quanto sia imbecille il packer...
Quindi fino all'OEP non vedrete niente di ciò che il packer fa dato che non
ci interessa minimamente.
Ma prima di pensare a OEP, andiamo con ordine! Come prima cosa l'unpacker chiede
all'utente il nome del programma da unpackare (grazie al cazzo), dopodiché alloca
sufficiente memoria per prendere almeno le informazioni essenziali per quanto
riguarda il PE, ovvero: Dos Header, Nt Headers e Section Table. Prese le informazioni,
controllo che il PE sia valido e che sia effettivamente stato packato con vbox:
IMAGE_DOS_HEADER
*ImgDosHeader;
IMAGE_NT_HEADERS *ImgNtHeaders;
IMAGE_SECTION_HEADER *ImgSectionHeader;
// potrebbe crashare tutto, non si sa mai
__try
{
ImgDosHeader = (IMAGE_DOS_HEADER *)(DWORD)
FileBuf;
ImgNtHeaders = (IMAGE_NT_HEADERS *)(DWORD)
&FileBuf[ImgDosHeader->e_lfanew];
if (ImgDosHeader->e_magic !=
IMAGE_DOS_SIGNATURE ||
ImgNtHeaders->Signature
!= IMAGE_NT_SIGNATURE)
{
printf("%s is not
a valid PE file\n", ProgName);
CloseAll();
return;
}
ImgSectionHeader = IMAGE_FIRST_SECTION(ImgNtHeaders);
// faccio i controlli relativi
all'unpacking
if (ImgNtHeaders->FileHeader.NumberOfSections
< 2)
{
printf("Cannot unpack:
merged sections?\n");
CloseAll();
return;
}
// è packato con vbox, controllo
leim
if (strncmp((char *) &ImgSectionHeader[
ImgNtHeaders->FileHeader.NumberOfSections
- 1].Name,
"PREVIEW", sizeof
("PREVIEW") - 1) != 0)
{
printf("Cannot unpack:
seems not to be packed with vbox\n");
CloseAll();
return;
}
}
__except (TRUE)
{
printf("An error occurred\n");
CloseAll();
return;
}
Come potete vedere oltre ai loffi controlli sulla validità del PE, c'è un strncmp
che non fa altro che controllare che l'ultima sezione del PE abbia nome PREVIEW
(che è appunto la sezione del vbox). Dopo questi stupidi controlli, assumo che
il PE sia valido e packato con vbox e quindi in futuro non userò più nessun
controllo delle eccezioni (solo quelle del programma che sto debuggando, non
quelle del mio programma). A questo punto avvio il processo di Image Ready per
debuggarlo.
STARTUPINFO
si;
GetStartupInfo(&si);
// creo il processo
if (CreateProcess(ProgName, NULL, NULL, NULL, FALSE,
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,
NULL, NULL, &si, π) == FALSE)
{
printf("Cannot start process\n");
CloseAll();
return;
}
Faccio notare i flag DEBUG_PROCESS e DEBUG_ONLY_THIS_PROCESS che mi servono
proprio per poter usare le Debug Api. Inizio quindi il ciclo di debug e mi preparo
a ricevere il primo evento
while
(TRUE)
{
if (!WaitForDebugEvent(&DbgEvent, INFINITE))
break;
// prendo il Context
Context.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &Context);
Come potete vedere il GetThreadContext viene effettuato ad ogni ciclo, così
nel caso debba leggerlo e/o settarlo non debbo chiamare la suddetta funzione
all'interno di ogni evento. Il primo evento che mi capita sotto le mani e quello
di creazione del processo, il quale mi torna mooolto utile dato che lo uso per
fare una certa cosa che adesso vedrete:
if
(DbgEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
{
StartAddr = RvaToVa(ImgNtHeaders,
ImgSectionHeader->VirtualAddress);
Size = ImgSectionHeader[1].VirtualAddress
-
ImgSectionHeader->VirtualAddress;
// cambio i diritti
per la sezione code
// ovvero interdico l'accesso
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size,
PAGE_NOACCESS, &OldProtect);
}
Come potete vedere all'interno di questo evento invalido la memoria corrispondente
alla prima sezione del PE (ovvero quella che in genere contiene il codice del
programma). Ma perché faccio ciò? Be' d'ora in poi finché non giungo all'OEP
terrò invalidata la sezione per controllare gli accessi ad'essa... Mi spiego
meglio, generalmente nei tutorial sul manual unpacking di vbox c'è scritto di
settare un bpx su GetVersion e far poppare il sice, capite però bene che questo
non possiamo farlo nell'unpacker, inoltre sarebbe una soluzione ridicola. Infatti
noi invalideremo la sezione che contiene l'oep affinché ogni accesso ad essa
genererà un access violation. Mi direte voi: si ma anche per decrittare la sezione
il packer accederà alla memoria invalidata e genererà un access violation. E'
vero ma a noi basterà controllare che l'istruzione che ha generato l'eccezione
sia all'interno della prima sezione per sapere che è la prima istruzione (ovvero
l'OEP) che viene eseguita in tale sezione. Se non si tratta dell'OEP renderemo
la memoria di nuovo leggibile - scrivibile, rieseguiremo l'istruzione che ha
generato l'eccezione e dopo di che l'avremo eseguita reinvalideremo un'altra
volta la memoria in attesa della prossima eccezzione che a sua volta potrà essere
finalmente l'OEP oppure no e se no... Sappiamo cosa succede. Ok, ma vediamo
come viene gestita la access violation.
else if (DbgEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_BREAKPOINT)
{
// ancora non
ci interessa questa
// eccezione,
la vedremo dopo
}
else if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_ACCESS_VIOLATION)
{
// verifico se
l'eccezione è causata da me
if (OEP
== 0)
{
// rimetto a posto
eip
Context.Eip
= (UINT) DbgEvent.u.Exception.
ExceptionRecord.ExceptionAddress;
// rendo leggibile
la locazione
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size,
PAGE_READWRITE, &OldProtect);
// controllo se
siamo arrivati all'oep
if
(Context.Eip >= StartAddr &&
Context.Eip
< (StartAddr + Size))
{
// vedremo dopo
cosa succede nel caso
//
fossimo giunti all'OEP
}
else
{
// setto il trap
flag per il single step
//
dato che devo reinvalidare la memoria
Context.EFlags
|= TRAP_FLAG;
SetThreadContext(pi.hThread,
&Context);
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
}
Nel caso la variabile OEP sia 0 so benissimo a cosa sono dovute le eccezioni
access violation, prima però di vedere se ho trovato l'OEP metto a posto le
cose in modo che l'esecuzione possa proseguire normalmente, metto a posto eip:
Context.Eip
= (UINT) DbgEvent.u.Exception.ExceptionRecord.ExceptionAddress;
Ovvero faccio puntare eip all'istruzione che ha generato l'eccezione ma che
questa volta che verrà rieseguita non genererà proprio nulla. E per fare ciò
è necessario che renda accessibile la memoria:
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size, PAGE_READWRITE, &OldProtect);
Altrimenti si ripete l'eccezione. Fatto ciò controllo se sono giunto all'OEP
con:
if (Context.Eip
>= StartAddr && Context.Eip < (StartAddr + Size))
Questo if non fa altro che vedere se l'istruzione che ha generato l'eccezione
sta nella prima sezione di codice, se sì allora è proprio l'OEP. Se però così
non fosse viene eseguito l'else che segue l'if. Questa routine setta il single
step:
Context.EFlags
|= TRAP_FLAG;
TRAP_FLAG è definito appositamente per settare l'ottavo bit di EFlags, ovviamente
per rendere effettivo il tutto devo poi chiamare SetThreadContext. Una volta
attivato il single step, subito dopo aver eseguito una istruzione, viene generata
l'eccezione single step:
else
if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_SINGLE_STEP)
{
if (OEP
== 0)
{
// rendo di nuovo
non accessibile la memoria
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size,
PAGE_NOACCESS, &OldProtect);
}
E come vedete rendo di nuovo inaccessibile la memoria. Il single step non c'è
bisogno di disattivarlo, si disattiva da solo ogni volta che viene generato,
quindi per creare un flusso dovrei risettare l'ottavo bit di EFlags all'interno
di ogni EXCEPTION_SINGLE_STEP.
Ok, adesso possiamo vedere che succede una volta arrivati all'OEP.
if
(Context.Eip >= StartAddr &&
Context.Eip
< (StartAddr + Size))
{
OEP
= VaToRva(ImgNtHeaders, Context.Eip);
printf("OEP:
%X\n", OEP);
Ok, ho ricavato l'OEP che adesso ho in versione RVA. Adesso però posso dumparmi
anche tutte le sezioni del PE (fatta eccezione per la PREVIEW del vbox). La
cosa bella è che adesso tutte le sezioni sono in chiaro (yahoo), l'unica cosa
che ci resta da fare è ricostruire la IAT e quindi l'IT.
char
DumpName[MAX_PATH];
printf("Insert
name of the dump file:\n");
scanf("%s",
DumpName);
hDumpFile
= CreateFile(DumpName, GENERIC_WRITE,
NULL,
NULL, CREATE_ALWAYS, NULL, NULL);
if
(hDumpFile == INVALID_HANDLE_VALUE)
{
printf("Cannot
create file\n");
break;
}
// metto a posto
alcuni campi per il dump
ImgNtHeaders->OptionalHeader.FileAlignment
=
ImgNtHeaders->OptionalHeader.SectionAlignment;
ImgNtHeaders->OptionalHeader.SizeOfHeaders
=
ImgSectionHeader->VirtualAddress;
// lascio lo spazio
per gli headers
//
che dumperò alla fine
SetFilePointer(hDumpFile,
ImgSectionHeader->VirtualAddress,
NULL,
FILE_BEGIN);
SetEndOfFile(hDumpFile);
WORD
y = ImgNtHeaders->FileHeader.NumberOfSections - 1;
BYTE
*Section;
//
dumpo tutte le sezioni
//
tranne quella del vbox
for
(WORD x = 0; x < y; x++)
{
StartAddr
= RvaToVa(ImgNtHeaders,
ImgSectionHeader[x].VirtualAddress);
Size
= ImgSectionHeader[x + 1].VirtualAddress -
ImgSectionHeader[x].VirtualAddress;
Section
= new BYTE [Size];
if
(Section == NULL)
{
printf("Not
enough memory\n");
break;
}
if
(ReadProcessMemory(pi.hProcess, (LPVOID) StartAddr,
Section,
Size, &BR) == FALSE)
{
printf("An
error occurred\n");
delete
Section;
break;
}
if
(!WriteFile(hDumpFile, Section, Size, &BW, NULL))
{
printf("An
error occurred\n");
delete
Section;
break;
}
delete
Section;
ImgSectionHeader[x].PointerToRawData
=
ImgSectionHeader[x].VirtualAddress;
ImgSectionHeader[x].SizeOfRawData
= Size;
}
Ok, adesso che ho
già una buona parte del file dumpato devo ripristinare la cosa più boriosa:
la IAT. Vediamo intanto come viene ripristinata dal vbox. Partiamo dal GetVersion
che sta poco dopo l'Original Entry Point.
:00CF567A
align 4
:00CF567C push ebp
:00CF567D mov ebp, esp
:00CF567F push 0FFFFFFFFh
:00CF5681 push offset unk_E3CE18
:00CF5686 push offset sub_CF53D4
:00CF568B mov eax, large fs:0
:00CF5691 push eax
:00CF5692 mov large fs:0, esp
:00CF5699 sub esp, 58h
:00CF569C push ebx
:00CF569D push esi
:00CF569E push edi
:00CF569F mov [ebp-18h], esp
:00CF56A2 call ds:dword_DC94C8 ; GetVersion
Come vedete la call ds:dword_DC94C8 punta alla IAT e più specificamente a GetVersion,
il valore però dell'api non risolta è 2180000h, sì perché le api non risolte
nella IAT generalmente hanno un valore che va da 2000000h a 2FFFFFFh (ma non
è detto, quindi non possiamo basarci su quello). Quindi se in softice andremo
a vedere cosa succede quando viene chiamata l'api, troveremo una call di questo
tipo:
:02180000
call 0700E61F
Questa call ci porta ad un'altra routine che contiene: call 0700E659. Entrando
anche in questa call, finiremo per trovare la call:
:0700E66D
call 0700C100
Subito dopo questa call abbiamo in eax l'entry point dell'api da risolvere.
Ritorniamo quindi alla call 0700E659 e vediamo le istruzioni che la seguono.
:0700E642
call 0700E659
:0700E647 add esp, 10h
:0700E64A mov eax, [ebp - 04h]
:0700E64D mov ebx, [ebp - 08h]
:0700E650 mov ecx, [ebp - 0Ch]
:0700E653 mov edx, [ebp - 10h]
:0700E656 pop ebx
:0700E657 leave
:0700E658 ret
Una volta arrivato al ret all'indirizzo puntato dallo stack pointer è l'entry
point dell'api risolta, così una volta eseguito questo ret verrà eseguita anche
l'api. Ma come facciamo ad arrivare a questo ret? Cioè io non posso presupporre
che la prima api chiamata sia GetVersion, né so quale api sono o no da risolvere.
Quind dopo essere giunto all'OEP mi metto un bel break point a GetProcAddress:
// ricavo l'address
di GetProcAddr
hKernel32
= GetModuleHandle("kernel32.dll");
AddrOfGetProc
= (DWORD) GetProcAddress(hKernel32,
"GetProcAddress");
// salva il byte
originale
ReadProcessMemory(pi.hProcess,
(LPCVOID)
AddrOfGetProc,
&BP_Buf, sizeof (BYTE), &BR);
// setto il break
point a GetProcAddr
WriteProcessMemory(pi.hProcess,
(LPVOID)
AddrOfGetProc,
&BP, sizeof (BYTE), &BW);
// setto true
il valore che conferma
//
che il prossimo bp è generato da me
SearchGetProcAddress
= TRUE;
}
Giunto al GetProcAddress
seguo tutte le istruzioni che lo seguono (istruzioni che potrebbero anche non
essere pertinenti con quello che sto cercando ma prima o poi lo trovo il ret).
if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_BREAKPOINT)
{
if (SearchGetProcAddress
== TRUE)
{
// siamo giunti
al fatidico GetProcAddress
//
tolgo il break point
Context.Eip--;
WriteProcessMemory(pi.hProcess,
(LPVOID)
AddrOfGetProc,
&BP_Buf, sizeof (BYTE), &BW);
// nel caso ci
fossero altri bp non voluti da me
SearchGetProcAddress
= FALSE;
// devo risalire
a alle cose che mi interessano
//
attivo il single step
Context.EFlags
|= TRAP_FLAG;
SetThreadContext(pi.hThread,
&Context);
//
attivo la var per passare al secondo
//
ciclo di debug
bOk
= TRUE;
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
break;
}
Come vedete infatti
inizio un flusso di single step (dopo aver ovviamente rimesso a posto le cose
dopo l'eccezione). Come vedete setto una variabile bOk e poi interrompo il ciclo
di debug per poi iniziarne uno nuovo, in verità questo non è necessario, lo
faccio solamente per non mettere troppo codice assieme. Prima però di iniziare
il nuovo ciclo, prendo un po' di informazioni riguardanti il modulo del vbox
presente nell'address space dell'applicazione che sto unpackando:
// prendo image base e size del modulo
// del vbox
DWORD VboxBase, VboxSize;
if (GetModuleInfo("vboxtb.dll", &VboxBase,
&VboxSize) == FALSE)
{
printf("Cannot retrive vboxtb info\n");
CloseAll();
return;
}
Ciò ci servirà in seguito per diverse cose. Inoltre alloco spazio per la sezione
Ntoskrnl (contenente descriptors e un eventuale ET (capirete dopo)) che andrà
a sostituire quella del vbox.
// + che bastanti
NewSect = new BYTE [
sizeof (IMAGE_IMPORT_DESCRIPTOR) * 100];
E leggo i falsi descriptos nella sezione del vbox.
// trovo i vecchi descriptors
IMAGE_IMPORT_DESCRIPTOR *Descr =
(IMAGE_IMPORT_DESCRIPTOR *)
NewSect;
if (ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders, ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress), Descr, sizeof
(IMAGE_IMPORT_DESCRIPTOR) * 100, &BR)
== FALSE)
{
printf("An error occurred\n");
return;
}
Adesso riechiamovi al Single Step alla ricerca del fatidico ret. Per prima cosa
leggo la prossima istruzione che dovrà essere eseguita secondo l'Eip:
BYTE
Istro;
// leggo la prossima
istro che viene eseguita
if (ReadProcessMemory(pi.hProcess,
(LPCVOID) Context.Eip,
&Istro,
sizeof (BYTE), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
Chiaramente se siamo giunti al punto giuto l'istruzione deve essere un ret (0xC3)
e deve essere nel modulo del vbox altrimenti siamo giunti ad un altro ret che
non ha niente a che fare col nostro, quindi mi accerto anche di queste due cose:
if
(Istro == 0xC3 &&
(Context.Eip
>= VboxBase &&
Context.Eip
< (VboxBase +
VboxSize)))
{
Accertate anche queste condizioni voglio essere sicuro che il ret sia quello
fatidico, quindi l'esp dovrà puntare ad un indirizzo che stavolta non è nel
modulo del vbox (altrimenti ci troveremmo di fronte ad un altro ret del vbox).
// legge lo stack
pointer
DWORD
NextAddr;
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
Context.Esp,
&NextAddr, sizeof (DWORD),
&BR)
== FALSE)
{
printf("An
error occurred\n");
break;
}
// controllo se
è il fatidico ret
if
(NextAddr < VboxBase ||
NextAddr
>= (VboxBase + VboxSize))
{
Se anche questo if risulta vero allora ho trovato il ret. A questo punto dobbiamo
iniziare a chiederci come faremo a risolvere la IAT del vbox. L'idea sarebbe
quella di partire dall'inizio della IAT (vedremo dopo come ricavarla) e far
chiamare ad una call [eax], messa da me all'EP, tutte le api non risolte (vedremo
dopo come capire se sono risolte o no), quindi siccome al fatidico ret avremo
posizionato un break point veniamo avvisati quando giungeremo a quel punto e
avremo all'indirizzo puntato da Esp l'enty dell'api risolta che provvederemo
a posizionare nella IAT. Fatto ciò sostituiremo l'indirizzo puntato da Esp con
quello dell'EP in modo che ricominci un nuovo ciclo con call [eax]. Ma prima
di andare così avanti vediamo i preparativi:
RetAddress
= Context.Eip;
WORD
Call = 0x10FF; // call [eax]
// mette la call
all'entry point
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
VA_OEP,
&Call, sizeof (WORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
// setto il break
point al ret
BP_Buf
= 0xC3;
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
RetAddress,
&BP, sizeof (BYTE), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
Adesso dobbiamo sapere dove sta la IAT. Ci sono due possibilità, o l'utente
specifica l'indirizzo:
//
chiedo all'utente se la iat la trovo
//
da solo o se me la dice lui
printf("Autofind
IAT? [Y/N]: ");
C
= getch();
if
(C != 'y' && C != 'Y')
{
char
Buffer[10];
printf("\n\nStart
Address: ");
scanf("%s",
Buffer);
StartOfIAT
= strtoul(Buffer, 0, 16);
printf("\nEnd
Address: ");
scanf("%s",
Buffer);
EndOfIAT
= strtoul(Buffer, 0, 16);
}
Oppure me la trovo da me. Il metodo che ho usato è alquanto rozzo. Siccome non
volevo basarmi su altri riferimenti nel codice del vbox (dato che è anche quello
insicuro, cambiamenti di versione implicherebbero molti cambiamenti nel codice).
Mi sono preso l'entry di un api risolta nella fake IAT del vbox e l'ho cercata
nella memoria del processo a partire dal Base Of Data, una volta trovata ho
eseguito un backtrace alla ricerca di due dword nulle o almeno fermandomi ad
un indirizzo che corrispondeva all'inizio di una sezione del PE. Una volta trovato
l'inizio ho cercato la fine della IAT questa volta cercando solamente due dword
nulle.
else
{
// devo cercare
la IAT
//
e per farlo parto dal Base Of Data
DWORD
Base;
StartOfIAT
= Base =RvaToVa(
ImgNtHeaders,
ImgNtHeaders->OptionalHeader.
BaseOfData);
// posso cercare
fino alla fine
//
dell'image size
EndOfIAT
= RvaToVa(ImgNtHeaders,
ImgNtHeaders->OptionalHeader.
SizeOfImage);
DWORD
Api;
//
prendo la prima api risolta
//
nella fake IAT del vbox
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
Descr[0].FirstThunk),
&Api,
sizeof (DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
// e la cerco
a partire dal
//
Base Of Data
DWORD
*Data = new DWORD [PAGE_SIZE /
sizeof
(DWORD)];
BOOL
bFound = FALSE;
while
(StartOfIAT < EndOfIAT)
{
ReadProcessMemory(pi.hProcess,
(LPCVOID)
StartOfIAT,
Data, PAGE_SIZE, &BR);
DWORD
Count = 0;
for
( ; Count < PAGE_SIZE /
sizeof
(DWORD); Count++)
{
if
(Data[Count] == Api)
{
StartOfIAT
+= Count
*
sizeof (DWORD);
bFound
= TRUE;
break;
}
}
if
(bFound == TRUE)
break;
StartOfIAT
+= PAGE_SIZE;
}
if
(bFound == FALSE)
{
printf("Cannot
find IAT\n");
break;
}
DWORD
Check;
//
va indietro cercando due dword nulle
//
oppure l'inizio di una serzione
//
per trovare l'inizio della IAT
bFound
= FALSE;
while
(StartOfIAT >= Base)
{
//
controllo a partire della
//
seconda sezione
for
(UINT x = 1; x <
ImgNtHeaders->FileHeader.
NumberOfSections;
x++)
{
if
(ImgSectionHeader[x].
VirtualAddress
== VaToRva(
ImgNtHeaders,
StartOfIAT))
{
StartOfIAT
-= sizeof (DWORD);
bFound
= TRUE;
break;
}
}
if
(bFound == TRUE)
break;
StartOfIAT
-= sizeof (DWORD);
ReadProcessMemory(pi.hProcess,
(LPCVOID)
StartOfIAT,
&Check, sizeof (DWORD), &BR);
if
(Check == NULL)
{
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
(StartOfIAT
- sizeof (DWORD)), &Check,
sizeof
(DWORD), &BR))
{
if
(Check == NULL)
{
bFound
= TRUE;
break;
}
}
}
if
(bFound == TRUE)
break;
}
if
(bFound == FALSE)
{
printf("Cannot
find IAT\n");
break;
}
StartOfIAT
+= sizeof (DWORD);
EndOfIAT
= StartOfIAT + sizeof (DWORD);
bFound
= FALSE;
while
(TRUE)
{
ReadProcessMemory(pi.hProcess,
(LPCVOID)
EndOfIAT,
&Check, sizeof (DWORD), &BR);
if
(Check == NULL)
{
ReadProcessMemory(pi.hProcess,
(LPCVOID)
(EndOfIAT
+ sizeof (DWORD)),
&Check,
sizeof (DWORD), &BR);
if
(Check == NULL)
{
bFound
= TRUE;
break;
}
}
EndOfIAT
+= sizeof (DWORD);
}
if
(bFound == FALSE)
{
printf("Cannot
find end of IAT\n");
break;
}
}
//
ptr iat = inizio iat
IAT_Ptr
= StartOfIAT;
A questo punto ho interrotto il flusso di single step e ho mi sono messo in
attesa del primo break point:
if
(DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_BREAKPOINT)
{
// rimetto a posto
dopo l'eccezione
if (WriteProcessMemory(pi.hProcess,
(LPVOID)
RetAddress,
&BP_Buf, sizeof (BYTE), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
Context.Eip--;
// per ripristinare
in seguito il bp
Context.EFlags
|= TRAP_FLAG;
Ogni volta dopo il break point devo rimettere a posto le cose e far eseguire
il ret, avvio però il single step in modo che dopo il ret (che ci riporta alla
nostra call) venga rimesso a posto il break point, infatti:
else
if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_SINGLE_STEP)
{
// se ho già il
Ret Address
if (RetAddress
!= 0)
{
// rimetto il
break point
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
RetAddress,
&BP, sizeof (BYTE), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
Ritornando all'evento break point, devo stavolta controllare se è effettivamente
iniziato il ciclo di ricostruzione della IAT:
// ho cominciato
il ciclo di ricostruzione?
if (bInit
== FALSE)
{
bInit
= TRUE;
// prima api da
risolvere
IAT_Ptr
= NextApiToSolve(IAT_Ptr,
EndOfIAT);
if
(!IAT_Ptr)
break;
Context.Eax
= IAT_Ptr;
// setto lo stack
pointer
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
Context.Esp,
&VA_OEP, sizeof (DWORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
SetThreadContext(pi.hThread,
&Context);
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
Come potete vedere, se così non fosse mi limito a settare la prima api da risolvere
e far eseguire la call. Se invece il ciclo è già iniziato, Esp punterà al VA
di una Api risolta che io opportunamente devo piazzare nella IAT. Ma prima di
vedere il codice che fa questo facciamo una piccola digressione sulla funzione
NextApiToSolve che non fa altro che trovarmi il VA della prossima Api da risolvere.
DWORD NextApiToSolve(DWORD
IAT_Ptr, DWORD EndOfIAT)
{
DWORD Api, BR;
while (IAT_Ptr < EndOfIAT)
{
if (ReadProcessMemory(pi.hProcess, (LPCVOID)
IAT_Ptr, &Api, sizeof
(DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
return
FALSE;
}
// controllo se l'api è da risolvere
// non basta vedere se l'indirizzo è
// contenuto nello space address del
// rispettivo modulo: pensate
// all'export forwarding
if (Api != 0)
{
BYTE IC;
if (ReadProcessMemory(pi.hProcess,
(LPCVOID)
Api,
&IC, sizeof (BYTE), &BR))
{
if (IC
== 0xE8)
{
DWORD
Check = 0;
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
(Api
+ 1), &Check, sizeof (DWORD), &BR))
{
// controllo se
è una chiamata vbox
Check
= Check + Api + 5;
// deve essere
vicino al RetAddress
if
(Check >= (RetAddress - 100) &&
Check
< RetAddress)
break;
}
}
}
}
IAT_Ptr += sizeof (DWORD);
}
return IAT_Ptr;
}
La funzione non fa altro che leggere dal VA nella IAT, se alla memoria a cui
punta vi è una call (0xE8) controlla l'indirizzo a cui salta questa call, se
salta nei pressi del RetAddress allora è effettivamente una Api non risolta.
Ok, possiamo tornare al codice vero e proprio. Come detto devo piazzare l'Api
risolta nella IAT:
// leggo l'api
risolta
DWORD
Entry;
if (ReadProcessMemory(pi.hProcess,
(LPCVOID)
Context.Esp,
&Entry, sizeof (DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
// metto l'entry
risolta nella iat
if (WriteProcessMemory(pi.hProcess,
(LPVOID)
IAT_Ptr,
&Entry, sizeof (DWORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
dopodiché prendo il pointer alla prossima Api da risolvere:
// prossima api
da risolvere
IAT_Ptr
= NextApiToSolve(IAT_Ptr,
EndOfIAT);
if (!IAT_Ptr)
break;
E la setto in eax in modo che venga chiamata da call [eax]:
Context.Eax
= IAT_Ptr;
Setto lo stack pointer in modo che il ret passi il controllo alla mia call:
if (WriteProcessMemory(pi.hProcess,
(LPVOID)
Context.Esp,
&VA_OEP, sizeof (DWORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
Quindi setto il context e continuo il debug:
SetThreadContext(pi.hThread,
&Context);
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
Ma, se siamo giunti alla fine della IAT facciamo tutt'altra roba.... Innanzitutto
mi leggo la IAT risolta dalla memoria del processo:
if
(IAT_Ptr >= EndOfIAT)
{
// sono giunto
alla fine della IAT
//
mi leggo la IAT risolta
DWORD
*IAT;
IAT
= new DWORD [(EndOfIAT - StartOfIAT) /
sizeof
(DWORD)];
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
StartOfIAT,
IAT, (EndOfIAT - StartOfIAT),
&BR)
== FALSE)
{
printf("An
error occurred\n");
break;
}
E comincio a preparare le cose per la mia nuova sezione la Ntoskrnl section
che andrà a sostituire come già detto quella del vbox e che conterrà i descriptors
della IT e una eventuale ET (vedremo dopo).
//
elimino la sezione del vbox
//
e metto una mia per i descriptors
//
e per l'eventuale ET
WORD
S = ImgNtHeaders->FileHeader.NumberOfSections - 1;
ZeroMemory(&ImgSectionHeader[S],
IMAGE_SIZEOF_SECTION_HEADER);
memcpy(ImgSectionHeader[S].Name,
"Ntoskrnl",
strlen("Ntoskrnl"));
ImgSectionHeader[S].VirtualAddress
=
ImgSectionHeader[S
- 1].VirtualAddress +
CalcAlignment(
ImgNtHeaders->OptionalHeader.SectionAlignment,
ImgSectionHeader[S
- 1].Misc.VirtualSize);
Ora io ho, se vi ricordate, in Descr i descriptors del vbox di cui adesso dovrò
collegare gli FT alle rispettive posizioni nella vera IAT. Fare ciò è molto
semplice basta prendere il valore dell'unica api importata e risolta per ogni
modulo importato dal PE nella fake IT del vbox e cercarlo nella nostra IAT risolta,
dopodiché fare un back trace alla ricerca di una dword nulla che fa ovviamente
da terminatore di thunk data dell'array precedente.
// collega i descriptors
alla iat
BYTE
*NamePos = (BYTE *)
((sizeof
(IMAGE_IMPORT_DESCRIPTOR) *
(DescrNum
+ 1)) + (DWORD) Descr);
char
DllName[100];
int
User32Pos = -1;
for
(UINT x = 0; x < DescrNum; x++)
{
// leggo l'ep
dell'api a cui punta
//
il descriptor del vbox
DWORD
VboxApi;
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
Descr[x].FirstThunk),
&VboxApi,
sizeof (DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
// trovo tale
funzione nella iat risolta
DWORD
Pos = StartOfIAT;
UINT
y;
for
(y = 0; ; y++)
{
if
(IAT[y] == VboxApi)
break;
}
//
adesso devo camminare all'indietro
//
e vedere dove inizia l'array
//
FT per quel descriptor
while
(TRUE)
{
if
(y == 0)
{
break;
}
else
if (IAT[y] == 0)
{
y++;
break;
}
y--;
}
Pos
+= y * sizeof (DWORD);
Descr[x].FirstThunk
=
VaToRva(ImgNtHeaders,
Pos);
Descr[x].OriginalFirstThunk
= 0;
// prendo il nome
della dll
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
Descr[x].Name),
DllName,
sizeof (DllName), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
if
(_strcmpi(DllName, "user32.dll") == 0)
User32Pos
= (int) x;
// lo copio nella
nuova sezione
memcpy(NamePos,
DllName, strlen(DllName) + 1);
Descr[x].Name
=
ImgSectionHeader[S].VirtualAddress
+
((DWORD)
(NamePos - (DWORD) Descr));
NamePos
+= strlen(DllName) + 1;
}
Come vedete nel ciclo mi segno la posizione del descriptor riguardante la user32.dll,
questo perché, come noi tutti sappiamo, il vbox riserva un trattamento speciale
alle Api GetMessageA e PeekMessageA. Ovvero queste due api non vengono risolte,
la IAT contiene per queste due Api due indirizzi che puntano all'interno dell'address
space del modulo del vbox. Quindi prima di tutto mi devo assicurare dove sono
nella IAT queste due Api.
while
(IAT_Ptr < EndOfIAT)
{
if
(IAT[j] == 0)
break;
if
(IAT[j] >= VboxBase &&
IAT[j]
< (VboxBase + VboxSize))
{
if
(A == 0)
{
A
= &IAT[j];
}
else
{
B
= &IAT[j];
}
}
IAT_Ptr
+= sizeof (DWORD);
j++;
}
Poi devo vedere se almeno una delle due è effettivamente importata (che domande
almeno uno per forza). Se le Api sono importate tutte e due controllo quale
è l'offset minore (che corrisponde sempre a GetMessageA) e mi regolo di conseguenza.
Se invece vi è una sola Api importata, non eseguo molti controlli e prendo per
buono che sia GetMessageA. Non avevo assolutamente voglia di accertarmi quale
offset corrispondesse a quale Api inoltre contare su troppi riferimenti è, come
già detto, ugualmente rischioso.
if
(A != 0)
{
HINSTANCE
User32 =
LoadLibrary("User32.dll");
DWORD
AddrOfGetMessageA = (DWORD)
GetProcAddress(User32,
"GetMessageA");
DWORD
AddrOfPeekMessageA = (DWORD)
GetProcAddress(User32,
"PeekMessageA");
// tutte e due
?
if
(A && B)
{
if
(A < B)
{
*A
= AddrOfGetMessageA;
*B
= AddrOfPeekMessageA;
}
else
{
*B
= AddrOfGetMessageA;
*A
= AddrOfPeekMessageA;
}
}
else
// solo uno ?
{
*A
= AddrOfGetMessageA;
}
}
Adesso noi abbiamo una IAT risolta, ma contenente indirizzi spesso di dll rilocate
e siccome ho deciso di far risolvere la IT alla mia wdll (dato che è solo un
lavoro meccanico e non di concetto) devo prima mettere a posto i VA nella IAT
come se nessuna dll fosse rilocata (in modo che la mia wdll becchi tutte le
funzioni senza problemi).
for
(x = 0; x < DescrNum; x++)
{
char
*ModName;
ModName
= (char *) (Descr[x].Name -
ImgSectionHeader[S].VirtualAddress)
+
(DWORD)
Descr;
DWORD
OriginalBase, ActualBase,
Size;
if
(!GetPeFileInfo(ModName, &OriginalBase, &Size))
continue;
if
(!GetModuleInfo(ModName, &ActualBase, &Size))
continue;
Le funzioni GetPeFileInfo e GetModuleInfo mi servono per ricavare l'image base
pensato per tale modulo e quello che invece ha nell'address space del processo
che stiamo unpackando. Ovviamente GetPeFileInfo prendere le informazioni da
file:
BOOL GetPeFileInfo(char
*ModName,
DWORD
*Base, DWORD *Size)
{
HANDLE hMod, hMapObj, hBaseAddress;
char Buffer[MAX_PATH];
IMAGE_DOS_HEADER *ImgDosHeader;
IMAGE_NT_HEADERS *ImgNtHeaders;
// controllo prima nella cartella del prog
hMod = CreateFile(ModName, GENERIC_READ,
FILE_SHARE_READ, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
// altrimenti nella system o nella windows
if (hMod == INVALID_HANDLE_VALUE)
{
GetSystemDirectory(Buffer, MAX_PATH);
wsprintf(Buffer, "%s\\%s", Buffer,
ModName);
hMod = CreateFile(Buffer, GENERIC_READ,
FILE_SHARE_READ, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hMod == INVALID_HANDLE_VALUE)
{
GetWindowsDirectory(Buffer,
MAX_PATH);
wsprintf(Buffer, "%s\\%s",
Buffer, ModName);
hMod = CreateFile(Buffer,
GENERIC_READ,
FILE_SHARE_READ,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
// file non trovato
if (hMod == INVALID_HANDLE_VALUE)
return
FALSE;
}
}
hMapObj = CreateFileMapping(hMod, NULL,
PAGE_READONLY, 0, 0, 0);
hBaseAddress = MapViewOfFile(hMapObj, FILE_MAP_READ,
0, 0, 0);
ImgDosHeader = (IMAGE_DOS_HEADER *) hBaseAddress;
ImgNtHeaders = (IMAGE_NT_HEADERS *)
(ImgDosHeader->e_lfanew + (DWORD) ImgDosHeader);
*Base = ImgNtHeaders->OptionalHeader.ImageBase;
*Size = ImgNtHeaders->OptionalHeader.SizeOfImage;
UnmapViewOfFile(hBaseAddress);
CloseHandle(hMapObj);
CloseHandle(hMod);
return TRUE;
}
Mentre GetModuleInfo fa uso delle psapi:
BOOL GetModuleInfo(char
*ModName,
DWORD
*Base, DWORD *Size)
{
HMODULE *hMods = 0;
DWORD dwSize = 256 * sizeof(DWORD), dwSize2;
UINT Num;
char Buffer[100];
MODULEINFO mdinfo;
BOOL Ret = FALSE;
// psapi
dwSize2 = 256 * sizeof(DWORD);
do
{
if (hMods)
{
HeapFree(GetProcessHeap(),
0, hMods);
dwSize2 *= 2;
}
hMods = (HMODULE *) HeapAlloc(
GetProcessHeap(), 0, dwSize2);
pEnumProcessModules(pi.hProcess, hMods,
dwSize2, &dwSize);
} while (dwSize == dwSize2);
Num = dwSize / sizeof(HMODULE);
for (UINT x = 1; x < Num; x++)
{
if (pGetModuleBaseName(pi.hProcess, hMods[x],
Buffer, sizeof (Buffer)))
{
if (_strcmpi(Buffer, ModName)
== 0)
{
if (pGetModuleInformation(pi.hProcess,
hMods[x],
&mdinfo, sizeof
(MODULEINFO)))
{
Ret
= TRUE;
}
break;
}
}
}
HeapFree(GetProcessHeap(), 0, hMods);
if (Ret == FALSE)
return FALSE;
*Base = (DWORD) mdinfo.lpBaseOfDll;
*Size = mdinfo.SizeOfImage;
return TRUE;
}
Quindi devo controllare ogni Api di ogni descriptor e vederne l'indirizzo, se
l'indirizzo punta all'interno del modulo a cui il descriptor fa riferimento
e se il modulo è stato rilocato allora aggiusta il valore. Invece se il valore
non punta all'interno di quel modulo vuol dire che l'Api è stata forwardata,
se così fosse devo trovare il modulo a cui appartiene la funzione forwardata,
prendere di quel modulo l'original e actual base e vedere se coincidono e se
così non fosse allora devo mettere a posto il valore nella IAT.
j
= (Descr[x].FirstThunk -
VaToRva(ImgNtHeaders,
StartOfIAT)) /
sizeof
(DWORD);
IAT_Ptr
= StartOfIAT;
// metto a posto
gli indirizzi
while
(IAT_Ptr < EndOfIAT)
{
if
(IAT[j] == 0)
break;
// controllo se
la l'entry
//
nella iat punta all'interno
//
del modulo, se no l'api è
//
stata forwardata e quindi devo
//
trovare il modulo a cui è stata passata
//
e calcolare in base a quello
//
ci pensa poi il rebuilder a trovare
//
l'api dalla quale è stata forwardata
if
(IAT[j] >= ActualBase &&
IAT[j]
< (ActualBase + Size))
{
// la dll è stata
rilocata ?
if
(OriginalBase != ActualBase)
{
IAT[j]
-= ActualBase;
IAT[j]
+= OriginalBase;
}
}
else
{
DWORD
fOriginalBase, fActualBase,
fSize;
char
Name[MAX_PATH];
if
(GetModuleBaseFromAddr(
IAT[j],
&fActualBase, Name))
{
GetPeFileInfo(Name,
&fOriginalBase,
&fSize);
if
(fOriginalBase != fActualBase)
{
IAT[j]
-= fActualBase;
IAT[j]
+= fOriginalBase;
}
}
}
IAT_Ptr
+= sizeof (DWORD);
j++;
}
}
Per vedere a quale
modulo appartenesse una Api ho usato la funzione GetModuleBaseFromAddr che altro
non fa che ritornare un modulo (e relativo Image Base) a partire da un address
qualunque:
BOOL GetModuleBaseFromAddr(DWORD
Addr, DWORD *FBase,
char *ModName)
{
HMODULE *hMods = 0;
DWORD dwSize = 256 * sizeof(DWORD), dwSize2;
UINT Num;
MODULEINFO mdinfo;
DWORD Base;
BOOL Ret = FALSE;
// psapi
dwSize2 = 256 * sizeof(DWORD);
do
{
if (hMods)
{
HeapFree(GetProcessHeap(),
0, hMods);
dwSize2 *= 2;
}
hMods = (HMODULE *) HeapAlloc(
GetProcessHeap(), 0, dwSize2);
pEnumProcessModules(pi.hProcess, hMods,
dwSize2, &dwSize);
} while (dwSize == dwSize2);
Num = dwSize / sizeof(HMODULE);
for (UINT x = 0; x < Num; x++)
{
if (pGetModuleInformation(pi.hProcess,
hMods[x], &mdinfo,
sizeof (MODULEINFO)))
{
Base = (DWORD) mdinfo.lpBaseOfDll;
if (Addr >= Base &&
Addr
< (Base + mdinfo.SizeOfImage))
{
pGetModuleBaseName(pi.hProcess,
hMods[x],
ModName,
MAX_PATH);
Ret
= TRUE;
break;
}
}
}
HeapFree(GetProcessHeap(), 0, hMods);
if (Ret == FALSE)
return FALSE;
*FBase = Base;
return TRUE;
}
Ma ritorniamo a noi. Adesso che abbiamo una IAT davvero perfetta e pronta per
la ricostruzione la dumpo su file e la libero dalla memoria:
// dumpo su file
la IAT
SetFilePointer(hDumpFile,
VaToOffset(ImgNtHeaders,
StartOfIAT),
NULL,
FILE_BEGIN);
WriteFile(hDumpFile,
IAT,
(EndOfIAT
- StartOfIAT), &BW, NULL);
delete
IAT;
Setto alcune variabili riguardanti la mia nuova sezione:
// vsize sezione
DWORD
VSize =
((DWORD)
(NamePos - (DWORD) Descr)) + 1;
DWORD
VAddr = ImgSectionHeader[S].
VirtualAddress;
Ora controllo se il PE ha una ET e se ce l'ha me la porto nella mia sezione,
si perché il vbox ovviamente non distrugge la Export Table però se la porta
nella sua sezione, quindi io siccome non voglio assolutamente lasciare la sezione
del vbox me la porto nella mia sezione.
//
adesso devo controllare se il prog ha
//
una ET, che, in caso affermativo
//
si trova nella sezione del vbox
//
quindi la redirigo nella mia sezione
DWORD
ET_Addr, ET_Size;
ET_Size
= ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
Size;
if
((ET_Addr = ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress)
&& ET_Size)
{
IMAGE_EXPORT_DIRECTORY
*ImgETDir;
// alloco la memoria per la ET
BYTE
*ET = new BYTE [ET_Size];
// leggo la ET dal processo
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
ET_Addr), ET,
ET_Size,
&BR))
{
ImgETDir
= (IMAGE_EXPORT_DIRECTORY *) ET;
// metto a posto tutti gli rva
DWORD
*Names = (DWORD *)
((ImgETDir->AddressOfNames
-
ET_Addr)
+ (DWORD) ET);
ImgETDir->Name
= (ImgETDir->Name - ET_Addr) +
VSize
+ VAddr;
ImgETDir->AddressOfFunctions
=
(ImgETDir->AddressOfFunctions
-
ET_Addr)
+ VSize + VAddr;
ImgETDir->AddressOfNames
=
(ImgETDir->AddressOfNames
- ET_Addr) +
VSize
+ VAddr;
ImgETDir->AddressOfNameOrdinals
=
(ImgETDir->AddressOfNameOrdinals
-
ET_Addr)
+ VSize + VAddr;
for
(DWORD x = 0; x < ImgETDir->
NumberOfNames;
x++)
{
Names[x]
= (Names[x] -
ET_Addr)
+ VSize + VAddr;
}
// rialloco memoria per aggiungere
//
la ET nella nuova sezione
BYTE
*Buf = new BYTE [
VSize
+ ET_Size];
memcpy(Buf,
NewSect, VSize);
delete
NewSect;
memcpy(&Buf[VSize],
ET, ET_Size);
NewSect
= Buf;
// faccio puntare la Data Dir
ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress
= VSize + VAddr;
// nuova size
della sezione
VSize
+= ET_Size;
}
delete
ET;
}
Sono stato un po' imprudente dato che non controllo nemmeno se la ET sta effettivamente
nella sezione del vbox e probabilmente altre versioni del vbox non spostano
la ET.. Vabbè chi se ne frega, ci sarà tempo per le migliorie.
Fatto anche il controllo della ET è giunto il momento degli ultimi aggiustamenti,
faccio gli ultimi calcoli per la mia sezione:
ImgSectionHeader[S].Misc.VirtualSize
= VSize;
ImgSectionHeader[S].PointerToRawData
=
ImgSectionHeader[S].VirtualAddress;
ImgSectionHeader[S].SizeOfRawData
=
ImgSectionHeader[S].Misc.VirtualSize;
ImgSectionHeader[S].Characteristics
=
IMAGE_SCN_MEM_READ;
Faccio puntare la IT del PE alla mia sezione:
ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress
=
ImgSectionHeader[S].VirtualAddress;
Metto a posto l'image size del PE:
ImgNtHeaders->OptionalHeader.SizeOfImage
=
ImgSectionHeader[S].VirtualAddress
+
CalcAlignment(
ImgNtHeaders->OptionalHeader.SectionAlignment,
ImgSectionHeader[S].Misc.VirtualSize);
Setto l'EP:
ImgNtHeaders->OptionalHeader.
AddressOfEntryPoint
= OEP;
Dumpo gli headers, la mia sezione e chiudo il file:
SetFilePointer(hDumpFile,
0,
NULL,
FILE_BEGIN);
WriteFile(hDumpFile,
ImgDosHeader,
ImgSectionHeader[0].VirtualAddress,
&BW, NULL);
SetFilePointer(hDumpFile,
0,
NULL,
FILE_END);
WriteFile(hDumpFile,
NewSect, VSize, &BW, NULL);
SetFilePointer(hDumpFile,
(CalcAlignment(
ImgNtHeaders->OptionalHeader.SectionAlignment,
ImgSectionHeader[S].Misc.VirtualSize)
-
VSize),
NULL, FILE_CURRENT);
SetEndOfFile(hDumpFile);
// chiudo il Dump
file
CloseHandle(hDumpFile);
A Questo punto esco da ciclo di debug, chiudo tutti gli handle aperti (proccesso
compreso) e chiedo all'utente se vuole rebuildare la IT (con la mia wdll).
printf("\nProg unpacked, rebuild IT? [Y/N]: ",
DumpName);
int C;
C = getch();
if (C != 'y' && C != 'Y')
return;
// ripristino la IT
HINSTANCE hWdll = LoadLibrary("wdll.dll");
if (!hWdll)
{
printf("\nCannot load wdll.dll\n");
return;
}
BOOL (__cdecl *RebuildIT)(char *, DWORD, BOOL);
RebuildIT = (BOOL (__cdecl *)(char *, DWORD, BOOL))
GetProcAddress(hWdll, "RebuildImportTable");
BOOL bRet = RebuildIT(DumpName, 0, FALSE);
Se l'utente sceglie di sì allora troveremo una IT completamente ricostruita
nel file dumpato. L'unica cosa che ci resta da fare è sostituire il file dumpato a quello originale
dato che alcuni programmi (come imageready) non funzionano se non rinominati.
In ogni caso sia ImageReady che Photoshop sono unpackati e fungono ad una velcità
centuplicata una volta rimosso lo schifosissimo vbox.
Adesso eccovi proposto in versione completa il codice (che io ritengo sempre
meglio di qualsiasi spiegazione):
-- unvbox.cpp
----------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <conio.h>
// definizioni
#define PAGE_SIZE 0x1000
#define TRAP_FLAG 0x100
typedef struct _MODULEINFO
{
LPVOID lpBaseOfDll;
DWORD SizeOfImage;
LPVOID EntryPoint;
} MODULEINFO, *LPMODULEINFO;
// variabili globali
PROCESS_INFORMATION pi;
BYTE *FileBuf = NULL;
HANDLE hDumpFile = INVALID_HANDLE_VALUE;
DWORD RetAddress = 0;
BYTE BP = 0xCC, BP_Buf;
HINSTANCE hPsapi = 0;
// funzioni psapi
BOOL (WINAPI *pEnumProcessModules)(HANDLE, HMODULE *,
DWORD,
LPDWORD);
DWORD (WINAPI *pGetModuleBaseName)(HANDLE,
HMODULE,
LPTSTR,
DWORD);
BOOL (WINAPI *pGetModuleInformation)(HANDLE,
HMODULE,
LPMODULEINFO,
DWORD);
// converte rva in file offset
DWORD RvaToOffset(IMAGE_NT_HEADERS *NT, DWORD Rva)
{
DWORD Offset = Rva, Limit;
IMAGE_SECTION_HEADER *Img;
WORD i;
Img = IMAGE_FIRST_SECTION(NT);
if (Rva < Img->PointerToRawData)
return Rva;
for (i = 0; i < NT->FileHeader.NumberOfSections; i++)
{
if (Img[i].SizeOfRawData)
Limit = Img[i].SizeOfRawData;
else
Limit = Img[i].Misc.VirtualSize;
if (Rva >= Img[i].VirtualAddress &&
Rva < (Img[i].VirtualAddress + Limit))
{
if (Img[i].PointerToRawData
!= 0)
{
Offset
-= Img[i].VirtualAddress;
Offset
+= Img[i].PointerToRawData;
}
return Offset;
}
}
return NULL;
}
// per passare da VA a Offset
DWORD VaToOffset(IMAGE_NT_HEADERS *NT, DWORD Va)
{
return RvaToOffset(NT,
(Va - NT->OptionalHeader.ImageBase));
}
// per passare da RVA a VA
DWORD RvaToVa(IMAGE_NT_HEADERS *NT, DWORD Rva)
{
return (Rva + NT->OptionalHeader.ImageBase);
}
// per passare da VA a RVA
DWORD VaToRva(IMAGE_NT_HEADERS *NT, DWORD Va)
{
return (Va - NT->OptionalHeader.ImageBase);
}
DWORD NextApiToSolve(DWORD IAT_Ptr, DWORD EndOfIAT)
{
DWORD Api, BR;
while (IAT_Ptr < EndOfIAT)
{
if (ReadProcessMemory(pi.hProcess, (LPCVOID)
IAT_Ptr, &Api, sizeof
(DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
return
FALSE;
}
// controllo se l'api è da risolvere
// non basta vedere se l'indirizzo è
// contenuto nello space address del
// rispettivo modulo: pensate
// all'export forwarding
if (Api != 0)
{
BYTE IC;
if (ReadProcessMemory(pi.hProcess,
(LPCVOID)
Api,
&IC, sizeof (BYTE), &BR))
{
if (IC
== 0xE8)
{
DWORD
Check = 0;
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
(Api
+ 1), &Check, sizeof (DWORD), &BR))
{
// controllo se
è una chiamata vbox
Check
= Check + Api + 5;
// deve essere
vicino al RetAddress
if
(Check >= (RetAddress - 100) &&
Check
< RetAddress)
break;
}
}
}
}
IAT_Ptr += sizeof (DWORD);
}
return IAT_Ptr;
}
// calcola l'allineamento
DWORD CalcAlignment(DWORD Alignment, DWORD TrueSize)
{
DWORD CalculatedAlignment;
for(CalculatedAlignment = Alignment; ; CalculatedAlignment
+= Alignment)
{
if (TrueSize <= CalculatedAlignment) break;
}
return CalculatedAlignment;
}
// routine di chiusura
void CloseAll(void)
{
if (FileBuf != NULL)
delete FileBuf;
if (hDumpFile != INVALID_HANDLE_VALUE)
CloseHandle(hDumpFile);
if (pi.hProcess != NULL)
CloseHandle(pi.hProcess);
if (pi.hThread != NULL)
CloseHandle(pi.hThread);
if (hPsapi != NULL)
FreeLibrary(hPsapi);
}
BOOL GetModuleInfo(char *ModName,
DWORD
*Base, DWORD *Size)
{
HMODULE *hMods = 0;
DWORD dwSize = 256 * sizeof(DWORD), dwSize2;
UINT Num;
char Buffer[100];
MODULEINFO mdinfo;
BOOL Ret = FALSE;
// psapi
dwSize2 = 256 * sizeof(DWORD);
do
{
if (hMods)
{
HeapFree(GetProcessHeap(),
0, hMods);
dwSize2 *= 2;
}
hMods = (HMODULE *) HeapAlloc(
GetProcessHeap(), 0, dwSize2);
pEnumProcessModules(pi.hProcess, hMods,
dwSize2, &dwSize);
} while (dwSize == dwSize2);
Num = dwSize / sizeof(HMODULE);
for (UINT x = 1; x < Num; x++)
{
if (pGetModuleBaseName(pi.hProcess, hMods[x],
Buffer, sizeof (Buffer)))
{
if (_strcmpi(Buffer, ModName)
== 0)
{
if (pGetModuleInformation(pi.hProcess,
hMods[x],
&mdinfo, sizeof
(MODULEINFO)))
{
Ret
= TRUE;
}
break;
}
}
}
HeapFree(GetProcessHeap(), 0, hMods);
if (Ret == FALSE)
return FALSE;
*Base = (DWORD) mdinfo.lpBaseOfDll;
*Size = mdinfo.SizeOfImage;
return TRUE;
}
BOOL GetModuleBaseFromAddr(DWORD Addr, DWORD *FBase,
char *ModName)
{
HMODULE *hMods = 0;
DWORD dwSize = 256 * sizeof(DWORD), dwSize2;
UINT Num;
MODULEINFO mdinfo;
DWORD Base;
BOOL Ret = FALSE;
// psapi
dwSize2 = 256 * sizeof(DWORD);
do
{
if (hMods)
{
HeapFree(GetProcessHeap(),
0, hMods);
dwSize2 *= 2;
}
hMods = (HMODULE *) HeapAlloc(
GetProcessHeap(), 0, dwSize2);
pEnumProcessModules(pi.hProcess, hMods,
dwSize2, &dwSize);
} while (dwSize == dwSize2);
Num = dwSize / sizeof(HMODULE);
for (UINT x = 0; x < Num; x++)
{
if (pGetModuleInformation(pi.hProcess,
hMods[x], &mdinfo,
sizeof (MODULEINFO)))
{
Base = (DWORD) mdinfo.lpBaseOfDll;
if (Addr >= Base &&
Addr
< (Base + mdinfo.SizeOfImage))
{
pGetModuleBaseName(pi.hProcess,
hMods[x],
ModName,
MAX_PATH);
Ret
= TRUE;
break;
}
}
}
HeapFree(GetProcessHeap(), 0, hMods);
if (Ret == FALSE)
return FALSE;
*FBase = Base;
return TRUE;
}
BOOL GetPeFileInfo(char *ModName,
DWORD
*Base, DWORD *Size)
{
HANDLE hMod, hMapObj, hBaseAddress;
char Buffer[MAX_PATH];
IMAGE_DOS_HEADER *ImgDosHeader;
IMAGE_NT_HEADERS *ImgNtHeaders;
// controllo prima nella cartella del prog
hMod = CreateFile(ModName, GENERIC_READ,
FILE_SHARE_READ, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
// altrimenti nella system o nella windows
if (hMod == INVALID_HANDLE_VALUE)
{
GetSystemDirectory(Buffer, MAX_PATH);
wsprintf(Buffer, "%s\\%s", Buffer,
ModName);
hMod = CreateFile(Buffer, GENERIC_READ,
FILE_SHARE_READ, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hMod == INVALID_HANDLE_VALUE)
{
GetWindowsDirectory(Buffer,
MAX_PATH);
wsprintf(Buffer, "%s\\%s",
Buffer, ModName);
hMod = CreateFile(Buffer,
GENERIC_READ,
FILE_SHARE_READ,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
// file non trovato
if (hMod == INVALID_HANDLE_VALUE)
return
FALSE;
}
}
hMapObj = CreateFileMapping(hMod, NULL,
PAGE_READONLY, 0, 0, 0);
hBaseAddress = MapViewOfFile(hMapObj, FILE_MAP_READ,
0, 0, 0);
ImgDosHeader = (IMAGE_DOS_HEADER *) hBaseAddress;
ImgNtHeaders = (IMAGE_NT_HEADERS *)
(ImgDosHeader->e_lfanew + (DWORD) ImgDosHeader);
*Base = ImgNtHeaders->OptionalHeader.ImageBase;
*Size = ImgNtHeaders->OptionalHeader.SizeOfImage;
UnmapViewOfFile(hBaseAddress);
CloseHandle(hMapObj);
CloseHandle(hMod);
return TRUE;
}
// start
void main(void)
{
// sistema nt?
OSVERSIONINFO osver;
osver.dwOSVersionInfoSize = sizeof(osver);
if (!GetVersionEx(&osver))
return;
if (osver.dwMajorVersion < 4)
{
printf("Need NT...\n");
return;
}
hPsapi = LoadLibrary("psapi.dll");
if (!hPsapi)
{
printf("Cannot load psapi.dll\n");
return;
}
pEnumProcessModules = (BOOL (WINAPI *)(HANDLE,
HMODULE *, DWORD, LPDWORD))
GetProcAddress(hPsapi, "EnumProcessModules");
pGetModuleBaseName = (DWORD (WINAPI *)(HANDLE,
HMODULE, LPTSTR, DWORD))
GetProcAddress(hPsapi, "GetModuleBaseNameA");
pGetModuleInformation = (BOOL (WINAPI *)(HANDLE,
HMODULE, LPMODULEINFO, DWORD))
GetProcAddress(hPsapi, "GetModuleInformation");
if (pEnumProcessModules == NULL ||
pGetModuleBaseName == NULL)
{
printf("Cannot load psapi functions\n");
FreeLibrary(hPsapi);
return;
}
printf("\nInsert the executable's name:\n");
// nome exe
char ProgName[MAX_PATH];
scanf("%s", ProgName);
// prendo le informazioni che mi servono dal PE
HANDLE hFile;
hFile = CreateFile(ProgName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Cannot open %s\n", ProgName);
FreeLibrary(hPsapi);
return;
}
// bah direi che basta per prendere le info sul PE
// btw faccio conto che l'nt header stia attaccato
// al dos header
DWORD BufSize = 0x10000;
FileBuf = new BYTE [BufSize];
if (!FileBuf)
{
printf("Not enough memory\n");
FreeLibrary(hPsapi);
return;
}
ZeroMemory(FileBuf, BufSize);
DWORD BR, BW;
if (!ReadFile(hFile, FileBuf, BufSize, &BR, NULL))
{
printf("Cannot read from %s\n",
ProgName);
FreeLibrary(hPsapi);
delete FileBuf;
return;
}
CloseHandle(hFile);
// roba PE
IMAGE_DOS_HEADER *ImgDosHeader;
IMAGE_NT_HEADERS *ImgNtHeaders;
IMAGE_SECTION_HEADER *ImgSectionHeader;
// potrebbe crashare tutto, non si sa mai
__try
{
ImgDosHeader = (IMAGE_DOS_HEADER *)(DWORD)
FileBuf;
ImgNtHeaders = (IMAGE_NT_HEADERS *)(DWORD)
&FileBuf[ImgDosHeader->e_lfanew];
if (ImgDosHeader->e_magic !=
IMAGE_DOS_SIGNATURE ||
ImgNtHeaders->Signature
!= IMAGE_NT_SIGNATURE)
{
printf("%s is not
a valid PE file\n", ProgName);
CloseAll();
return;
}
ImgSectionHeader = IMAGE_FIRST_SECTION(ImgNtHeaders);
// faccio i controlli relativi
all'unpacking
if (ImgNtHeaders->FileHeader.NumberOfSections
< 2)
{
printf("Cannot unpack:
merged sections?\n");
CloseAll();
return;
}
// è packato con vbox, controllo
leim
if (strncmp((char *) &ImgSectionHeader[
ImgNtHeaders->FileHeader.NumberOfSections
- 1].Name,
"PREVIEW", sizeof
("PREVIEW") - 1) != 0)
{
printf("Cannot unpack:
seems not to be packed with vbox\n");
CloseAll();
return;
}
}
__except (TRUE)
{
printf("An error occurred\n");
CloseAll();
return;
}
// da qui in poi prendo per buono
// che il PE sia valido, non è molto
// cauto ma chi se ne sbatte
// tanto mica vi crasha il sistema
STARTUPINFO si;
GetStartupInfo(&si);
// creo il processo
if (CreateProcess(ProgName, NULL, NULL, NULL, FALSE,
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,
NULL, NULL, &si, π) == FALSE)
{
printf("Cannot start process\n");
CloseAll();
return;
}
// inizio il debug
DEBUG_EVENT DbgEvent;
CONTEXT Context;
DWORD OldProtect;
DWORD StartAddr, Size;
DWORD OEP = 0; // original entry
point
BOOL SearchGetProcAddress
= FALSE;
DWORD AddrOfGetProc;
BOOL bOk = FALSE;
HINSTANCE hKernel32;
char DumpName[MAX_PATH];
BYTE *NewSect;
while (TRUE)
{
if (!WaitForDebugEvent(&DbgEvent, INFINITE))
break;
// prendo il Context
Context.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &Context);
if (DbgEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
{
StartAddr = RvaToVa(ImgNtHeaders,
ImgSectionHeader->VirtualAddress);
Size = ImgSectionHeader[1].VirtualAddress
-
ImgSectionHeader->VirtualAddress;
// cambio i diritti
per la sezione code
// ovvero interdico l'accesso
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size,
PAGE_NOACCESS, &OldProtect);
}
else if (DbgEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_BREAKPOINT)
{
if (SearchGetProcAddress
== TRUE)
{
// siamo giunti al fatidico GetProcAddress
//
tolgo il break point
Context.Eip--;
WriteProcessMemory(pi.hProcess,
(LPVOID)
AddrOfGetProc,
&BP_Buf, sizeof (BYTE), &BW);
// nel caso ci fossero altri bp non voluti da me
SearchGetProcAddress
= FALSE;
// devo risalire a alle cose che mi interessano
//
attivo il single step
Context.EFlags
|= TRAP_FLAG;
SetThreadContext(pi.hThread,
&Context);
// attivo la var per passare al secondo
//
ciclo di debug
bOk
= TRUE;
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
break;
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
else if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_ACCESS_VIOLATION)
{
// verifico se l'eccezione è causata da me
if (OEP
== 0)
{
// rimetto a posto eip
Context.Eip
= (UINT) DbgEvent.u.Exception.
ExceptionRecord.ExceptionAddress;
// rendo leggibile la locazione
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size,
PAGE_READWRITE, &OldProtect);
// controllo se siamo arrivati all'oep
if
(Context.Eip >= StartAddr &&
Context.Eip
< (StartAddr + Size))
{
OEP
= VaToRva(ImgNtHeaders, Context.Eip);
printf("OEP:
%X\n", OEP);
// ok, adesso che sono giunto all'ep
//
mi salvo le sezioni che adesso
//
sono in chiaro, l'unica cosa da mettere
//
a posto dopo resta la IAT
//
prima di tutto però mi creo un File
//
che sarà il dump, chiedo il nome all'utente
printf("Insert
name of the dump file:\n");
scanf("%s",
DumpName);
hDumpFile
= CreateFile(DumpName, GENERIC_WRITE,
NULL,
NULL, CREATE_ALWAYS, NULL, NULL);
if
(hDumpFile == INVALID_HANDLE_VALUE)
{
printf("Cannot
create file\n");
break;
}
// metto a posto alcuni campi per il dump
ImgNtHeaders->OptionalHeader.FileAlignment
=
ImgNtHeaders->OptionalHeader.SectionAlignment;
ImgNtHeaders->OptionalHeader.SizeOfHeaders
=
ImgSectionHeader->VirtualAddress;
// lascio lo spazio per gli headers
//
che dumperò alla fine
SetFilePointer(hDumpFile,
ImgSectionHeader->VirtualAddress,
NULL,
FILE_BEGIN);
SetEndOfFile(hDumpFile);
WORD
y = ImgNtHeaders->FileHeader.NumberOfSections - 1;
BYTE
*Section;
// dumpo tutte le sezioni
//
tranne quella del vbox
for
(WORD x = 0; x < y; x++)
{
StartAddr
= RvaToVa(ImgNtHeaders,
ImgSectionHeader[x].VirtualAddress);
Size
= ImgSectionHeader[x + 1].VirtualAddress -
ImgSectionHeader[x].VirtualAddress;
Section
= new BYTE [Size];
if
(Section == NULL)
{
printf("Not
enough memory\n");
break;
}
if
(ReadProcessMemory(pi.hProcess, (LPVOID) StartAddr,
Section,
Size, &BR) == FALSE)
{
printf("An
error occurred\n");
delete
Section;
break;
}
if
(!WriteFile(hDumpFile, Section, Size, &BW, NULL))
{
printf("An
error occurred\n");
delete
Section;
break;
}
delete
Section;
ImgSectionHeader[x].PointerToRawData
=
ImgSectionHeader[x].VirtualAddress;
ImgSectionHeader[x].SizeOfRawData
= Size;
}
SetFilePointer(hDumpFile,
0, NULL, FILE_BEGIN);
WriteFile(hDumpFile,
FileBuf,
ImgSectionHeader->VirtualAddress,
&BW, NULL);
// ricavo l'address di GetProcAddr
hKernel32
= GetModuleHandle("kernel32.dll");
AddrOfGetProc
= (DWORD) GetProcAddress(hKernel32,
"GetProcAddress");
// salva il byte originale
ReadProcessMemory(pi.hProcess,
(LPCVOID)
AddrOfGetProc,
&BP_Buf, sizeof (BYTE), &BR);
// setto il break point a GetProcAddr
WriteProcessMemory(pi.hProcess,
(LPVOID)
AddrOfGetProc,
&BP, sizeof (BYTE), &BW);
// setto true il valore che conferma
//
che il prossimo bp è generato da me
SearchGetProcAddress
= TRUE;
}
else
{
// setto il trap flag per il single step
//
dato che devo reinvalidare la memoria
Context.EFlags
|= TRAP_FLAG;
SetThreadContext(pi.hThread,
&Context);
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
}
else if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_SINGLE_STEP)
{
if (OEP
== 0)
{
// rendo di nuovo non accessibile la memoria
VirtualProtectEx(pi.hProcess,
(PVOID) StartAddr,
Size,
PAGE_NOACCESS, &OldProtect);
}
else
{
// devo trovare la call che ripristina le api
Context.EFlags
|= TRAP_FLAG;
SetThreadContext(pi.hThread,
&Context);
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
}
else if (DbgEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
break;
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
if (bOk != TRUE)
{
CloseAll();
return;
}
// prendo image base e size del modulo
// del vbox
DWORD VboxBase, VboxSize;
if (GetModuleInfo("vboxtb.dll", &VboxBase,
&VboxSize) == FALSE)
{
printf("Cannot retrive vboxtb info\n");
CloseAll();
return;
}
// + che bastanti
NewSect = new BYTE [
sizeof (IMAGE_IMPORT_DESCRIPTOR) * 100];
// trovo i vecchi descriptors
IMAGE_IMPORT_DESCRIPTOR *Descr =
(IMAGE_IMPORT_DESCRIPTOR *)
NewSect;
if (ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders, ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress), Descr, sizeof
(IMAGE_IMPORT_DESCRIPTOR) * 100, &BR)
== FALSE)
{
printf("An error occurred\n");
return;
}
UINT DescrNum = 0;
// conto il numero dei descriptors
while (TRUE)
{
if (Descr[DescrNum].FirstThunk)
DescrNum++;
else
break;
}
DWORD VA_OEP =
RvaToVa(ImgNtHeaders, OEP);
DWORD StartOfIAT, EndOfIAT, IAT_Ptr = 0;
DWORD RetCount = 0;
BOOL bInit = FALSE;
bOk = FALSE;
while (TRUE)
{
if (!WaitForDebugEvent(&DbgEvent, INFINITE))
break;
// prendo il Context
Context.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &Context);
if (DbgEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_BREAKPOINT)
{
// rimetto a posto dopo l'eccezione
if (WriteProcessMemory(pi.hProcess,
(LPVOID)
RetAddress,
&BP_Buf, sizeof (BYTE), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
Context.Eip--;
// per ripristinare in seguito il bp
Context.EFlags
|= TRAP_FLAG;
// ho cominciato il ciclo di ricostruzione?
if (bInit
== FALSE)
{
bInit
= TRUE;
// prima api da risolvere
IAT_Ptr
= NextApiToSolve(IAT_Ptr,
EndOfIAT);
if
(!IAT_Ptr)
break;
Context.Eax
= IAT_Ptr;
// setto lo stack pointer
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
Context.Esp,
&VA_OEP, sizeof (DWORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
SetThreadContext(pi.hThread,
&Context);
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
// leggo l'api risolta
DWORD
Entry;
if (ReadProcessMemory(pi.hProcess,
(LPCVOID)
Context.Esp,
&Entry, sizeof (DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
// metto l'entry risolta nella iat
if (WriteProcessMemory(pi.hProcess,
(LPVOID)
IAT_Ptr,
&Entry, sizeof (DWORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
// prossima api da risolvere
IAT_Ptr
= NextApiToSolve(IAT_Ptr,
EndOfIAT);
if (!IAT_Ptr)
break;
Context.Eax
= IAT_Ptr;
// setto lo stack pointer
if (WriteProcessMemory(pi.hProcess,
(LPVOID)
Context.Esp,
&VA_OEP, sizeof (DWORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
if (IAT_Ptr
>= EndOfIAT)
{
// sono giunto alla fine della IAT
//
mi leggo la IAT risolta
DWORD
*IAT;
IAT
= new DWORD [(EndOfIAT - StartOfIAT) /
sizeof
(DWORD)];
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
StartOfIAT,
IAT, (EndOfIAT - StartOfIAT),
&BR)
== FALSE)
{
printf("An
error occurred\n");
break;
}
// elimino la sezione del vbox
//
e metto una mia per i descriptors
//
e per l'eventuale ET
WORD
S = ImgNtHeaders->FileHeader.NumberOfSections - 1;
ZeroMemory(&ImgSectionHeader[S],
IMAGE_SIZEOF_SECTION_HEADER);
memcpy(ImgSectionHeader[S].Name,
"Ntoskrnl",
strlen("Ntoskrnl"));
ImgSectionHeader[S].VirtualAddress
=
ImgSectionHeader[S
- 1].VirtualAddress +
CalcAlignment(
ImgNtHeaders->OptionalHeader.SectionAlignment,
ImgSectionHeader[S
- 1].Misc.VirtualSize);
// collega i descriptors alla iat
BYTE
*NamePos = (BYTE *)
((sizeof
(IMAGE_IMPORT_DESCRIPTOR) *
(DescrNum
+ 1)) + (DWORD) Descr);
char
DllName[100];
int
User32Pos = -1;
for
(UINT x = 0; x < DescrNum; x++)
{
// leggo l'ep dell'api a cui punta
//
il descriptor del vbox
DWORD
VboxApi;
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
Descr[x].FirstThunk),
&VboxApi,
sizeof (DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
// trovo tale funzione nella iat risolta
DWORD
Pos = StartOfIAT;
UINT
y;
for
(y = 0; ; y++)
{
if
(IAT[y] == VboxApi)
break;
}
// adesso devo camminare all'indietro
//
e vedere dove inizia l'array
//
FT per quel descriptor
while
(TRUE)
{
if
(y == 0)
{
break;
}
else
if (IAT[y] == 0)
{
y++;
break;
}
y--;
}
Pos
+= y * sizeof (DWORD);
Descr[x].FirstThunk
=
VaToRva(ImgNtHeaders,
Pos);
Descr[x].OriginalFirstThunk
= 0;
// prendo il nome della dll
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
Descr[x].Name),
DllName,
sizeof (DllName), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
if
(_strcmpi(DllName, "user32.dll") == 0)
User32Pos
= (int) x;
// lo copio nella nuova sezione
memcpy(NamePos,
DllName, strlen(DllName) + 1);
Descr[x].Name
=
ImgSectionHeader[S].VirtualAddress
+
((DWORD)
(NamePos - (DWORD) Descr));
NamePos
+= strlen(DllName) + 1;
}
// vado alla ricerca di GetMessageA
//
e PeekMessageA
DWORD
*A = 0, *B = 0, j;
IAT_Ptr
= StartOfIAT;
j
= (Descr[User32Pos].FirstThunk -
VaToRva(ImgNtHeaders,
StartOfIAT)) /
sizeof
(DWORD);
while
(IAT_Ptr < EndOfIAT)
{
if
(IAT[j] == 0)
break;
if
(IAT[j] >= VboxBase &&
IAT[j]
< (VboxBase + VboxSize))
{
if
(A == 0)
{
A
= &IAT[j];
}
else
{
B
= &IAT[j];
}
}
IAT_Ptr
+= sizeof (DWORD);
j++;
}
// almeno una delle due api
//
è importata dal prog?
if
(A != 0)
{
HINSTANCE
User32 =
LoadLibrary("User32.dll");
DWORD
AddrOfGetMessageA = (DWORD)
GetProcAddress(User32,
"GetMessageA");
DWORD
AddrOfPeekMessageA = (DWORD)
GetProcAddress(User32,
"PeekMessageA");
// tutte e due ?
if
(A && B)
{
if
(A < B)
{
*A
= AddrOfGetMessageA;
*B
= AddrOfPeekMessageA;
}
else
{
*B
= AddrOfGetMessageA;
*A
= AddrOfPeekMessageA;
}
}
else
// solo uno ?
{
*A
= AddrOfGetMessageA;
}
}
// adesso metto
a posto gli offset
//
delle dll che sono state rilocate
for
(x = 0; x < DescrNum; x++)
{
char
*ModName;
ModName
= (char *) (Descr[x].Name -
ImgSectionHeader[S].VirtualAddress)
+
(DWORD)
Descr;
DWORD
OriginalBase, ActualBase,
Size;
if
(!GetPeFileInfo(ModName, &OriginalBase, &Size))
continue;
if
(!GetModuleInfo(ModName, &ActualBase, &Size))
continue;
j
= (Descr[x].FirstThunk -
VaToRva(ImgNtHeaders,
StartOfIAT)) /
sizeof
(DWORD);
IAT_Ptr
= StartOfIAT;
// metto a posto
gli indirizzi
while
(IAT_Ptr < EndOfIAT)
{
if
(IAT[j] == 0)
break;
// controllo se
la l'entry
//
nella iat punta all'interno
//
del modulo, se no l'api è
//
stata forwardata e quindi devo
//
trovare il modulo a cui è stata passata
//
e calcolare in base a quello
//
ci pensa poi il rebuilder a trovare
//
l'api dalla quale è stata forwardata
if
(IAT[j] >= ActualBase &&
IAT[j]
< (ActualBase + Size))
{
// la dll è stata
rilocata ?
if
(OriginalBase != ActualBase)
{
IAT[j]
-= ActualBase;
IAT[j]
+= OriginalBase;
}
}
else
{
DWORD
fOriginalBase, fActualBase,
fSize;
char
Name[MAX_PATH];
if
(GetModuleBaseFromAddr(
IAT[j],
&fActualBase, Name))
{
GetPeFileInfo(Name,
&fOriginalBase,
&fSize);
if
(fOriginalBase != fActualBase)
{
IAT[j]
-= fActualBase;
IAT[j]
+= fOriginalBase;
}
}
}
IAT_Ptr
+= sizeof (DWORD);
j++;
}
}
// dumpo su file
la IAT
SetFilePointer(hDumpFile,
VaToOffset(ImgNtHeaders,
StartOfIAT),
NULL,
FILE_BEGIN);
WriteFile(hDumpFile,
IAT,
(EndOfIAT
- StartOfIAT), &BW, NULL);
delete
IAT;
// vsize sezione
DWORD
VSize =
((DWORD)
(NamePos - (DWORD) Descr)) + 1;
DWORD
VAddr = ImgSectionHeader[S].
VirtualAddress;
// adesso devo
controllare se il prog ha
//
una ET, che, in caso affermativo
//
si trova nella sezione del vbox
//
quindi la redirigo nella mia sezione
DWORD
ET_Addr, ET_Size;
ET_Size
= ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
Size;
if
((ET_Addr = ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress)
&& ET_Size)
{
IMAGE_EXPORT_DIRECTORY
*ImgETDir;
// alloco la memoria per la ET
BYTE
*ET = new BYTE [ET_Size];
// leggo la ET dal processo
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
ET_Addr), ET,
ET_Size,
&BR))
{
ImgETDir
= (IMAGE_EXPORT_DIRECTORY *) ET;
// metto a posto tutti gli rva
DWORD
*Names = (DWORD *)
((ImgETDir->AddressOfNames
-
ET_Addr)
+ (DWORD) ET);
ImgETDir->Name
= (ImgETDir->Name - ET_Addr) +
VSize
+ VAddr;
ImgETDir->AddressOfFunctions
=
(ImgETDir->AddressOfFunctions
-
ET_Addr)
+ VSize + VAddr;
ImgETDir->AddressOfNames
=
(ImgETDir->AddressOfNames
- ET_Addr) +
VSize
+ VAddr;
ImgETDir->AddressOfNameOrdinals
=
(ImgETDir->AddressOfNameOrdinals
-
ET_Addr)
+ VSize + VAddr;
for
(DWORD x = 0; x < ImgETDir->
NumberOfNames;
x++)
{
Names[x]
= (Names[x] -
ET_Addr)
+ VSize + VAddr;
}
// rialloco memoria per aggiungere
//
la ET nella nuova sezione
BYTE
*Buf = new BYTE [
VSize
+ ET_Size];
memcpy(Buf,
NewSect, VSize);
delete
NewSect;
memcpy(&Buf[VSize],
ET, ET_Size);
NewSect
= Buf;
// faccio puntare la Data Dir
ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress
= VSize + VAddr;
// nuova size della sezione
VSize
+= ET_Size;
}
delete
ET;
}
// ultimi calcoli per la nuova sezione
ImgSectionHeader[S].Misc.VirtualSize
= VSize;
ImgSectionHeader[S].PointerToRawData
=
ImgSectionHeader[S].VirtualAddress;
ImgSectionHeader[S].SizeOfRawData
=
ImgSectionHeader[S].Misc.VirtualSize;
ImgSectionHeader[S].Characteristics
=
IMAGE_SCN_MEM_READ;
// faccio puntare la IT alla nuova sezione
ImgNtHeaders->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress
=
ImgSectionHeader[S].VirtualAddress;
// metto a posto l'image size
ImgNtHeaders->OptionalHeader.SizeOfImage
=
ImgSectionHeader[S].VirtualAddress
+
CalcAlignment(
ImgNtHeaders->OptionalHeader.SectionAlignment,
ImgSectionHeader[S].Misc.VirtualSize);
// scrivo su file headers e nuova sezione
//
però prima metto a posto l'EP
ImgNtHeaders->OptionalHeader.
AddressOfEntryPoint
= OEP;
SetFilePointer(hDumpFile,
0,
NULL,
FILE_BEGIN);
WriteFile(hDumpFile,
ImgDosHeader,
ImgSectionHeader[0].VirtualAddress,
&BW, NULL);
SetFilePointer(hDumpFile,
0,
NULL,
FILE_END);
WriteFile(hDumpFile,
NewSect, VSize, &BW, NULL);
SetFilePointer(hDumpFile,
(CalcAlignment(
ImgNtHeaders->OptionalHeader.SectionAlignment,
ImgSectionHeader[S].Misc.VirtualSize)
-
VSize),
NULL, FILE_CURRENT);
SetEndOfFile(hDumpFile);
// chiudo il Dump file
CloseHandle(hDumpFile);
delete
NewSect;
// tutto ok
bOk
= TRUE;
break;
}
SetThreadContext(pi.hThread,
&Context);
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
else if (DbgEvent.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_SINGLE_STEP)
{
// se ho già il Ret Address
if (RetAddress
!= 0)
{
// rimetto il break point
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
RetAddress,
&BP, sizeof (BYTE), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
// altrimenti...
BYTE
Istro;
// leggo la prossima istro che viene eseguita
if (ReadProcessMemory(pi.hProcess,
(LPCVOID) Context.Eip,
&Istro,
sizeof (BYTE), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
if (Istro
== 0xC3 &&
(Context.Eip
>= VboxBase &&
Context.Eip
< (VboxBase +
VboxSize)))
{
// legge lo stack pointer
DWORD
NextAddr;
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
Context.Esp,
&NextAddr, sizeof (DWORD),
&BR)
== FALSE)
{
printf("An
error occurred\n");
break;
}
// controllo se è il fatidico ret
if
(NextAddr < VboxBase ||
NextAddr
>= (VboxBase + VboxSize))
{
RetAddress
= Context.Eip;
WORD
Call = 0x10FF; // call [eax]
// mette la call all'entry point
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
VA_OEP,
&Call, sizeof (WORD), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
// setto il break point al ret
BP_Buf
= 0xC3;
if
(WriteProcessMemory(pi.hProcess, (LPVOID)
RetAddress,
&BP, sizeof (BYTE), &BW) == FALSE)
{
printf("An
error occurred\n");
break;
}
int
C;
// chiedo all'utente se la iat la trovo
//
da solo o se me la dice lui
printf("Autofind
IAT? [Y/N]: ");
C
= getch();
if
(C != 'y' && C != 'Y')
{
char
Buffer[10];
printf("\n\nStart
Address: ");
scanf("%s",
Buffer);
StartOfIAT
= strtoul(Buffer, 0, 16);
printf("\nEnd
Address: ");
scanf("%s",
Buffer);
EndOfIAT
= strtoul(Buffer, 0, 16);
}
else
{
// devo cercare la IAT
//
e per farlo parto dal Base Of Data
DWORD
Base;
StartOfIAT
= Base =RvaToVa(
ImgNtHeaders,
ImgNtHeaders->OptionalHeader.
BaseOfData);
// posso cercare
fino alla fine
//
dell'image size
EndOfIAT
= RvaToVa(ImgNtHeaders,
ImgNtHeaders->OptionalHeader.
SizeOfImage);
DWORD
Api;
// prendo la prima
api risolta
//
nella fake IAT del vbox
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
RvaToVa(ImgNtHeaders,
Descr[0].FirstThunk),
&Api,
sizeof (DWORD), &BR) == FALSE)
{
printf("An
error occurred\n");
break;
}
// e la cerco
a partire dal
//
Base Of Data
DWORD
*Data = new DWORD [PAGE_SIZE /
sizeof
(DWORD)];
BOOL
bFound = FALSE;
while
(StartOfIAT < EndOfIAT)
{
ReadProcessMemory(pi.hProcess,
(LPCVOID)
StartOfIAT,
Data, PAGE_SIZE, &BR);
DWORD
Count = 0;
for
( ; Count < PAGE_SIZE /
sizeof
(DWORD); Count++)
{
if
(Data[Count] == Api)
{
StartOfIAT
+= Count
*
sizeof (DWORD);
bFound
= TRUE;
break;
}
}
if
(bFound == TRUE)
break;
StartOfIAT
+= PAGE_SIZE;
}
if
(bFound == FALSE)
{
printf("Cannot
find IAT\n");
break;
}
DWORD
Check;
// va indietro
cercando due dword nulle
//
oppure l'inizio di una serzione
//
per trovare l'inizio della IAT
bFound
= FALSE;
while
(StartOfIAT >= Base)
{
// controllo a
partire della
//
seconda sezione
for
(UINT x = 1; x <
ImgNtHeaders->FileHeader.
NumberOfSections;
x++)
{
if
(ImgSectionHeader[x].
VirtualAddress
== VaToRva(
ImgNtHeaders,
StartOfIAT))
{
StartOfIAT
-= sizeof (DWORD);
bFound
= TRUE;
break;
}
}
if
(bFound == TRUE)
break;
StartOfIAT
-= sizeof (DWORD);
ReadProcessMemory(pi.hProcess,
(LPCVOID)
StartOfIAT,
&Check, sizeof (DWORD), &BR);
if
(Check == NULL)
{
if
(ReadProcessMemory(pi.hProcess, (LPCVOID)
(StartOfIAT
- sizeof (DWORD)), &Check,
sizeof
(DWORD), &BR))
{
if
(Check == NULL)
{
bFound
= TRUE;
break;
}
}
}
if
(bFound == TRUE)
break;
}
if
(bFound == FALSE)
{
printf("Cannot
find IAT\n");
break;
}
StartOfIAT
+= sizeof (DWORD);
EndOfIAT
= StartOfIAT + sizeof (DWORD);
bFound
= FALSE;
while
(TRUE)
{
ReadProcessMemory(pi.hProcess,
(LPCVOID)
EndOfIAT,
&Check, sizeof (DWORD), &BR);
if
(Check == NULL)
{
ReadProcessMemory(pi.hProcess,
(LPCVOID)
(EndOfIAT
+ sizeof (DWORD)),
&Check,
sizeof (DWORD), &BR);
if
(Check == NULL)
{
bFound
= TRUE;
break;
}
}
EndOfIAT
+= sizeof (DWORD);
}
if
(bFound == FALSE)
{
printf("Cannot
find end of IAT\n");
break;
}
}
// ptr iat = inizio
iat
IAT_Ptr
= StartOfIAT;
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
}
// continuo il
tracing single step
Context.EFlags
|= TRAP_FLAG;
SetThreadContext(pi.hThread,
&Context);
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId,
DBG_CONTINUE);
continue;
}
}
else if (DbgEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
break;
}
ContinueDebugEvent(DbgEvent.dwProcessId,
DbgEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
CloseAll();
if (bOk == FALSE)
return;
printf("\nProg unpacked, rebuild IT? [Y/N]: ",
DumpName);
int C;
C = getch();
if (C != 'y' && C != 'Y')
return;
// ripristino la IT
HINSTANCE hWdll = LoadLibrary("wdll.dll");
if (!hWdll)
{
printf("\nCannot load wdll.dll\n");
return;
}
BOOL (__cdecl *RebuildIT)(char *, DWORD, BOOL);
RebuildIT = (BOOL (__cdecl *)(char *, DWORD, BOOL))
GetProcAddress(hWdll, "RebuildImportTable");
BOOL bRet = RebuildIT(DumpName, 0, FALSE);
if (bRet == FALSE)
{
printf("\nCannot rebuild IT\n");
return;
}
printf("\nIT rebuilded\n");
return;
}
------------------------------------------------------------------
Ok abbiamo finito, endlich! E sono pure riuscito a finire unpacker + tutorial in tre giorni così
me ne avanzano due di pura libertà (yahoo).
CONCLUSIONI
Come vedete quando si coda un unpacker bisogna tener conto di tante cose e nonostante
l'unpacker sia 'solo' per vbox ho scritto abbastanza codice. Inoltre alla fin
fine i problemi che si pongono sono sempre gli stessi, si può solo complicare
alcuni dettagli ma nel complesso il gioco resta sempre lo stesso.
Note finali |
Per finire stavolta voglio salutare un po' di persone:
AndreaGeddon con il quale si possono fare sempre in query discussioni interessanti
(informatica, società, scienza, tricologia NdQue :P)
=)
Quake2 che se non mi finisce il wpe come minimo lo mangio!!!
Quequero con le sue idee stxxxxx (stupende) comunicateci nei momenti più merxxxxxx
(meravigliosi) =)
themr ovvero il mio leim kicker preferito, themr for president!!
Un saluto anche a tutti quelli che hanno lavorato al xmas-pack. Bah, in verità
potrei salutare un altro centinaio di persone, ma non mi pare il caso.
Un saluto a tutti e alla prossima!
Ntoskrnl
Disclaimer |
Ovviamente reverso solo a scopo didattico (e perché mi piace photoshop).