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

Popular posts from this blog

Avoiding detection when injecting a DLL into a game process

Some global variable set, mystery function

FULL REFLECTIVE INJECTION PACK