You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否通过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,表示符号加载失败,因为符号路径配置或模块符号加载逻辑有误

核心修正点

  1. 替换SymInitialize的句柄
    把DLL模块句柄换成目标进程的句柄(可通过OpenProcess获取,需申请PROCESS_QUERY_INFORMATION | PROCESS_VM_READ权限)

  2. 调整符号选项
    移除SYMOPT_PUBLICS_ONLY选项——该选项仅加载导出的公共符号,无法访问未导出的全局变量

  3. 显式加载模块符号
    调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:06:01