Basic-Dynamic-API-Resolution
Basic-Dynamic-API-Resolution
What is it?
Basec runtime API resolution using GetProcAddress and GetModuleHandleA. Instead of importing VirtualAlloc at compile time — which places it in the PE’s Import Address Table (IAT) and makes the dependency immediately visible to static analysis — the function is looked up at runtime and stored in a local function pointer. The call goes through that pointer rather than through an import thunk.
How it works
Static import (what we avoid)
Compiler sees: VirtualAlloc(…) Linker generates an IAT entry: [kernel32.dll!VirtualAlloc] PE loader fills the IAT at load time with the real VA
The limitation: GetProcAddress string in .rdata — and what the next step is (API hashing)
Dynamic resolution (what we do)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GetModuleHandleA("Kernel32.dll")
→ returns the base address of kernel32.dll
→ kernel32 is loaded in every Windows process so this always succeeds
→ does NOT increment the DLL's reference count (vs LoadLibraryA which does)
GetProcAddress(hKernel32, "VirtualAlloc")
→ walks kernel32.dll's export directory
→ matches "VirtualAlloc" by name → returns the function's VA
→ identical to what the PE loader does at startup — just done on demand
pVirtualAlloc = (FpVirtualAlloc)result
→ stored in a local function pointer variable
pVirtualAlloc(NULL, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
→ call goes through a register: call rax (or similar)
→ no IAT entry, no import thunk
→ static analysis of the PE imports list shows nothing
dynamic-res.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <Windows.h>
#include <stdio.h>
/*
Function pointer typedef for VirtualAlloc — mirrors the original WinAPI signature
lpAddress -> preferred base address (NULL = let OS choose)
dwSize -> number of bytes to allocate
flAllocationType-> MEM_COMMIT | MEM_RESERVE, etc.
flProtect -> page protection (PAGE_READWRITE, PAGE_EXECUTE_READWRITE, etc.)
*/
typedef LPVOID(WINAPI* FpVirtualAlloc)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
int main() {
FpVirtualAlloc pVirtualAlloc = NULL;
LPVOID pBuffer = NULL;
// GetProcAddress walks the export directory of the module and returns the VA of the named export — identical to how the PE loader resolvesimports, but done at runtime instead of load time
pVirtualAlloc = (FpVirtualAlloc)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "VirtualAlloc");
if (!pVirtualAlloc) {
printf("[!] Failed to resolve VirtualAlloc \n");
return -1;
}
printf("[+] VirtualAlloc resolved at: 0x%p \n", pVirtualAlloc);
// Call VirtualAlloc through the function pointer
pBuffer = pVirtualAlloc(NULL, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pBuffer) {
printf("[!] VirtualAlloc Failed: %d \n", GetLastError());
return -1;
}
printf("[+] Allocated 1024 bytes at: 0x%p \n", pBuffer);
printf("[#] Press <Enter> To Exit ... \n");
getchar();
return 0;
}
This post is licensed under
CC BY 4.0
by the author.
