【发布时间】:2017-06-30 08:47:31
【问题描述】:
我正在尝试从已加载的 DLL 中枚举符号。对于那些感兴趣的人,这是CPPCoverage project 的一部分,对于某些功能,我需要符号数据。
问题分解
当进程启动或加载 DLL 时,需要为一些已计划的新功能枚举符号。
基本上创建了一个进程,使用dbghelp获取符号信息。接下来,使用SymEnumSymbols 迭代符号。发生这种情况有两个时刻:
- 进程启动时 (
CREATE_PROCESS_DEBUG_EVENT) - 加载 DLL 时 (
LOAD_DLL_DEBUG_EVENT)
在 (2) 期间一切正常。但是,在(1)期间不能枚举符号。
行为是一切正常,直到SymEnumSymbols 调用。返回值告诉我有一个错误,但GetLastError 返回成功。另外,回调函数也没有被调用。
更奇怪的是,调用SymGetSymFromName 确实确实有效。
最小测试用例
static BOOL CALLBACK EnumerateSymbols(
PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext)
{
std::cout << "Symbol: " << pSymInfo->Name << std::endl;
return TRUE;
}
void Test()
{
SymSetOptions(SYMOPT_LOAD_ANYTHING);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
auto str = "FullPathToSomeExeWithPDB.exe";
auto result = CreateProcess(str, NULL, NULL, NULL, FALSE,
DEBUG_PROCESS, NULL, NULL, &si, &pi);
if (result == 0)
{
auto err = GetLastError();
std::cout << "Error running process: " << err << std::endl;
return;
}
if (!SymInitialize(pi.hProcess, NULL, FALSE))
{
auto err = GetLastError();
std::cout << "Symbol initialization failed: " << err << std::endl;
return;
}
bool first = false;
DEBUG_EVENT debugEvent = { 0 };
while (!first)
{
if (!WaitForDebugEvent(&debugEvent, INFINITE))
{
auto err = GetLastError();
std::cout << "Wait for debug event failed: " << err << std::endl;
return;
}
if (debugEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
{
auto dllBase = SymLoadModuleEx(
pi.hProcess,
debugEvent.u.CreateProcessInfo.hFile,
str,
NULL,
reinterpret_cast<DWORD64>(debugEvent.u.CreateProcessInfo.lpBaseOfImage),
0,
NULL,
0);
if (!dllBase)
{
auto err = GetLastError();
std::cout << "Loading the module failed: " << err << std::endl;
return;
}
if (!SymEnumSymbols(pi.hProcess, dllBase, NULL, EnumerateSymbols, nullptr))
{
auto err = GetLastError();
std::cout << "Error: " << err << std::endl;
}
first = true;
}
}
// cleanup code is omitted
}
【问题讨论】:
-
好的。你正在打电话给
GetLastError太晚了。您需要在满足条件后立即调用它,并记录它以返回有意义的值。不要将其与任何其他代码穿插。 -
@IInspectable 测试用例...通常我只是抛出一个异常。无论如何,我只是修改它以立即调用它。不会改变任何事情...
-
我想CREATE_PROCESS_DEBUG_EVENT 是在任何模块被加载之前引发的:“系统在进程开始在用户模式下执行之前生成此调试事件”。 Why do I get ERROR_INVALID_HANDLE from GetModuleFileNameEx when I know the process handle is valid? 似乎相关。
-
@IInspectable - 当
CREATE_PROCESS_DEBUG_EVENT引发时 - 已加载 exe 和 ntdll.dll -
@RbMm:你知道,此时用户态模块信息表是否已经初始化?我的想法是,虽然创建了一个进程对象,并且将最少的部分映射到地址空间,但还没有运行任何用户模式引导代码,模块查询返回空结果列表。