非PEB获取ntdll和kernel32模块基址的精妙之道
字数 881 2025-08-29 22:41:10
非PEB获取ntdll和kernel32模块基址的精妙之道
前言
在Windows系统编程和安全研究中,获取关键系统模块(如ntdll.dll和kernel32.dll)的基地址是一项基础但重要的技术。传统方法通过PEB(Process Environment Block)获取模块基址已被广泛使用,但也容易被安全软件检测。本文将详细介绍几种不依赖PEB获取这些关键模块基址的替代方法。
方法一:通过TEB(Thread Environment Block)获取
虽然TEB通常与PEB相关联,但我们可以通过更隐蔽的方式利用它:
// 32位实现
__declspec(naked) HMODULE GetNtdllBaseFromTeb() {
__asm {
mov eax, fs:[0x18] // 获取TEB地址
mov eax, [eax + 0x30] // 获取PEB地址
mov eax, [eax + 0x0C] // 获取PEB_LDR_DATA
mov eax, [eax + 0x1C] // 获取InInitializationOrderModuleList.Flink
mov eax, [eax] // 获取第一个模块(通常是ntdll)
mov eax, [eax + 0x08] // 获取基地址
ret
}
}
// 64位实现
HMODULE GetNtdllBaseFromTeb64() {
PPEB pPeb;
PPEB_LDR_DATA pLdr;
PLDR_DATA_TABLE_ENTRY pEntry;
pPeb = (PPEB)__readgsqword(0x60);
pLdr = (PPEB_LDR_DATA)pPeb->Ldr;
pEntry = (PLDR_DATA_TABLE_ENTRY)pLdr->InInitializationOrderModuleList.Flink;
return (HMODULE)pEntry->DllBase;
}
方法二:通过异常处理链获取
利用SEH(结构化异常处理)机制获取模块基址:
HMODULE GetModuleBaseViaException() {
__try {
// 故意触发异常
*(int*)0 = 0;
}
__except(GetExceptionInformation() ?
((EXCEPTION_POINTERS*)GetExceptionInformation())->ContextRecord->Eip : 0) {
MEMORY_BASIC_INFORMATION mbi;
CONTEXT* ctx = ((EXCEPTION_POINTERS*)GetExceptionInformation())->ContextRecord;
VirtualQuery((void*)ctx->Eip, &mbi, sizeof(mbi));
return (HMODULE)mbi.AllocationBase;
}
return NULL;
}
方法三:通过API调用栈回溯
利用调用栈回溯技术获取调用者模块基址:
HMODULE GetModuleBaseViaStackWalk() {
PVOID addresses[1];
USHORT frames = CaptureStackBackTrace(1, 1, addresses, NULL);
if (frames == 1) {
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(addresses[0], &mbi, sizeof(mbi))) {
return (HMODULE)mbi.AllocationBase;
}
}
return NULL;
}
方法四:通过内存扫描
直接扫描进程内存查找模块特征:
HMODULE FindModuleByMemoryScan(const char* moduleName) {
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
LPBYTE pStart = (LPBYTE)sysInfo.lpMinimumApplicationAddress;
LPBYTE pEnd = (LPBYTE)sysInfo.lpMaximumApplicationAddress;
MEMORY_BASIC_INFORMATION mbi;
while (pStart < pEnd) {
if (VirtualQuery(pStart, &mbi, sizeof(mbi)) == sizeof(mbi)) {
if (mbi.State == MEM_COMMIT && mbi.Type == MEM_IMAGE) {
IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)mbi.AllocationBase;
if (pDos->e_magic == IMAGE_DOS_SIGNATURE) {
IMAGE_NT_HEADERS* pNt = (IMAGE_NT_HEADERS*)((LPBYTE)pDos + pDos->e_lfanew);
if (pNt->Signature == IMAGE_NT_SIGNATURE) {
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(
(LPBYTE)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (pExport) {
char* pName = (char*)((LPBYTE)pDos + pExport->Name);
if (_stricmp(pName, moduleName) == 0) {
return (HMODULE)mbi.AllocationBase;
}
}
}
}
}
pStart += mbi.RegionSize;
}
}
return NULL;
}
方法五:通过未文档化API
使用未公开的API函数获取模块信息:
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
// ... 其他字段
} LDR_MODULE, *PLDR_MODULE;
HMODULE GetModuleBaseUndocumented() {
PPEB pPeb = NtCurrentTeb()->ProcessEnvironmentBlock;
PLDR_MODULE pLdrModule = (PLDR_MODULE)pPeb->Ldr->InLoadOrderModuleList.Flink;
return (HMODULE)pLdrModule->BaseAddress;
}
方法六:通过TEB的静态TLS数组
利用线程本地存储(TLS)数组获取:
HMODULE GetModuleBaseViaTls() {
PTEB pTeb = NtCurrentTeb();
// TLS数组的第4个槽位通常指向kernel32.dll
HMODULE hModule = (HMODULE)pTeb->TlsSlots[3];
return hModule;
}
方法七:通过API哈希搜索
结合内存扫描和API哈希技术:
DWORD HashAPI(const char* apiName) {
DWORD hash = 0;
while (*apiName) {
hash = ((hash << 5) + hash) + *apiName++;
}
return hash;
}
HMODULE FindModuleByAPIHash(DWORD apiHash) {
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
LPBYTE pStart = (LPBYTE)sysInfo.lpMinimumApplicationAddress;
LPBYTE pEnd = (LPBYTE)sysInfo.lpMaximumApplicationAddress;
MEMORY_BASIC_INFORMATION mbi;
while (pStart < pEnd) {
if (VirtualQuery(pStart, &mbi, sizeof(mbi)) == sizeof(mbi)) {
if (mbi.State == MEM_COMMIT && mbi.Type == MEM_IMAGE) {
IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)mbi.AllocationBase;
if (pDos->e_magic == IMAGE_DOS_SIGNATURE) {
IMAGE_NT_HEADERS* pNt = (IMAGE_NT_HEADERS*)((LPBYTE)pDos + pDos->e_lfanew);
if (pNt->Signature == IMAGE_NT_SIGNATURE) {
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(
(LPBYTE)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (pExport) {
DWORD* pNames = (DWORD*)((LPBYTE)pDos + pExport->AddressOfNames);
for (DWORD i = 0; i < pExport->NumberOfNames; i++) {
char* pName = (char*)((LPBYTE)pDos + pNames[i]);
if (HashAPI(pName) == apiHash) {
return (HMODULE)mbi.AllocationBase;
}
}
}
}
}
}
pStart += mbi.RegionSize;
}
}
return NULL;
}
方法八:通过中断门或调用门
利用CPU特权指令获取(需内核权限):
; 32位示例
get_module_base:
push ebp
mov ebp, esp
sidt [ebp-6] ; 获取IDT地址
mov eax, [ebp-4] ; 获取IDT基址
mov eax, [eax+4] ; 获取第一个中断处理程序地址
shr eax, 16 ; 获取段选择子
and eax, 0xFFF8 ; 清除TI和RPL位
mov eax, [eax] ; 获取段描述符
mov eax, [eax+2] ; 获取段基址
leave
ret
方法九:通过Vectored Exception Handling
利用向量化异常处理机制:
HMODULE GetModuleBaseViaVEH() {
PVOID handler = AddVectoredExceptionHandler(1, [](PEXCEPTION_POINTERS pExc) -> LONG {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((void*)pExc->ContextRecord->Eip, &mbi, sizeof(mbi));
pExc->ContextRecord->Eax = (DWORD)mbi.AllocationBase;
return EXCEPTION_CONTINUE_EXECUTION;
});
__try {
__asm int 3; // 触发断点异常
}
__except(EXCEPTION_EXECUTE_HANDLER) {
HMODULE hModule;
__asm mov hModule, eax;
RemoveVectoredExceptionHandler(handler);
return hModule;
}
return NULL;
}
方法十:通过硬件断点
利用调试寄存器获取:
HMODULE GetModuleBaseViaHwBp() {
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
ctx.Dr0 = (DWORD)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
ctx.Dr7 = (1 << 0) | (1 << 2); // 设置DR0为执行断点
SetThreadContext(GetCurrentThread(), &ctx);
__try {
LoadLibraryA("user32.dll"); // 触发硬件断点
}
__except(GetExceptionCode() == EXCEPTION_SINGLE_STEP ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((void*)ctx.Dr0, &mbi, sizeof(mbi));
return (HMODULE)mbi.AllocationBase;
}
return NULL;
}
防御措施与检测方法
了解这些技术后,安全开发者可以采取以下防御措施:
- 内存保护:使用PAGE_GUARD保护关键模块内存区域
- API监控:挂钩VirtualQuery等关键API检测异常调用模式
- 异常监控:监控异常的频率和来源
- 调用栈验证:检查API调用的返回地址是否在预期模块内
- TEB/PEB保护:使用内存加密技术保护关键数据结构
总结
本文详细介绍了10种不依赖PEB获取ntdll和kernel32模块基址的技术,从简单的TEB访问到复杂的硬件断点利用。这些技术在恶意软件分析、反病毒开发和系统安全研究中都有重要应用。理解这些方法不仅有助于开发更安全的软件,也能帮助安全研究人员更好地检测和防御恶意代码。