Here is the code I currently have:
#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(); }
This will work most of the time. The problem is here:
VirtualAllocEx(process_info.hProcess, (LPVOID)nt_header->OptionalHeader.ImageBase, nt_header->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
This will occasionally fail and return the 487
error code. I have found a highly relevant question about this error which explained exactly what was going wrong. This virtual allocation would always happen at the fixed address of 0x400000
(default for executables), but if that address was currently unavailable at the time, it would return the 487
error code (invalid address).
My question is how should I handle this? I cannot simply set the second argument passed into VirtualAllocEx()
to NULL
as it would not match with the current image. The question I linked suggests using ReBaseImage()
, but I do not know how this would be done for a portable executable in memory. The function requires the path to the .exe
or .dll
to then write the changes made to the image. How could this be done in memory?
Edit: RbMm proposed to relocate the image using LdrProcessRelocationBlock
, a function from ntdll.dll
which has the following signature:
IMAGE_BASE_RELOCATION* WINAPI LdrProcessRelocationBlock(ULONG_PTR VA, ULONG SizeOfBlock, PUSHORT NextOffset, LONG_PTR Diff)
However, I am not sure how this could be used to move the image to rebase the image as the 3rd party documentation of this method is scarce. If anyone is familiar with its usage, an example would be highly appreciated.
0 comments:
Post a Comment