Reputation: 2257
I've been experimenting with ways to obtain the Kernel32.dll base address in C shellcode and came up with the following method. It works on my machine running Windows 11 Pro
, but I'm wondering how robust this method is across different versions of Windows. Here's the code:
#include <intrin.h>
#include <stdint.h>
#include <stdio.h>
#ifdef _WIN64
#define PEB_OFFSET_FROM_GS 0x60
#define PEB __readgsqword(PEB_OFFSET_FROM_GS)
#define LDR_DATA_IN_PEB_OFFSET 24
#define IN_LOAD_ORDER_MODULE_LIST_OFFSET 16
#define DLL_BASE_OFFSET 48
#else
#define PEB_OFFSET_FROM_FS 0x30
#define PEB __readfsdword(PEB_OFFSET_FROM_FS)
#define LDR_DATA_IN_PEB_OFFSET 12
#define IN_LOAD_ORDER_MODULE_LIST_OFFSET 12
#define DLL_BASE_OFFSET 24
#endif
// The sequence of modules loaded into the process typically follows the order:
// [Executable Image], [ntdll.dll], [kernel32.dll].
#define KERNEL32_BASE_ADDRESS *(uintptr_t*)(*(uintptr_t*)(*(uintptr_t*)(*(uintptr_t*)(*(uintptr_t*)(PEB + LDR_DATA_IN_PEB_OFFSET) + IN_LOAD_ORDER_MODULE_LIST_OFFSET))) + DLL_BASE_OFFSET)
int main()
{
printf("Kernel32.dll Base Address: 0x%p\n", (void*)KERNEL32_BASE_ADDRESS);
}
Disassembly of KERNEL32_BASE_ADDRESS with register edx
storing the Kernel32 base address:
...
mov eax,dword ptr fs:[00000030h] ; eax = address of PEB
mov ecx,dword ptr [eax+0Ch] ; ecx = address of Ldr
mov edx,dword ptr [ecx+0Ch] ; edx = first module entry address
mov eax,dword ptr [edx] ; eax = second module entry address
mov ecx,dword ptr [eax] ; ecx = third module entry address
mov edx,dword ptr [ecx+18h] ; edx = kernel32.dll base address
...
Here's a single obfuscated x86/x64 macro
#define KERNEL32_BASE_ADDRESS *(uintptr_t*)(*(uintptr_t*)(*(uintptr_t*)(*(uintptr_t*)(*(uintptr_t*)((sizeof(uintptr_t) == 4 ? __readfsdword(0x30) : __readgsqword(0x60)) + (sizeof(uintptr_t) == 4 ? 12 : 24)) + (sizeof(uintptr_t) == 4 ? 12 : 16)))) + (sizeof(uintptr_t) == 4 ? 24 : 48))
Upvotes: 1
Views: 793