在内存中运行可移植可执行文件 - WinApi

Mic*_*ith 13 c winapi

这是我目前的代码:

#include <Windows.h>

DWORD run_portable_executable(unsigned char* binary)
{
    BOOL success;
    const DWORD binary_address = (DWORD)binary;
    IMAGE_DOS_HEADER* const dos_header = (LPVOID)binary;
    IMAGE_NT_HEADERS* const nt_header = (LPVOID)(binary_address + dos_header->e_lfanew);

    STARTUPINFOW startup_info;
    PROCESS_INFORMATION process_info;

    // Zero the structs to ensure valid values.
    SecureZeroMemory(&startup_info, sizeof(startup_info));
    SecureZeroMemory(&process_info, sizeof(process_info));

    WCHAR current_file_path[MAX_PATH];
    GetModuleFileNameW(NULL, current_file_path, MAX_PATH);

    // Use the current executable as a dummy process to be taken over by the binary.
    success = CreateProcessW(current_file_path, NULL, NULL, NULL, FALSE, 
        CREATE_SUSPENDED, NULL, NULL, &startup_info, &process_info);

    if (!success)
        goto err;

    CONTEXT ctx = 
    {
        .ContextFlags = CONTEXT_FULL
    };

    success = GetThreadContext(process_info.hThread, &ctx);

    if (!success)
        goto err;

    // The following will occasionally fail because the fixed address of 0x400000 might
    // not be available or might not contain enough space.
    LPVOID const pe_base = VirtualAllocEx(process_info.hProcess, 
        (LPVOID)nt_header->OptionalHeader.ImageBase, 
            nt_header->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, 
                PAGE_EXECUTE_READWRITE);

    if (!pe_base)
        goto err;

    success = WriteProcessMemory(process_info.hProcess, pe_base, binary,
        nt_header->OptionalHeader.SizeOfHeaders, NULL);

    if (!success)
        goto err;

    const DWORD pe_base_address = (DWORD)pe_base;
    const DWORD end_of_pe_header = 
        binary_address + dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS);

    for (WORD i = 0; i < nt_header->FileHeader.NumberOfSections; ++i)
    {
        const DWORD section_offset = i * sizeof(IMAGE_SECTION_HEADER);
        const IMAGE_SECTION_HEADER* const section_header = 
            (LPVOID)(end_of_pe_header + section_offset);

        LPVOID const section_base_address = 
            (LPVOID)(pe_base_address + section_header->VirtualAddress);

        LPCVOID const section_binary_buffer = 
            (LPVOID)(binary_address + section_header->PointerToRawData);

        success = WriteProcessMemory(process_info.hProcess, section_base_address, 
            section_binary_buffer, section_header->SizeOfRawData, NULL);

        if (!success)
            goto err;
    }

    // Ebx points to the PEB struct, where the 8 byte offset points to the
    // ImageBaseAddress member.
    LPVOID const modified_ebx = (LPVOID)(ctx.Ebx + 8);

    success = WriteProcessMemory(process_info.hProcess, modified_ebx, 
        &nt_header->OptionalHeader.ImageBase, sizeof(DWORD), NULL);

    if (!success)
        goto err;

    ctx.Eax = pe_base_address + nt_header->OptionalHeader.AddressOfEntryPoint;

    success = SetThreadContext(process_info.hThread, &ctx);

    if (!success)
        goto err;

    success = ResumeThread(process_info.hThread);

    if (!success)
        goto err;

    return 0;
err:
    return GetLastError();
}
Run Code Online (Sandbox Code Playgroud)

这将在大多数时间工作.问题出在这里:

VirtualAllocEx(process_info.hProcess, (LPVOID)nt_header->OptionalHeader.ImageBase, 
    nt_header->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE);
Run Code Online (Sandbox Code Playgroud)

这有时会失败并返回487错误代码.我发现了一个关于这个错误的高度相关的问题,它确切地解释了出了什么问题.此虚拟分配将始终发生在固定地址0x400000(可执行文件的默认值),但如果该地址当前不可用,则会返回487错误代码(无效地址).

我的问题是我该如何处理?我不能简单地设置传入的第二个参数VirtualAllocEx(),NULL因为它与当前图像不匹配.我链接问题建议使用ReBaseImage(),但我不知道如何在内存中为可移植可执行文件执行此操作.该函数需要路径指向.exe.dll然后写入对图像所做的更改.怎么可能在内存中完成?

编辑: RbMm提出搬迁使用图像LdrProcessRelocationBlock,函数从ntdll.dll具有以下特征:

IMAGE_BASE_RELOCATION* WINAPI LdrProcessRelocationBlock(ULONG_PTR VA, ULONG SizeOfBlock,
    PUSHORT NextOffset, LONG_PTR Diff)
Run Code Online (Sandbox Code Playgroud)

但是,由于此方法的第三方文档很少,我不确定如何使用它来移动图像来重新定义图像.如果有人熟悉它的用法,我们将非常感谢一个例子.

Sam*_*tha 2

变基 PE 映像需要使用 PE 文件的重定位表(通常在 参考资料 部分)修复 RVA .reloc。如果VirtualAlloc没有返回首选基地址的内存,您确实必须VirtualAlloc再次调用NULL它返回的地址空间并对它执行修复。

hasherezade 的libpeconv库提供了该功能的公共实现。