这是我目前的代码:
#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)
但是,由于此方法的第三方文档很少,我不确定如何使用它来移动图像来重新定义图像.如果有人熟悉它的用法,我们将非常感谢一个例子.
变基 PE 映像需要使用 PE 文件的重定位表(通常在 参考资料 部分)修复 RVA .reloc
。如果VirtualAlloc
没有返回首选基地址的内存,您确实必须VirtualAlloc
再次调用NULL
它返回的地址空间并对它执行修复。
hasherezade 的libpeconv库提供了该功能的公共实现。