动态加载 windows dll 时,如何分辨缺少的依赖模块的名称



我有时会遇到动态库无法在客户站点加载的问题。 这通常是因为他们的系统配置错误。 我需要能够获取缺少的依赖模块的名称,以便我可以记录它,并使修复他们的系统变得更加容易。 我怎样才能做到这一点? 请注意,我需要可以在代码中输入答案,这意味着我无法使用依赖关系检查器或进程监视器或任何其他工具来解决问题。 我确实需要一种方法来编程地做到这一点。 依赖关系检查器可以做到这一点的事实意味着有一种方法。

从win7开始ntdll.dll导出下一个API:

struct FAILUREDATA 
{
NTSTATUS status;
WCHAR DllName[0x20];
WCHAR FunctionName[0x20];
};
extern "C" NTSYSCALLAPI FAILUREDATA* NTAPI LdrGetFailureData();

ntdll.dll2 种情况下的Ldr 子系统日志失败 -GetProcAddress失败(在这种情况下FunctionName填充)或 DLL 加载失败。 但有一个例外 -如果未找到顶级(即您在调用中使用的LibFileNameLoadLibrary[Ex]- 则未记录失败。 但是,如果未找到依赖 DLL(或初始化失败) - 将记录此错误,并在FAILUREDATA.DllName中记录依赖DLL的名称(如果它超过 31 符号 - 它将被截断) - 在这种情况下的通常状态STATUS_DLL_NOT_FOUNDSTATUS_DLL_INIT_FAILED。此外,如果找到 top-dll 但初始化失败 - 这也将被记录。如果在 DLL 加载期间无法解析某些函数 - 在这种情况下,FunctionName将是有效且通常的状态 -STATUS_ENTRYPOINT_NOT_FOUNDSTATUS_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当然要实现您的真实日志记录

失败后你打电话给OnLdrFailLoadLibraryW这样说。

#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>

  • 如果DllMainB.DLL返回 FALSE

    c0000142 loaded DLL <A.DLL> fail DLL <B.DLL>

  • 如果DllMainA.DLL返回 FALSE

    c0000142 loaded DLL <A.DLL> fail DLL <A.DLL>

  • 如果A.DLLB.DLL导入SomeFuncB.DLL不导出它

    c0000139 loaded DLL <A.DLL> fail DLL <Unknown> SomeFunc

最新更新