【问题标题】:How to determine a windows executables DLL dependencies programmatically?如何以编程方式确定 Windows 可执行文件 DLL 依赖项?
【发布时间】:2010-10-10 11:33:54
【问题描述】:

如何使用编程方法确定二进制文件依赖于哪个 DLL?

需要明确的是,我不是试图确定正在运行的 exec 的 DLL 依赖关系,而是任何任意 exec(可能缺少所需的 DLL)的依赖关系。我正在寻找在 C/C++ 应用程序中实现的解决方案。这是我的应用程序需要在运行时完成的事情,第三方应用程序无法完成(比如依赖)。

【问题讨论】:

标签: c++ windows dll dependencies executable


【解决方案1】:

看看IMAGE_LOAD_FUNCTION API。它将返回一个指向LOADED_IMAGE 结构的指针,您可以使用它来访问PE 文件的各个部分。

您可以找到一些描述结构布局方式的文章herehere。您可以下载文章here的源代码。

我认为这应该可以为您提供所需的一切。

更新:

我刚刚下载了这篇文章的源代码。如果您打开EXEDUMP.CPP 并查看DumpImportsSection,它应该有您需要的代码。

【讨论】:

  • 感谢您的建议。特别是对于源示例的链接。正是我想要的。
【解决方案2】:

这无法确定。至少不是没有大量的工作。任何二进制文件都可以调用 LoadLibrary 来加载 DLL。即使您要扫描对 LoadLibrary 的所有调用的代码,您也必须确定用于标识库的字符串。追踪字符串在动态内存中的位置比您想要解决的要困难。

【讨论】:

  • 为什么要在这里投票给我?据我所知,这个答案在技术上是准确的。
  • 我对为什么这个答案被否决的猜测:它不区分隐式依赖项(可以确定,参见 alex2k8 的链接)和显式依赖项(这就是你所说的)。别灰心,答案对了一半!
  • @jdigital:这个答案不正确。这是一个问题的正确答案,要求以编程方式查找可执行映像的所有依赖项。问题是针对一般情况,因此在实例化 COM 对象时,您必须考虑使用LoadLibrary 或隐式加载的模块的运行时动态链接。 Dependency Walker 有一个分析模式,它试图解释在运行时加载的二进制文件,但你不能保证 100% 的代码覆盖率,所以即使这种尝试也行不通。不管你喜不喜欢,这是正确的答案。
【解决方案3】:

基于pedump 代码的 76 行代码(不要忘记添加 Imagehlp.lib 作为依赖项):

#include <stdio.h>
#include "windows.h" //DONT REMOVE IT
#include "ImageHlp.h"
#include "stdafx.h"

template <class T> PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, T* pNTHeader) // 'T' == PIMAGE_NT_HEADERS 
{
    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
    unsigned i;

    for ( i=0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )
    {
        // This 3 line idiocy is because Watcom's linker actually sets the
        // Misc.VirtualSize field to 0.  (!!! - Retards....!!!)
        DWORD size = section->Misc.VirtualSize;
        if ( 0 == size )
            size = section->SizeOfRawData;

        // Is the RVA within this section?
        if ( (rva >= section->VirtualAddress) && 
             (rva < (section->VirtualAddress + size)))
            return section;
    }

    return 0;
}

template <class T> LPVOID GetPtrFromRVA( DWORD rva, T* pNTHeader, PBYTE imageBase ) // 'T' = PIMAGE_NT_HEADERS 
{
    PIMAGE_SECTION_HEADER pSectionHdr;
    INT delta;

    pSectionHdr = GetEnclosingSectionHeader( rva, pNTHeader );
    if ( !pSectionHdr )
        return 0;

    delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
    return (PVOID) ( imageBase + rva - delta );
}


void DumpDllFromPath(wchar_t* path) {
    char name[300];
    wcstombs(name,path,300);

    PLOADED_IMAGE image=ImageLoad(name,0);

    if (image->FileHeader->OptionalHeader.NumberOfRvaAndSizes>=2) {
        PIMAGE_IMPORT_DESCRIPTOR importDesc=
            (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA(
                image->FileHeader->OptionalHeader.DataDirectory[1].VirtualAddress,
                image->FileHeader,image->MappedAddress);
        while ( 1 )
        {
            // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
            if ( (importDesc->TimeDateStamp==0 ) && (importDesc->Name==0) )
                break;

            printf("  %s\n", GetPtrFromRVA(importDesc->Name,
                                           image->FileHeader,
                                           image->MappedAddress) );
            importDesc++;
        }
    }
    ImageUnload(image);

}

//Pass exe or dll as argument 
int _tmain(int argc, _TCHAR* argv[])
{
    DumpDllFromPath(argv[1]);

    return 0;
}

【讨论】:

    【解决方案4】:

    简而言之,您需要扫描 PE 文件的 imports 部分,以查找可执行文件使用的每个 DLL。然后递归定位并扫描每个dll,直到找到所有依赖项。

    当然,应用可以将 LoadLibrary 系列函数用于必需或可选功能。使用此方法不会检测到。

    【讨论】:

      【解决方案5】:

      Dependency Walker 可以通过使用配置文件菜单来做到这一点,如果你有一个目标可执行文件。只需加载可执行文件,告诉它开始分析,它会列出执行程序时加载的所有模块。

      Dependency Walker FAQ (first question...)

      【讨论】:

      • Profiling 只看到加载那些你碰巧触发的模块。除非您可以保证 100% 的代码覆盖率,否则您无法确定找到所有依赖项。然后你还会看到加载的模块,这些模块并不是你的程序严格要求的(例如打开文件保存对话框时的 shell 扩展)。
      【解决方案6】:

      您可以调用一个 DLL 来为您计算所有这些信息并将答案作为 CString 数组传回吗?

      PE Format DLL 可以为您做到这一点。提供源代码,没有 GPL 限制。 PE 文件资源管理器是一个使用 DLL 的 GUI 应用程序,也提供源代码(非 GPL)。

      【讨论】:

        猜你喜欢
        • 2010-10-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-18
        • 1970-01-01
        • 1970-01-01
        • 2016-12-25
        • 2014-03-12
        • 1970-01-01
        相关资源
        最近更新 更多