无法直接获取dll导出函数列表,因windows不维护运行时符号反向映射;必须通过静态解析pe文件导出表实现。

Windows 下用 EnumProcessModules 和 GetProcAddress 无法直接获取 DLL 导出符号
这不是一个“调用某个函数就能列出所有符号”的问题。Windows PE 加载器不会在运行时维护一份完整的、可遍历的导出符号表缓存。你拿到的是模块句柄(HMODULE),它本身不携带符号名列表;GetProcAddress 只能按名查地址,不能反向枚举。
真正可行的路径是:定位每个已加载 DLL 的文件路径 → 解析其 PE 文件头 → 手动读取导出表(Export Directory)→ 提取 Name Pointer Table 和 Ordinal Table → 解析字符串并关联函数 RVA。
常见错误是试图对 GetModuleHandle(NULL)(主 EXE)或任意 HMODULE 直接调用未公开的枚举 API,结果返回空或崩溃——因为系统 API 不提供该能力。
用 EnumProcessModules + GetModuleFileName 获取所有已加载 DLL 路径
这是起点,但要注意:此方法只给出当前进程加载的模块路径,不区分是否为 DLL(EXE 也会被列进来),且无法识别延迟加载或显式 LoadLibrary 后又被 FreeLibrary 卸载的模块(它们已不在模块列表中)。
-
EnumProcessModules需要PROCESS_QUERY_INFORMATION+PROCESS_VM_READ权限,沙盒环境(如 VS 调试器附加时)可能失败 - 模块数组大小需动态获取:
EnumProcessModules(hProcess, NULL, 0, &cbNeeded)先探大小,再分配缓冲区 -
GetModuleFileName对某些系统 DLL(如ntdll.dll)可能返回空字符串,此时应 fallback 到GetMappedFileName或检查\KnownDlls\路径 - 路径不含扩展名或带重定向(如
api-ms-win-core-*.dll)时,仍可正常解析 PE 头——只要文件可读
解析 PE 导出表必须手动读取文件,不能依赖内存镜像
运行时 DLL 在内存中的映像(HMODULE 指向的基址)通常经过重定位、节对齐调整、甚至 ASLR 偏移,而导出表结构(如 IMAGE_EXPORT_DIRECTORY)里的字符串指针指向的是原始文件偏移。直接从内存读会得到乱码或访问违规。
正确做法是:用上一步得到的磁盘路径打开文件(CreateFile + FILE_SHARE_READ),MapViewOfFile 映射为 IMAGE_DOS_HEADER*,然后按 PE 标准结构逐级跳转到导出目录。
- 必须验证
e_lfanew、OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]是否有效,很多 DLL(尤其 .NET 程序集)导出表为空 - 导出名称是 ANSI 字符串,但部分新 DLL 使用 UTF-8 编码(罕见);遇到非 ASCII 名称建议用
MultiByteToWideChar(CP_UTF8, ...)转换 -
AddressOfNames指向的是名字 RVA,不是 VA —— 要减去镜像基址(通常是 0x10000)再加到映射基址上才能取字符串 - 不要忽略
Base字段:导出序号 = 表中索引 +Base,否则GetProcAddress(hMod, "xxx")查不到对应函数
实际工程中更推荐用 dumpbin /exports 或 llvm-readobj --exports 做离线分析
运行时动态枚举所有 DLL 导出符号开销大、易出错、权限受限,且对某些注入 DLL 或内存加载的模块(如用 VirtualAlloc + WriteProcessMemory 手动加载)完全失效。
如果你的目标是调试、逆向或构建符号数据库,优先考虑离线方案:
- 用
dumpbin /exports path odll.dll输出文本,管道解析(注意多语言 Windows 下输出编码可能是 GBK) - 用
llvm-readobj --exports --elf-output-style=gnu dll.dll(LLVM 工具链)输出结构化 JSON,兼容性更好 - 若必须运行时做,封装成独立工具进程(而非注入目标进程),避免权限和稳定性问题
- 注意 UWP 应用、WSL2 中的 Windows 进程、以及启用 CFG/ETW 的场景下,部分模块可能拒绝读取或返回伪造路径
导出符号不是运行时“活数据”,而是 PE 文件静态元信息。想靠 GetCurrentProcess + 几个 API 拿全,本质上是在和 PE 规范和 Windows 加载器博弈——赢面小,代价高。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











