ASPack Automatic Unpacking
From UIC
Aspack 2.12 automatic unpacking
Contents |
| ASPack Automatic Unpacking | |
|---|---|
| Author: | Pn |
| Email: | |
| Website: | No Site |
| Date: | 29/10/2004 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| 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<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
{
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
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
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.