getmodulehandle(null)在dll内部返回的是主模块(exe)基址,而非当前dll基址;正确方法是使用dllmain入参hinstdll或getmodulehandleex(get_module_handle_ex_flag_from_address, &some_func_in_dll, &hmod)。

Windows下用GetModuleHandle获取DLL基址
在DLL内部想知道自己被加载到哪个地址,最直接的方式是调用 GetModuleHandle(NULL) ——但这返回的是主模块(EXE)基址,不是当前DLL的。正确做法是传入 NULL 以外的参数:对DLL自身,用 GetModuleHandle(L"your.dll");更稳妥的是在DLL入口点(DllMain)里用入参 hinstDLL,它就是该DLL的加载基址。
常见错误是硬编码DLL名或依赖当前目录——路径无关,只认模块名(不含路径,不区分大小写),且必须确保该DLL已由系统加载(比如通过 LoadLibrary 显式加载,或作为依赖被自动解析)。若模块名拼错或尚未加载,GetModuleHandle 返回 NULL。
-
GetModuleHandle不增加引用计数,安全但不可用于后续FreeLibrary - 若需跨线程稳定使用,建议在
DllMain中保存hinstDLL到全局变量 - 调试时可用
GetModuleInformation配合GetModuleHandle查看实际大小和入口点
Linux下用dladdr获取SO基址
Linux没有“模块句柄”概念,但 dladdr 能根据任意函数地址反查所在共享库信息。典型用法:在SO内定义一个导出函数(如 void get_self_base() {}),然后调用 dladdr((void*)&get_self_base, &info),info.dli_fbase 就是该SO的加载基址。
注意:dladdr 依赖glibc的符号表和动态链接器元数据,若SO被 strip 过且无调试段,可能失效;另外,它查的是包含该地址的“第一个”映射区域,对同一SO多个加载实例(如 RTLD_LOCAL 多次加载)不保证唯一性。
- 必须链接
-ldl,且运行时不能禁用动态链接器(如LD_PRELOAD干扰可能导致dladdr返回错误基址) -
info.dli_fname是绝对路径,可用于验证是否为预期SO - 避免传入栈地址或全局变量地址——它们可能落在其他映射段(如
.data或.bss),导致查到主程序而非SO
跨平台方案:编译期确定相对偏移
如果只是需要计算模块内某符号相对于基址的偏移(比如实现重定位或patch),比运行时查基址更可靠的做法是:在编译时把目标符号地址减去模块起始地址(即 &symbol - (uintptr_t)&__executable_start),但 __executable_start 在SO中不可用。可行替代是定义一个节(section)起始符号:
#ifdef _WIN32
#pragma section(".mybase", read)
__declspec(allocate(".mybase")) char mybase_marker;
#else
__attribute__((section(".mybase"))) char mybase_marker;
#endif
然后用 (uintptr_t)&mybase_marker 当作模块逻辑基址——它实际是该节在内存中的地址,对DLL/SO都有效,且无需API调用。
- 该方式不依赖运行时环境,但要求链接器保留该节(MSVC默认保留,GCC需加
-Wl,--no-as-needed防优化掉) - 不同构建配置(PIE/非PIE、ASLR开启与否)下,该地址仍是有效偏移基准
- 不能直接当“加载基址”用于外部重定位,仅适用于模块内部符号寻址
容易忽略的ASLR与重定位影响
现代系统默认启用ASLR,意味着每次加载基址都不同。单纯获取基址没意义,除非你打算做运行时patch或手动解析重定位表。而真正需要基址的场景(如HOOK、插桩、自修改代码),往往还要处理重定位:Windows DLL有重定位表(.reloc),Linux SO若为PIE则也有类似机制。直接硬编码偏移会失败。
例如,用 GetModuleHandle 拿到基址后,若想 patch 某个函数,必须先确认该函数所在页是否可写(VirtualProtect)、是否已被重定位过(否则跳转地址还是原始值);Linux下还需检查 /proc/self/maps 确认该SO映射权限(r-xp 表示不可写)。
- ASLR开启时,
GetModuleHandle和dladdr返回的确实是随机基址,但模块内所有符号偏移固定 - 调试器附加时可能禁用ASLR,导致基址看似“固定”,上线后行为突变
- 64位下指针宽度变化、符号可见性(
__attribute__((visibility("hidden"))))也会影响dladdr查找结果
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











