能否通过SymFromName获取DLL中未导出全局变量的地址?
问题场景
我在Windows 10环境下实现类调试器功能,DLL中包含C程序,由EXE加载实例化。目前可操作通过__declspec(dllexport)导出的变量与函数,但希望对未导出的全局变量执行同样操作。
通过objdump -t命令可查看目标变量:
[3400](sec 4)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x000000a1 ___notExportedVar
以下是原代码示例:
void getVariable(void* inst, const char* function) { HINSTANCE handle = (HINSTANCE) inst; ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; TCHAR szSymbolName[MAX_SYM_NAME]; _tcscpy_s(szSymbolName, MAX_SYM_NAME, TEXT(function)); BOOL status; SymSetOptions(SYMOPT_UNDNAME|SYMOPT_DEBUG|SYMOPT_DEFERRED_LOADS|SYMOPT_PUBLICS_ONLY); // 设置当前进程的符号路径 SetEnvironmentVariable("_NT_SYMBOLS_PATH", "E:\\path\\to\\exe;E:\\path\\to\\dll;SRV*C:\\WINDOWS\\TEMP*http://msdl.microsoft.com/download/symbols"); //status = SymInitialize(handle, NULL, FALSE); status = SymInitialize(handle, NULL, TRUE); if (status == FALSE) { DWORD lastError = GetLastError(); error(INTERNAL_ERROR, "ERROR: SymInitialize - Windows Error %d - see https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-", lastError); } if (SymFromName(handle, szSymbolName, pSymbol)){ // 后续操作逻辑 }else{ DWORD err = GetLastError(); error(INTERNAL_ERROR, "ERROR: unable to find %s variable in DLL - Windows Error %d - see https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-", function, err); } }
运行时遇到的错误:
- 调用
SymInitialize(handle, NULL, TRUE)返回错误码3221225485 - 若第三个参数传
FALSE,则SymFromName调用失败,错误码126
注:传入的句柄是ProcessExplorer中观察到的DLL句柄
问题分析与解决步骤
错误码本质
- 3221225485即
0xC0000005,属于访问违规,核心原因是SymInitialize的第一个参数必须是进程句柄,而非DLL模块句柄 - 126即
ERROR_MOD_NOT_FOUND,表示符号加载失败,因为符号路径配置或模块符号加载逻辑有误
核心修正点
替换SymInitialize的句柄
把DLL模块句柄换成目标进程的句柄(可通过OpenProcess获取,需申请PROCESS_QUERY_INFORMATION | PROCESS_VM_READ权限)调整符号选项
移除SYMOPT_PUBLICS_ONLY选项——该选项仅加载导出的公共符号,无法访问未导出的全局变量显式加载模块符号
调用SymInitialize后,必须用SymLoadModuleEx显式加载目标DLL的符号,传入DLL的基地址、本地路径等信息
修正后的代码示例
#include <windows.h> #include <dbghelp.h> #include <tchar.h> #pragma comment(lib, "dbghelp.lib") void getVariable(HANDLE hProcess, void* dllBaseAddr, const char* varName) { ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; TCHAR szSymbolName[MAX_SYM_NAME]; _tcscpy_s(szSymbolName, MAX_SYM_NAME, TEXT(varName)); // 设置符号选项:启用未修饰名称、延迟加载,禁用仅加载公共符号 SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEBUG | SYMOPT_DEFERRED_LOADS); // 配置符号路径 SetEnvironmentVariable(TEXT("_NT_SYMBOL_PATH"), TEXT("E:\\path\\to\\exe;E:\\path\\to\\dll;SRV*C:\\WINDOWS\\TEMP*http://msdl.microsoft.com/download/symbols")); // 初始化符号库,传入进程句柄 BOOL status = SymInitialize(hProcess, NULL, FALSE); if (!status) { DWORD lastError = GetLastError(); printf("ERROR: SymInitialize - Windows Error %d\n", lastError); return; } // 显式加载目标DLL的符号 DWORD64 moduleBase = SymLoadModuleEx(hProcess, NULL, TEXT("E:\\path\\to\\your.dll"), NULL, (DWORD64)dllBaseAddr, 0, NULL, 0); if (moduleBase == 0) { DWORD lastError = GetLastError(); printf("ERROR: SymLoadModuleEx - Windows Error %d\n", lastError); SymCleanup(hProcess); return; } // 根据名称查找符号 if (SymFromName(hProcess, szSymbolName, pSymbol)) { // 计算变量实际地址:模块基地址 + 符号偏移 void* varAddr = (void*)(moduleBase + pSymbol->Address); printf("找到变量 %s,地址:%p\n", varName, varAddr); // 这里可添加读写变量的逻辑 } else { DWORD err = GetLastError(); printf("ERROR: 无法在DLL中找到变量 %s - Windows Error %d\n", varName, err); } // 清理符号资源 SymCleanup(hProcess); }
额外注意事项
- 确保目标DLL编译时生成了pdb文件(Debug/Release模式均需启用调试信息)
- 程序需以管理员权限运行,避免进程访问权限不足
- 目标进程与调试器程序的位数必须一致(64位配64位,32位配32位),否则符号查找会失败
内容的提问来源于stack exchange,提问作者Flochocinco
相关产品推荐
相关产品推荐

