动态加载Windows DLL时,如何判断缺少的依赖模块的名称

时间:2017-03-14 21:34:37

标签: windows dll

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

1 个答案:

答案 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_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> %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.DLLB.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.DLLB.DLL导入 SomeFunc ,但B.DLL不导出

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