Zoom Icon

ASPack Automatic Unpacking

From UIC

Aspack 2.12 automatic unpacking

Contents


ASPack Automatic Unpacking
Author: Pn
Email: Email
Website: No Site
Date: 29/10/2004 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments: Bravo pn, non ti conveniva checkare per una qualche signature invece di leggere direttamente il nome della sezione? :) Bravo davvero. Que



Introduzione

Per fare un automatic unpacking dovreste avere le conoscenze di: PE e debug api.


Tools


Essay

Questa è la seconda parte del mio tute sull’aspack 2.12 e cercheremo di creare un automatic unpacking.
Per prima cosa dobbiamo trovare l’OEP e l’IT del programma da unpackare, se avete letto il mio tute precedente avete capito che è una cosa semplice.
Infatti RVA dell’OEP e dell’IT si trovano in questo modo:
ImageBase + VirtuaAddress(.aspack) + [0x39B(OEP) || 0x279(IT)].
Per approfondimenti vedete il tute di aspack.

Allora noi dobbiamo fare ciò:

  • Prendere l’image-base e il VirtualAddress della sezione .aspack (se è presente), salvarli in variabili e trovare l’IT e l’OEP.
  • Creare un debug ad hoc, che si fermi prima che aspack giocherelli con l’IAT e settare un bp .
  • Al bp dumpare tutte le sezioni, tranne quelle da .aspack in poi.
  • Aprire il file dumpato e cambiare il numero di sezioni, il file-alignment, l’OEP, l'IT, settare RawSize=VirtualSize e RawOffset=VirtualOffset, azzerare il campo Relocation e calcolare la grandezza dell’IT.


Procediamo con ordine, iniziamo con il primo passo:

File PE

#include<windows.h>  
#include<stdio.h>  

int main() {

    HANDLE hFile;   //handle del file
    FILE *pFile;   //qui ho fatto un obbrobrio (ndPn 2008)
    BYTE *BaseAddress;   //puntantore allo spazio di memoria che contiene l'exe
    BYTE x, sezmen;     //variabile di supporto e le sezioni da togliere
    DWORD OffsetOEP, OffsetIT ,addrOEP, addrIT;    //i vari offset
    BYTE aspdir = 0;    //indice della sezione aspack nella SectionTable
    DWORD it = 0x279;  //offset dove leggere l'IT
    DWORD oep = 0x39b; //offset dove leggere l’oep
    DWORD int3 = 0xCC;  //opcode di int3
    DWORD buffer, imagebase;  
    DWORD size=0;   //grandezza del dump
    DWORD FileSize, rawsize, VA;  //grandezza del file, offset e VA della sezione .aspack
    DWORD BR, BW, brw;
    char NomeFile[15];

    //Per il PE
    IMAGE_DOS_HEADER *ImageDosHeader;    
    IMAGE_NT_HEADERS *ImageNtHeaders;
    IMAGE_SECTION_HEADER *ImageSectionHeader;

    //Per debug
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    DEBUG_EVENT DebugEv;
    DWORD dwContinueStatus;
    CONTEXT    context;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi) );

    printf("\n\nUnaspack for aspack 2.12 by Pn\n\n");  //si prega di non rimuoverlo :D
    printf("Inserisci il file protetto con aspack: ");
    scanf("%s",NomeFile);
    printf("\nInizio analisi...\n\n");

   hFile = CreateFile(NomeFile,GENERIC_READ, FILE_SHARE_READ,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (hFile == INVALID_HANDLE_VALUE)
        {
        printf("Non posso aprire il file\n");
        return -1;
        }

    FileSize = GetFileSize(hFile,NULL);
    BaseAddress = (BYTE *) malloc(FileSize);
    if(!ReadFile(hFile,BaseAddress,FileSize,&BR,NULL))
       {
        free(BaseAddress);
        CloseHandle(hFile);
        return -1;
        }

    ImageDosHeader = (IMAGE_DOS_HEADER *) BaseAddress;
    if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
        {
        printf("Dos handle invalido");
        free(BaseAddress);
        CloseHandle(hFile);
        return -1;
        }

    ImageNtHeaders = (IMAGE_NT_HEADERS *) (ImageDosHeader->e_lfanew +(DWORD) ImageDosHeader);

     if (ImageNtHeaders->Signature != IMAGE_NT_SIGNATURE)
         {
         printf("Invalid Pe handle");
         free(BaseAddress);
         CloseHandle(hFile);
         return -1;
         }

        ImageSectionHeader = IMAGE_FIRST_SECTION(ImageNtHeaders);  //indirizzo prima sezione del PE

        //Verifa se è presente una sezione .aspack
        printf("La sezione .aspack e' la num :\t");
        for(x=0; x <= ImageNtHeaders->FileHeader.NumberOfSections; x++){
            if(!strcmp((char*)ImageSectionHeader[x].Name,".aspack"))
                           {
                           aspdir = x;
                           break;
                           }
        }

      //Le sezioni da togliere nel dumping
     sezmen =  ImageNtHeaders->FileHeader.NumberOfSections - aspdir;

     //Vede se ha trovato una sezione .aspack
     if(aspdir == 0) {
       printf("Non trovata.\nForse non è packato");return -1;}
        printf("%X\n",aspdir+1);

     //calcola Offser e VA della sezione .aspack
     rawsize = ImageSectionHeader[aspdir].PointerToRawData;
     VA = ImageNtHeaders->OptionalHeader.ImageBase + ImageSectionHeader[aspdir].VirtualAddress;
     imagebase = ImageNtHeaders->OptionalHeader.ImageBase;

     //calcola l’offset e VA dell’OEP e dell’IT
        OffsetIT = rawsize + it;
        OffsetOEP = rawsize + oep;
        addrIT = VA + it;
        addrOEP = VA + oep;

        //Calcola la grandezza del file da dumpare
        size +=  ImageSectionHeader[0].VirtualAddress;
        for(x=0; x<=aspdir-1;x++)
        {
           size += ImageSectionHeader[x].Misc.VirtualSize;
        }

        //chiude tutto perchè abbiamo finito di lavorare con il PE(per il momento)
        free(BaseAddress);
        CloseHandle(hFile);

       //troviamo l’OEP e l’IT
       pFile = fopen(NomeFile,"rb");
       if (pFile==NULL) {printf("Errore ri-apertura file");return 0;}
       printf("\nInizio ricerca delle info per il dumping:");
       printf("\n\tL'OEP originale e’ (RVA):");
       fseek(pFile,OffsetOEP,SEEK_SET);
       oep = getw(pFile);
       printf("  %X",oep);
       printf("\n\tL'IT originale e’ (RVA):");
       fseek(pFile,OffsetIT,SEEK_SET);
       it = getw(pFile);
       printf("   %X",it);
       fclose(pFile);

Abbiamo trovato l’OEP e l’IT, ora dobbiamo fare il debugger che ci fermi prima che aspack giocherelli con l’IAT.

Debugger

//Creamo il processo per il debug  if(!CreateProcess(NomeFile,NULL,NULL,NULL,FALSE,DEBUG_ONLY_THIS_PROCESS|CREATE_SUSPENDED,NULL,NULL,&si,&pi))
    {
    printf("\nImpossibile creare il processo\n");return -1;}
    printf("\nProcesso avviato");

    HANDLE hProc = pi.hProcess;
    HANDLE hMainT = pi.hThread;

    //salvo il byte prima della modifica
    if(!ReadProcessMemory(hProc,(void*)addrIT-1,&buffer,1,NULL)){return -1;}

    //metto un bp
    if(!WriteProcessMemory(hProc, (void*)addrIT-1, &int3, 1, NULL)){
    return -1;}
    printf("\nBp settato");

    // ripristina l'esecuzione
    ResumeThread(hMainT);

//ora inziamo a fare il debug vero e proprio
for(;;)

{
   dwContinueStatus = DBG_CONTINUE;
   WaitForDebugEvent(&DebugEv, INFINITE);
   // risponde agli eventi di debug
   switch (DebugEv.dwDebugEventCode)
   {
   case EXCEPTION_DEBUG_EVENT:
   if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
   {
      if(DebugEv.u.Exception.ExceptionRecord.ExceptionAddress != (void*)addrIT-1)
      {
      dwContinueStatus = DBG_CONTINUE;
      }
      else
      {
      // risponde al breakpoint
      printf("\nRaggiunto un bp a: [%8.X]",DebugEv.u.Exception.ExceptionRecord.ExceptionAddress);
    //rimetto a posto il byte originale
    if(!WriteProcessMemory(hProc,(void*)addrIT-1,&buffer,1,NULL)){ return -1;}

   //Ritocca l’eip
   context.ContextFlags = 0x1003f;
    GetThreadContext(hMainT,&context);
    context.Eip--;
    SetThreadContext(hMainT,&context);

    //Continua con l’esecuzione
    ResumeThread(hMainT);

In questo modi ci siamo fermati al bp impostato da noi

Dumping

//Cerco di dumpare
    hFile = CreateFile("MyDump.exe", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,  FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile==INVALID_HANDLE_VALUE){return -1;}
    printf("\nFile Creato");

    // Alloco spazio in memoria
    BYTE *Section;
    Section = malloc(size);
    if(Section == NULL) {CloseHandle(hFile); printf("Errore per il  buffer"); return -1;}

    //legge in memoria
    if(ReadProcessMemory(hProc, (void*)imagebase, Section, size, &BR) == FALSE){
    printf("\nImpossibile leggere in memoria"); free(Section);
    CloseHandle(hFile); return -1;}

    //Dumpa
    if(!WriteFile(hFile,Section,size,&BR,NULL)){printf("\nImpossibile scrivere");
    free(Section);
    CloseHandle(hFile); return -1;} else {printf("\nFile dumpato");}
    free(Section);
    CloseHandle(hFile);

ora dobbiamo sistemare il PE del file dumpato:

PE Fixing

//Apre il file appena creato
   hFile = CreateFile("MyDump.exe",GENERIC_READ |GENERIC_WRITE, FILE_SHARE_READ,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (hFile == INVALID_HANDLE_VALUE)
        {
        printf("Non posso aprire il file\n");
        return -1;
        }

    FileSize = GetFileSize(hFile,NULL);
    BaseAddress = (BYTE *) malloc(FileSize);
   
 if(!ReadFile(hFile,BaseAddress,FileSize,&BR,NULL))
       {
        free(BaseAddress);
        CloseHandle(hFile);
        return -1;
        }

    ImageDosHeader = (IMAGE_DOS_HEADER *) BaseAddress;

    if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
        {
        printf("Dos handle invalido");
        free(BaseAddress);
        CloseHandle(hFile);
        return -1;
        }

    ImageNtHeaders = (IMAGE_NT_HEADERS *) (ImageDosHeader->e_lfanew +(DWORD) ImageDosHeader);

     if (ImageNtHeaders->Signature != IMAGE_NT_SIGNATURE)
         {
         printf("Invalid Pe handle");
         free(BaseAddress);
         CloseHandle(hFile);
         return -1;
         }
        ImageSectionHeader = IMAGE_FIRST_SECTION(ImageNtHeaders);

        //modifichiamo l'EP
        ImageNtHeaders->OptionalHeader.AddressOfEntryPoint = oep;

        //diminuiamo il munero delle sezioni
        ImageNtHeaders->FileHeader.NumberOfSections -= sezmen;

        //Modifichiamo size of image
        ImageNtHeaders->OptionalHeader.SizeOfImage = size;

        //riallineo
        ImageNtHeaders->OptionalHeader.FileAlignment = ImageNtHeaders->OptionalHeader.SectionAlignment;

        //SizeOfHeader = VA
        ImageNtHeaders->OptionalHeader.SizeOfHeaders = ImageSectionHeader->VirtualAddress;

        //RS=VS && RO=VO
        for(x=0;x<=ImageNtHeaders->FileHeader.NumberOfSections;x++){
        ImageSectionHeader[x].SizeOfRawData = ImageSectionHeader[x].Misc.VirtualSize;
        ImageSectionHeader[x].PointerToRawData = ImageSectionHeader[x].VirtualAddress;
        }

       //Modifichiamo l'IT        ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = it;

        //Modifichiamo Base relocation
ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0x0000;
ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0x0000;

        //calcoliamo la grandezza dell’it
        sizIT = 0x7f88;
        for(x=0;x<=10;x++){
        SetFilePointer(hFile,sizIT,NULL,FILE_BEGIN);
        if(!ReadFile(hFile, &buffer, 4, &BR, NULL)){printf("\nErrore lettura file");return -1;}
        if(buffer==0){break;}
        sizeIT+=0x14;
        sizIT += 0x0c;
        }

        //Modifichiamo il size dell'IT, quel + 0x14 indica una IT Table vuota
ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = sizeIT+0x14;

        //salviamo le modifiche
        SetFilePointer(hFile,0,NULL,FILE_BEGIN);
        if(!WriteFile(hFile,BaseAddress,FileSize,&brw,NULL)){
        printf("\nErrore nel modificare il Pe");free(BaseAddress);
        CloseHandle(hFile); return -1;}

        //se arriviamo qui abbiamo finito;
        free(BaseAddress);
        CloseHandle(hFile);
dwContinueStatus = DBG_CONTINUE;
    }

   }
   else
   {
     // se è eccezione cerca di farlo ripristinare dal prg o esce
     printf("\nRaggiunta un eccezione a: [%8.X]",
     DebugEv.u.Exception.ExceptionRecord.ExceptionAddress);
     dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
   }
   break;

   case EXIT_PROCESS_DEBUG_EVENT:

    // Se stiamo qui, significa che il prg sta per chiudersi.
    printf("\nE'stato chiamato ExitProcess");
    ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus);
    return;
   break;

   }
   ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus);
}
return 0;

}

Abbiamo finito.
Spero che sia stato abbastanza chiaro, altrimenti rileggete, vedete le reference di ciascuna api, o rileggete i tute sul debug e sul pe.
Alla prossima :D

PS: NON LEVATE IL POWERED BY PN, SENNO VE MAGNO :D


Note Finali

Grazie ai ragazzi della uic, Bender0, Geo, Fego, Ged per il suo tute su aspack chee mi ha aiutato un po’, Saturn e tutti gli altri che mi hanno aiutato.
Compreso Que che sta mettendo impiedi questo progetto
Poi i miei boss Nt e Quake :D

Pn =)


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.