Complete C++ Code: Memory-Only PE Loader
// MemoryPELoader.cpp
#include <Windows.h>
#include <iostream>
typedef BOOL(WINAPI* DLLMAIN)(HINSTANCE, DWORD, LPVOID);
void* LoadPEFromMemory(unsigned char* rawData) {
// Step 1: Parse headers
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)rawData;
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cout << "Invalid DOS Signature.\n";
return nullptr;
}
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(rawData + dosHeader->e_lfanew);
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
std::cout << "Invalid NT Signature.\n";
return nullptr;
}
// Step 2: Allocate memory for full image
void* allocatedMem = VirtualAlloc(
(LPVOID)(ntHeaders->OptionalHeader.ImageBase),
ntHeaders->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (!allocatedMem) {
// Try allocating anywhere if preferred base fails
allocatedMem = VirtualAlloc(
nullptr,
ntHeaders->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
}
if (!allocatedMem) {
std::cout << "Memory allocation failed.\n";
return nullptr;
}
// Step 3: Copy headers
memcpy(allocatedMem, rawData, ntHeaders->OptionalHeader.SizeOfHeaders);
// Step 4: Copy sections
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
for (int i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, section++) {
void* dest = (BYTE*)allocatedMem + section->VirtualAddress;
void* src = rawData + section->PointerToRawData;
memcpy(dest, src, section->SizeOfRawData);
}
// Step 5: Fix imports
if (ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) {
PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)(
(BYTE*)allocatedMem + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
);
while (importDesc->Name) {
char* moduleName = (char*)((BYTE*)allocatedMem + importDesc->Name);
HMODULE hModule = LoadLibraryA(moduleName);
if (!hModule) {
std::cout << "Failed to load module: " << moduleName << "\n";
return nullptr;
}
PIMAGE_THUNK_DATA thunkOrig = (PIMAGE_THUNK_DATA)((BYTE*)allocatedMem + importDesc->OriginalFirstThunk);
PIMAGE_THUNK_DATA thunkFirst = (PIMAGE_THUNK_DATA)((BYTE*)allocatedMem + importDesc->FirstThunk);
while (thunkOrig->u1.AddressOfData) {
FARPROC func = nullptr;
if (thunkOrig->u1.Ordinal & IMAGE_ORDINAL_FLAG) {
func = GetProcAddress(hModule, (LPCSTR)(thunkOrig->u1.Ordinal & 0xFFFF));
} else {
PIMAGE_IMPORT_BY_NAME importName = (PIMAGE_IMPORT_BY_NAME)((BYTE*)allocatedMem + thunkOrig->u1.AddressOfData);
func = GetProcAddress(hModule, (LPCSTR)importName->Name);
}
if (!func) {
std::cout << "Failed to resolve import.\n";
return nullptr;
}
thunkFirst->u1.Function = (ULONGLONG)func;
++thunkOrig;
++thunkFirst;
}
++importDesc;
}
}
// Step 6: Relocate if necessary
ULONGLONG delta = (ULONGLONG)allocatedMem - ntHeaders->OptionalHeader.ImageBase;
if (delta != 0) {
if (ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) {
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(
(BYTE*)allocatedMem + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress
);
while (reloc->VirtualAddress) {
WORD* relocItem = (WORD*)((BYTE*)reloc + sizeof(IMAGE_BASE_RELOCATION));
int items = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
for (int i = 0; i < items; i++) {
if ((relocItem[i] >> 12) == IMAGE_REL_BASED_DIR64) {
ULONGLONG* patchAddr = (ULONGLONG*)((BYTE*)allocatedMem + (reloc->VirtualAddress + (relocItem[i] & 0xFFF)));
*patchAddr += delta;
}
}
reloc = (PIMAGE_BASE_RELOCATION)((BYTE*)reloc + reloc->SizeOfBlock);
}
}
}
return allocatedMem;
}
void ExecuteLoadedPE(void* peImage) {
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)peImage;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)peImage + dosHeader->e_lfanew);
void* entryPoint = (BYTE*)peImage + ntHeaders->OptionalHeader.AddressOfEntryPoint;
if (ntHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) {
// Call DllMain
DLLMAIN dllEntry = (DLLMAIN)entryPoint;
dllEntry((HINSTANCE)peImage, DLL_PROCESS_ATTACH, nullptr);
} else {
// Call main() directly
using MainFunc = void(*)();
MainFunc mainFunc = (MainFunc)entryPoint;
mainFunc();
}
}
int main() {
std::cout << "[*] Memory PE Loader started.\n";
// -- Example: Load a raw PE binary from file into memory --
HANDLE hFile = CreateFileA("payload.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cout << "[-] Cannot open payload.exe.\n";
return -1;
}
DWORD fileSize = GetFileSize(hFile, NULL);
unsigned char* buffer = new unsigned char[fileSize];
DWORD bytesRead = 0;
ReadFile(hFile, buffer, fileSize, &bytesRead, NULL);
CloseHandle(hFile);
if (bytesRead != fileSize) {
std::cout << "[-] File read error.\n";
delete[] buffer;
return -1;
}
// -- Load into memory --
void* loadedPE = LoadPEFromMemory(buffer);
delete[] buffer;
if (!loadedPE) {
std::cout << "[-] Loading PE failed.\n";
return -1;
}
std::cout << "[+] PE loaded successfully at memory: " << loadedPE << "\n";
// -- Execute it --
ExecuteLoadedPE(loadedPE);
std::cout << "[*] Execution finished.\n";
return 0;
}
Comments
Post a Comment