/* RunPE unpacker - interestingmalware@gmail.com
*
* Based on the example at http://www.hex-rays.com/idapro/scriptable.htm
*/
#include <idc.idc>
// flag for CreateProcess()
#define CREATE_SUSPENDED 0x4
static main() {
auto code, bptea;
auto esp, flags;
auto buffer;
auto filename = "unpacked.exe";
// ignore EXCEPTION_FLT_INEXACT_RESULT. VB6 uses this a lot and usually
// handles the exception, but it makes things a pain for debuggers
SetExceptionFlags(0xC000008F, 0);
Message("Starting process...\n");
// launch the debugger and run until the entry point
if (!RunTo(BeginEA()))
return Warning("Can't debug to entrypoint");
// wait until process is suspended
code = GetDebuggerEvent(WFNE_SUSP, -1);
if (code <= 0)
return Warning("Can't get debugger event");
// set breakpoint on CreateProcessW
bptea = AddHardwareBp("kernel32_CreateProcessW");
if(!bptea)
return Warning("Can't break on CreateProcessW");
// resume the execution and wait until CreateProcessW is called
code = GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1);
if (code <= 0)
return Warning("Can't resume execution for CreateProcessW breakpoint");
DelBpt(bptea);
// check arguments to CreateProcessW
esp = GetRegValue("ESP");
Message("CreateProcessW called from %.8X\n", Dword(esp));
Message(" lpApplicationName: %s\n", GetString(Dword(esp+4), -1, ASCSTR_UNICODE));
Message(" lpCommandLine: %s\n", GetString(Dword(esp+8), -1, ASCSTR_UNICODE));
// make sure flags contain CREATE_SUSPENDED
flags = Dword(esp+24);
if(!(flags & CREATE_SUSPENDED)) {
return Warning("Process created without CREATE_SUSPENDED flag (flag = %.8X)", flags);
}
// let the call to CreateProcessW finish - run until the return address is hit
RunTo(Dword(esp));
while(1) {
code = GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1);
if (code <= 0)
return Warning("Failed to let CreateProcessW finish");
if(code != STEP) break;
}
// now break on calls to NtWriteVirtualMemory
bptea = AddHardwareBp("ntdll_NtWriteVirtualMemory");
if(!bptea)
return Warning("Can't set breakpoint on NtWriteVirtualMemory");
// resume the execution and wait until NtWriteVirtualMemory is called
code = GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1);
if (code <= 0)
return Warning("Can't resume execution for NtWriteVirtualMemory breakpoint");
esp = GetRegValue("ESP");
buffer = Dword(esp+12);
DelBpt(bptea);
Message("NtWriteVirtualMemory called from %.8X, buffer address: %.8X\n", Dword(esp), buffer);
Message("Dumping file...\n");
DumpPE(buffer, filename);
}
// add a hardware breakpoint at the specified name.
// success: returns the address of the breakpoint
// fail: returns 0
static AddHardwareBp(name) {
auto bptea;
// get address of function
bptea = LocByName(name);
if (bptea == BADADDR) {
Message ("Could not locate %s\n", name);
return 0;
}
// set a hardware breakpoint
Message("Setting a hardware breakpoint on %s\n", name);
// XXX - this should check return value, but sometimes this call fails
// even when the breakpoint is still set (in IDA 5.7)
AddBptEx(bptea, 1, BPT_EXEC);
//if(!AddBptEx(bptea, 1, BPT_EXEC)) {
// Message("Failed to add hardware breakpoint for %s\n", name);
// return 0;
//}
return bptea;
}
// for dump_pe()
#define OPTHEADER_OFFSET 0x18
#define SECTION_HEADER_SIZE 0x28
// write a PE file stored in memory to disk. this function determines the size
// of the PE file by calculating the offset of the end of the last section.
// this can miss data stored by some PE files. there are probably better ways
// to do this, but it works for me.
static DumpPE(start_addr, filename) {
auto nt_headers, num_secs, last_sec;
auto end_addr;
auto f, ptr;
// check the DOS header magic value
SetType(start_addr, "IMAGE_DOS_HEADER;");
if(Word(start_addr.e_magic) != 0x5a4d) {
Message("Not an EXE, invalid magic: %x\n", Word(start_addr.e_magic));
return 0;
}
nt_headers = start_addr + Dword(start_addr.e_lfanew);
SetType(nt_headers, "IMAGE_NT_HEADERS;");
// check NT headers magic value
if(Dword(nt_headers.Signature) != 0x4550) {
Message("Not a PE, invalid signature: %x\n", Dword(nt_headers.Signature));
return 0;
}
// figure out number of sections
num_secs = Word(nt_headers.FileHeader.NumberOfSections);
if(num_secs > 16) {
Message("Sanity check failed, %i sections\n", num_secs);
return;
}
// get offset of last section header
last_sec = nt_headers + OPTHEADER_OFFSET + Word(nt_headers.FileHeader.SizeOfOptionalHeader) +
(SECTION_HEADER_SIZE * (num_secs - 1));
// figure out offset the end of the last section
SetType(last_sec, "IMAGE_SECTION_HEADER;");
end_addr = start_addr + Dword(last_sec.PointerToRawData) + Dword(last_sec.SizeOfRawData);
// sanity check
if(start_addr == end_addr) {
Message("Something broke, start = end = %x\n", start_addr);
return 0;
}
Message("Writing %i bytes to %s...\n", end_addr - start_addr, filename);
// write the file
f = fopen(filename, "wb");
for(ptr=start_addr; ptr<end_addr; ptr++) {
fputc(Byte(ptr), f);
}
fclose(f);
Message("Done writing file\n");
return 1;
}