windows用enumprocessmodules+getmodulefilenameex获取模块路径,需注意权限和api选择;linux解析/proc/self/maps过滤.so路径;macos用_dyld_image_count和_dyld_get_image_name;跨平台需考虑时机与符号可见性差异。

Windows下用EnumProcessModules获取模块路径
Windows没有公开API直接列出“所有已加载DLL”,但EnumProcessModules配合GetModuleFileNameEx能可靠拿到当前进程每个模块的完整路径。注意:必须启用SE_DEBUG_NAME权限才能查其他进程,查自身进程则不需要。
-
EnumProcessModules返回的是模块句柄数组,不是路径字符串,漏掉这步转换会得到一堆HMODULE而不知对应哪个DLL - 必须用
GetModuleFileNameEx(不是GetModuleFileName),后者对非主模块常返回空或错误路径 - 需要链接
psapi.lib,且Win10之后建议用EnumProcessModulesEx并传入LIST_MODULES_ALL标志,否则可能漏掉某些64位/32位混合加载的模块 - 示例关键片段:
DWORD needed;<br>EnumProcessModules(hProcess, nullptr, 0, &needed);<br>HMODULE* modules = new HMODULE[needed / sizeof(HMODULE)];<br>EnumProcessModules(hProcess, modules, needed, &needed);<br>for (int i = 0; i WCHAR path[MAX_PATH];<br> GetModuleFileNameEx(hProcess, modules[i], path, MAX_PATH);<br> // path即DLL绝对路径<br>}
Linux下读取/proc/self/maps解析SO路径
Linux没有系统级API枚举已加载共享库,标准做法是解析/proc/self/maps,过滤出带.so后缀且权限含x(可执行)或p(私有)的行,再提取第6列路径字段。
- 不是所有映射都是SO文件——匿名映射、堆、栈、VDSO等都混在里面,只靠
strstr(line, ".so")会误判(比如路径含so字符串但不是SO) - 第6列可能为空(如vvar/vdso),需跳过空字段;也可能为
[stack]或[heap],这些不是SO - 同一SO可能被mmap多次(如不同权限段),应去重,按路径字符串判等即可
- 示例判断逻辑:
if (fields.size() >= 6 && !fields[5].empty() && fields[5].find(".so") != std::string::npos && fields[5][0] == '/') {<br> so_paths.insert(fields[5]);<br>}
macOS用_dyld_image_count遍历Mach-O镜像
macOS用_dyld_image_count和_dyld_get_image_name组合获取所有已加载镜像路径,这是Apple官方支持的方式,比解析/proc更稳定(macOS没/proc/self/maps)。
-
_dyld_image_count返回的是所有镜像总数,包括主程序、所有dylib、bundle甚至插入的插件,不区分是否为动态库 - 需用
strcasestr(name, ".dylib")或检查扩展名过滤,仅靠strstr可能漏掉.framework/Versions/A/xxx这类路径 - 注意:该函数在程序启动早期(如构造函数中)可能不可用,应在
main或之后调用 - 无需额外链接库,头文件只需
<mach-o></mach-o>
跨平台封装要注意符号可见性与时机问题
若想写一套跨平台代码统一获取模块列表,不能简单宏开关拼凑,因为各系统加载时机和符号可见性差异很大。
- Windows的
EnumProcessModules在DLL的DLL_PROCESS_ATTACH阶段调用可能失败——此时模块表尚未完全初始化 - Linux下
/proc/self/maps在main之前读取可能为空,尤其用了LD_PRELOAD时,部分SO在main之后才加载 - macOS的
_dyld_image_count在__attribute__((constructor))里可用,但此时某些dylib可能还未完成rebase,路径虽存在但内容未就绪 - 最稳妥的时机是
main函数入口之后、任何自定义初始化完成前
实际用的时候,别指望一次调用就拿到“全部”——尤其是热加载、dlopen、插件机制介入后,列表随时变化。真正需要全量快照的场景,得配合定时轮询或注入钩子,那又是另一层复杂度了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











