GetModuleHandle(NULL)获取当前DLL模块基址?

做一项目想在DLL内部代码实现获取本DLL的模块基址,而且不知道本DLL名称

最简单的方法是想到GetModuleHandle(NULL),是否可以呢?

//自己尝试了一下:

//DLL代码(testDll):
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        {
            HMODULE hModule = GetModuleHandle(NULL);
            WCHAR wszhModule[MAX_PATH] = {0};
            swprintf_s(wszhModule, L"DLL里调用GetModuleHandle(NULL)获取到的地址为:0x%x", \
                (DWORD)hModule);
            MessageBoxW(0, wszhModule, L"提示", 0);
        }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
//EXE代码(test):

int _tmain(int argc, _TCHAR* argv[])
{
    LoadLibrary(L"testDll");

    return 0;
}

在这里插入图片描述

结论:

在DLL内部调用GetModuleHandle(NULL),获取的是主模块(EXE)基址
其实很好理解GetModuleHandle(NULL)最终是在进程空间被调用的

<think>我们正在处理一个关于x32dll内存加载异常的问题,具体是缺少RtlAddFunctionTable函数。这个函数通常用于异常处理(SEH)中注册函数表,以便在发生异常时系统可以找到正确的处理程序。在内存加载DLL时,由于DLL不是通过常规方式加载(例如,不是通过LoadLibrary),因此需要手动处理异常处理相关的注册。解决方案思路:1.理解问题:当我们在内存中加载DLL(而不是通过系统加载器从文件加载)时,我们需要手动设置异常处理所需的信息。RtlAddFunctionTable函数用于向系统注册一个函数表(该表包含异常处理信息)。如果我们的系统版本(如某些Windows版本)没有提供这个函数,或者我们无法链接到该函数(例如在早期版本的Windows中),我们需要寻找替代方案。2.替代方案:我们可以尝试使用RtlInstallFunctionTableCallback函数(如果可用)来注册异常处理。另外,我们也可以手动构建异常处理信息并注册,但这比较复杂。3.检查Windows版本:由于不同版本的Windows提供的函数可能不同,我们需要根据当前操作系统的版本选择合适的方法。4.内存加载DLL的关键步骤:在内存加载DLL时,我们需要模拟PE加载器的行为,包括重定位、导入表处理等,同时还要处理异常处理(SEH)的注册。具体修改方法:步骤1:在代码中动态获取RtlAddFunctionTable函数的地址。如果该函数不可用(例如在WindowsXP等较老系统上),则尝试使用RtlInstallFunctionTableCallback。步骤2:使用RtlInstallFunctionTableCallback的示例(如果可用):这个函数需要传入一个回调函数,当系统需要函数表信息时,会调用这个回调函数来获取。步骤3:如果上述两个函数都不可用,则可能需要手动设置异常处理信息。这涉及到直接操作线程环境块(TEB)中的异常处理链表。但这种方法比较复杂且不稳定,一般不推荐。这里提供一个使用RtlInstallFunctionTableCallback的示例代码(注意:需要根据你的实际代码调整):首先,定义函数指针类型:```ctypedefBOOL(WINAPI*RtlInstallFunctionTableCallbackFunc)(DWORD_PTRTableIdentifier,DWORD_PTRBaseAddress,DWORDLength,PGET_RUNTIME_FUNCTION_CALLBACKCallback,PVOIDContext,PCWSTROutOfProcessCallbackDll);typedefPVOID(WINAPI*RtlLookupFunctionEntryFunc)(DWORD64ControlPc,PDWORD64ImageBase,PUNWIND_HISTORY_TABLEHistoryTable);```然后,在内存加载DLL并完成重定位和导入表处理后,进行异常处理注册:```c//假设我们有一个全局变量来存储DLL的基地址和大小DWORD_PTRdllBase=(DWORD_PTR)lpDllData;//lpDllData是内存中DLL的起始地址DWORDdllSize=...;//从PE头中获取DLL映像的大小//尝试获取RtlInstallFunctionTableCallback函数HMODULEntdll=GetModuleHandleA("ntdll.dll");RtlInstallFunctionTableCallbackFuncpRtlInstallFunctionTableCallback=(RtlInstallFunctionTableCallbackFunc)GetProcAddress(ntdll,"RtlInstallFunctionTableCallback");if(pRtlInstallFunctionTableCallback){//使用RtlInstallFunctionTableCallbackBOOLsuccess=pRtlInstallFunctionTableCallback((DWORD_PTR)dllBase|0x3,//标识符,低两位必须为3(表示使用回调)dllBase,dllSize,(PGET_RUNTIME_FUNCTION_CALLBACK)RtlLookupFunctionEntry,//回调函数,通常使用RtlLookupFunctionEntryNULL,NULL);if(!success){//处理错误}}else{//尝试使用RtlAddFunctionTable(如果可用)RtlAddFunctionTableFuncpRtlAddFunctionTable=(RtlAddFunctionTableFunc)GetProcAddress(ntdll,"RtlAddFunctionTable");if(pRtlAddFunctionTable){//获取函数表(从PE头中获取)PIMAGE_RUNTIME_FUNCTION_ENTRYpFunctionTable=...;//从PE头中获取异常处理目录(通常是IMAGE_DIRECTORY_ENTRY_EXCEPTION)DWORDentryCount=...;//函数表的条目数if(pFunctionTable&&entryCount>0){BOOLsuccess=pRtlAddFunctionTable(pFunctionTable,entryCount,dllBase);if(!success){//处理错误}}}else{//对于非常老的系统(如WindowsXPSP0),可能两个函数都没有,那么可能需要手动注册异常处理。//但请注意,手动注册异常处理非常复杂且容易出错,通常不推荐。//在这种情况下,可以考虑不注册异常处理,但这可能导致DLL内部发生异常时无法正确处理。//或者,可以尝试使用其他内存加载库(如MemoryModule)中处理异常的方法。}}```注意:以上代码需要包含Windows头文件,并链接ntdll.lib(或使用GetModuleHandle和GetProcAddress动态获取函数)。另外,获取函数表(异常目录)的方法:从PE文件头中,我们可以获取到异常目录(IMAGE_DIRECTORY_ENTRY_EXCEPTION)的地址和大小。在内存加载DLL后,这个目录的地址需要根据加载基址进行调整(重定位)。步骤4:在卸载DLL时,需要注销函数表。可以使用RtlDeleteFunctionTable函数(如果之前使用RtlAddFunctionTable)进行删除。对于使用RtlInstallFunctionTableCallback注册的,可能不需要显式删除(当模块卸载时系统会自动处理?),但具体需要查阅文档。注意事项:-这种方法主要针对32位DLL(x86),因为64位DLL(x64)的异常处理机制不同(使用展开信息)。-在32位系统中,异常处理主要依赖于FS:[0]链表(结构化异常处理链)。而函数表(用于展开)在32位中是可选的,但在64位中是必须的。因此,对于32位DLL,如果DLL内部没有使用异常处理,可能可以省略这一步。但为了安全起见,最好还是注册。参考引用:[^1]:关于RtlAddFunctionTable和RtlInstallFunctionTableCallback的文档可以在MSDN中找到,但请注意这些函数是未公开文档的,因此需要谨慎使用。相关问题:1.如何从PE文件头中获取异常处理目录(函数表)?2.在32位系统中,手动注册异常处理链的方法是什么?3.内存加载DLL时,如何处理重定位和导入表?4.在哪些Windows版本中RtlAddFunctionTable函数不可用?5.内存加载DLL后,如何正确卸载并清理资源(包括异常处理注册)?由于问题复杂,建议使用现有的成熟内存加载库(如MemoryModule)作为参考或直接使用,它们已经处理了这些细节。</think>### 解决 x32dll 内存加载异常(缺少 RtlAddFunctionTable 函数) #### 问题分析 在内存加载 32 位 DLL 时,`RtlAddFunctionTable` 函数缺失会导致异常,因为该函数负责向系统注册异常处理信息(SEH)。当 DLL 被内存加载(而非通过 `LoadLibrary` 加载)时,系统无法自动处理异常处理表,需手动注册。 --- ### 解决方案与修改方法 #### 方法 1:动态获取函数地址(推荐) 通过 `GetProcAddress` 动态获取 `RtlAddFunctionTable` 地址,兼容不同 Windows 版本: ```c #include <Windows.h> typedef BOOL(WINAPI* RtlAddFunctionTableFunc)( PVOID FunctionTable, DWORD EntryCount, DWORD_PTR BaseAddress ); BOOL FixFunctionTable(DWORD_PTR dllBase) { // 从 ntdll 获取函数地址 HMODULE ntdll = GetModuleHandleA("ntdll.dll"); if (!ntdll) return FALSE; RtlAddFunctionTableFunc pRtlAddFunctionTable = (RtlAddFunctionTableFunc)GetProcAddress(ntdll, "RtlAddFunctionTable"); if (!pRtlAddFunctionTable) return FALSE; // 从 PE 头获取异常目录 PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)dllBase; PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(dllBase + dosHeader->e_lfanew); DWORD exceptionDirRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; DWORD exceptionDirSize = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; if (exceptionDirRVA && exceptionDirSize) { PIMAGE_RUNTIME_FUNCTION_ENTRY funcTable = (PIMAGE_RUNTIME_FUNCTION_ENTRY)(dllBase + exceptionDirRVA); DWORD entryCount = exceptionDirSize / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY); // 注册异常处理表 return pRtlAddFunctionTable(funcTable, entryCount, dllBase); } return TRUE; // 无异常目录则跳过 } ``` #### 方法 2:使用 `RtlInstallFunctionTableCallback`(备选) 若目标系统不支持 `RtlAddFunctionTable`(如 WinXP SP0),改用回调方式注册: ```c typedef BOOL(WINAPI* RtlInstallFunctionTableCallbackFunc)( DWORD_PTR TableIdentifier, DWORD_PTR BaseAddress, DWORD Length, PGET_RUNTIME_FUNCTION_CALLBACK Callback, PVOID Context, PCWSTR OutOfProcessCallbackDll ); BOOL InstallCallback(DWORD_PTR dllBase, DWORD dllSize) { HMODULE ntdll = GetModuleHandleA("ntdll.dll"); RtlInstallFunctionTableCallbackFunc pRtlInstall = (RtlInstallFunctionTableCallbackFunc)GetProcAddress(ntdll, "RtlInstallFunctionTableCallback"); if (pRtlInstall) { return pRtlInstall( (DWORD_PTR)dllBase | 0x3, // 标识符(低2位必须为3) dllBase, dllSize, RtlLookupFunctionEntry, // 系统回调函数 NULL, NULL ); } return FALSE; } ``` #### 方法 3:手动注册异常链(仅限 32 位) 在极端情况下直接操作线程环境块(TEB): ```c #pragma pack(push, 1) typedef struct _EXCEPTION_REGISTRATION_RECORD { struct _EXCEPTION_REGISTRATION_RECORD* Next; PEXCEPTION_ROUTINE Handler; } EXCEPTION_REGISTRATION_RECORD; #pragma pack(pop) void ManualSEHRegistration(DWORD_PTR dllBase) { EXCEPTION_REGISTRATION_RECORD* pNewRecord = (EXCEPTION_REGISTRATION_RECORD*)malloc(sizeof(EXCEPTION_REGISTRATION_RECORD)); // 设置自定义异常处理函数 pNewRecord->Handler = (PEXCEPTION_ROUTINE)(dllBase + YourExceptionHandlerRVA); // 插入到 TEB 异常链头部 NT_TIB* pTib = (NT_TIB*)NtCurrentTeb(); pNewRecord->Next = pTib->ExceptionList; pTib->ExceptionList = pNewRecord; } ``` --- ### 关键注意事项 1. **基址校验** DLL 的加载基址必须匹配 PE 头的 `ImageBase`,否则需先处理重定位。 2. **系统兼容性** - `RtlAddFunctionTable`:WinXP SP1+ 及现代 Windows - `RtlInstallFunctionTableCallback`:WinXP SP0+(需动态检测) 3. **卸载清理** 内存卸载 DLL 时需调用 `RtlDeleteFunctionTable` 释放资源。 4. **异常目录验证** 通过 `CFF Explorer` 等工具确认 DLL 是否包含 `IMAGE_DIRECTORY_ENTRY_EXCEPTION` 目录。 > 提示:完整实现可参考开源项目 [MemoryModule](https://github.com/fancycode/MemoryModule) 或 [Kernel-Memory-Hacker](https://github.com/AxtMueller/Windows-Kernel-Programmer) 中的 SEH 处理模块[^1]。 --- ### 相关问题 1. 如何在内存加载 DLL 时处理重定位和导入表? 2. Windows 异常处理机制(SEH)的工作原理是什么? 3. 如何检测不同 Windows 版本对 `RtlAddFunctionTable` 的支持? 4. 内存加载 DLL 后如何正确卸载并释放资源? 5. 如何编写自定义异常处理函数(`PEXCEPTION_ROUTINE`)? [^1]: 实现细节参考 Microsoft 未公开文档及开源逆向工程成果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值