我有时会遇到动态库无法在客户站点加载的问题。这通常是因为他们的系统配置错误。 我需要能够获取缺少的依赖模块的名称,以便我可以记录它,并使修复系统更加容易。 我怎么能做到这一点? 请注意,我需要一个可以放在代码中的答案,这意味着我无法使用依赖检查器,进程监视器或任何其他工具来解决问题。 我确实需要一种以编程方式完成它的方法。 Dependency Checker可以做到这一点,这意味着有办法。
答案 0 :(得分:1)
从win7 ntdll.dll
导出下一个api开始:
struct FAILUREDATA
{
NTSTATUS status;
WCHAR DllName[0x20];
WCHAR FunctionName[0x20];
};
extern "C" NTSYSCALLAPI FAILUREDATA* NTAPI LdrGetFailureData();
ntdll.dll
Ldr-subsystem 日志失败2例 - GetProcAddress
失败(在这种情况下FunctionName
已填充)或 DLL 加载失败。但有一个例外 - 如果找不到顶层(即你在LoadLibrary[Ex]
调用中使用的 LibFileName ) - 未记录失败。但是如果找不到依赖 DLL (或初始化失败) - 将记录此错误并将{em> DLL 的名称记录在FAILUREDATA.DllName
中(如果更长)超过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> %S\n", 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>
如果来自B.DLL
的 DllMain 返回FALSE
c0000142 loaded DLL <A.DLL> fail DLL <B.DLL>
如果来自A.DLL
的 DllMain 返回FALSE
c0000142 loaded DLL <A.DLL> fail DLL <A.DLL>
如果A.DLL
从B.DLL
导入 SomeFunc ,但B.DLL
不导出
c0000139 loaded DLL <A.DLL> fail DLL <Unknown> SomeFunc