我有时会遇到动态库无法在客户站点加载的问题。 这通常是因为他们的系统配置错误。 我需要能够获取缺少的依赖模块的名称,以便我可以记录它,并使修复他们的系统变得更加容易。 我怎样才能做到这一点? 请注意,我需要可以在代码中输入答案,这意味着我无法使用依赖关系检查器或进程监视器或任何其他工具来解决问题。 我确实需要一种方法来编程地做到这一点。 依赖关系检查器可以做到这一点的事实意味着有一种方法。
从win7开始ntdll.dll
导出下一个API:
struct FAILUREDATA
{
NTSTATUS status;
WCHAR DllName[0x20];
WCHAR FunctionName[0x20];
};
extern "C" NTSYSCALLAPI FAILUREDATA* NTAPI LdrGetFailureData();
ntdll.dll
2 种情况下的Ldr 子系统日志失败 -GetProcAddress
失败(在这种情况下FunctionName
填充)或 DLL 加载失败。 但有一个例外 -如果未找到顶级(即您在调用中使用的LibFileNameLoadLibrary[Ex]
- 则未记录失败。 但是,如果未找到依赖 DLL(或初始化失败) - 将记录此错误,并在FAILUREDATA.DllName
中记录依赖DLL的名称(如果它超过 31 符号 - 它将被截断) - 在这种情况下的通常状态STATUS_DLL_NOT_FOUND
或STATUS_DLL_INIT_FAILED
。此外,如果找到 top-dll 但初始化失败 - 这也将被记录。如果在 DLL 加载期间无法解析某些函数 - 在这种情况下,FunctionName
将是有效且通常的状态 -STATUS_ENTRYPOINT_NOT_FOUND
或STATUS_ORDINAL_NOT_FOUND
不幸的是LdrGetFailureData
不包含在ntdll[p].lib
中 - 因此需要使用GetProcAddress
来获取它。 您可以声明下一个全局数据:
static union {
FAILUREDATA* (NTAPI *LdrGetFailureData)();
PVOID pvLdrGetFailureData;
};
和开始通话
pvLdrGetFailureData = GetProcAddress(GetModuleHandle(L"ntdll"), "LdrGetFailureData");
然后实现下一个函数:
void OnLdrFail(PCWSTR TopDllName)
{
if (LdrGetFailureData)
{
FAILUREDATA* pfd = LdrGetFailureData();
if (NTSTATUS status = pfd->status)
{
DbgPrint("%x loaded DLL <%S> fail DLL <%S> %Sn", status, TopDllName, pfd->DllName, pfd->FunctionName);
}
else
{
// in case loaded(top) DLL not found
DbgPrint("%x loaded DLL <%S>n", GetLastError(), TopDllName);
}
}
}
到位DbgPrint
当然要实现您的真实日志记录
失败后你打电话给OnLdrFail
LoadLibraryW
这样说。
#define CLEAR_FAILURE_DATA() if (LdrGetFailureData) LdrGetFailureData()->status = 0
CLEAR_FAILURE_DATA();
HMODULE hmod = LoadLibraryW(lpLibFileName);
if (!hmod)
{
OnLdrFail(lpLibFileName);
}
因为我怎么说 Ldr 不填充FAILUREDATA
以防找不到lpLibFileName
- 它不清楚这个结构的先前状态 - 所以需要自己做这件事(这里可以保存以前的错误,与当前调用无关)(但是,如果依赖于lpLibFileName
找不到或任何 dll 初始化失败 - 这将被记录)
例如:
A.DLL
依赖于B.DLL
,未找到B.DLL
将是下一个日志c0000135 loaded DLL <A.DLL> fail DLL <B.DLL>
如果DllMain从
B.DLL
返回 FALSEc0000142 loaded DLL <A.DLL> fail DLL <B.DLL>
如果DllMain从
A.DLL
返回 FALSEc0000142 loaded DLL <A.DLL> fail DLL <A.DLL>
如果
A.DLL
从B.DLL
导入SomeFunc但B.DLL
不导出它c0000139 loaded DLL <A.DLL> fail DLL <Unknown> SomeFunc