在寻找一种在内存中运行可移植可执行文件的方法后,我在大约 10 个不同的项目中偶然发现了同一段代码,它们都具有相同的黑魔法硬编码数字。我尽我所能重构它并进行一些错误处理,这是最终结果:
#include <stdint.h>
#include <Windows.h>
unsigned long run_portable_executable(unsigned char* binary)
{
int success = 1, rc = 0;
const uintptr_t binary_address = (uintptr_t)binary;
IMAGE_DOS_HEADER* const dos_header = (IMAGE_DOS_HEADER*)binary;
IMAGE_NT_HEADERS* const nt_header = (IMAGE_NT_HEADERS*)(binary_address + dos_header->e_lfanew);
if (nt_header->Signature != IMAGE_NT_SIGNATURE) {
rc = 1;
goto out;
}
STARTUPINFOW startup_info;
PROCESS_INFORMATION process_info;
SecureZeroMemory(&startup_info, sizeof(startup_info));
SecureZeroMemory(&process_info, sizeof(process_info));
wchar_t current_file_path[MAX_PATH];
GetModuleFileNameW(NULL, current_file_path, MAX_PATH);
success = CreateProcessW(current_file_path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup_info, &process_info);
if (!success)
goto out;
CONTEXT* const ctx = (CONTEXT*)VirtualAlloc(NULL, sizeof(ctx), MEM_COMMIT, PAGE_READWRITE);
ctx->ContextFlags = CONTEXT_FULL;
success = GetThreadContext(process_info.hThread, ctx);
if (!success)
goto out;
uintptr_t* image_base;
void* const modified_ebx = (void*)(ctx->Ebx + 8);
success = ReadProcessMemory(process_info.hProcess, modified_ebx, &image_base, 4, NULL);
if (!success)
goto out;
void* const binary_base = VirtualAllocEx(process_info.hProcess, (void*)(nt_header->OptionalHeader.ImageBase),
nt_header->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
success = WriteProcessMemory(process_info.hProcess, binary_base, binary, nt_header->OptionalHeader.SizeOfHeaders, NULL);
if (!success)
goto out;
const uintptr_t binary_base_address = (uintptr_t)binary_base;
for (unsigned short i = 0; i < nt_header->FileHeader.NumberOfSections; ++i) {
IMAGE_SECTION_HEADER* section_header = (IMAGE_SECTION_HEADER*)(binary_address + dos_header->e_lfanew + 248 + (i * 40));
void* const virtual_base_address = (void*)(binary_base_address + section_header->VirtualAddress);
void* const virtual_buffer = (void*)(binary_address + section_header->PointerToRawData);
success = WriteProcessMemory(process_info.hProcess, virtual_base_address, virtual_buffer, section_header->SizeOfRawData, 0);
if (!success)
goto out;
}
success = WriteProcessMemory(process_info.hProcess, modified_ebx, (void*)&nt_header->OptionalHeader.ImageBase, 4, 0);
if (!success)
goto out;
ctx->Eax = binary_base_address + nt_header->OptionalHeader.AddressOfEntryPoint;
success = SetThreadContext(process_info.hThread, ctx);
if (!success)
goto out;
success = ResumeThread(process_info.hThread);
if (!success)
goto out;
out:
return !success ? GetLastError() : rc;
}
Run Code Online (Sandbox Code Playgroud)
这工作得很好,但是,我不明白几个部分。
下面的指针指向什么:
void* const modified_ebx = (void*)(ctx->Ebx + 8);
Run Code Online (Sandbox Code Playgroud)
我假设它与堆栈寄存器有关ebx,但为什么它增加了 8 个字节,它应该代表什么?
接下来困扰我的事情如下:
(IMAGE_SECTION_HEADER*)(binary_address + dos_header->e_lfanew + 248 + (i * 40));
Run Code Online (Sandbox Code Playgroud)
我的猜测是每个文件头部分都是 40 字节,这解释了i * 40,但为什么会有248偏移量呢?这些价值观从何而来?如果有一个特定的结构可以解释这些偏移量,如果有人能让我知道,我将不胜感激,以便我可以用正确的sizeof().