AUTOMATIC UNPACKING: VBox 4.6

Data

by Ntoskrnl

 

28/12/2003

UIC's Home Page

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