C++:获取本机DLL依赖关系,但不加载进程

问题描述:

我需要以编程方式获取DLL的依赖关系列表。这里是我试图解决这个任务:C++:获取本机DLL依赖关系,但不加载进程

BSTR GetDllDependencies(const wchar_t* dllPath) 
{ 
    std::wstring dependencies; 

    struct LibDeleter 
    { 
     typedef HMODULE pointer; 
     void operator()(HMODULE hMod) { FreeLibrary(hMod); } 
    }; 

    auto hModRaw = LoadLibraryExW(dllPath, NULL, DONT_RESOLVE_DLL_REFERENCES); //(*)nullptr nere 
    auto hMod = std::unique_ptr<HMODULE, LibDeleter>(); 

    auto imageBase = (DWORD_PTR)hMod.get(); 

    auto header = ImageNtHeader(hMod.get()); 
    auto importRVA = header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; 
    auto importTable = (PIMAGE_IMPORT_DESCRIPTOR)(DWORD_PTR)(importRVA + imageBase); 

    while (importRVA && importTable->OriginalFirstThunk) 
    { 
     auto importedModuleName = (char*)(DWORD_PTR)(importTable->Name + imageBase); 
     dependencies 
      .append(importedModuleName, importedModuleName + std::strlen(importedModuleName)) 
      .append(L","); 

     importTable++; 
    } 

    auto result = SysAllocString(dependencies.c_str()); 

    return result; 
} 

它的工作原理。但是,正如你所看到的,它会将DLL加载到进程中。我在这个地方遇到了问题:LoadLibraryEx返回nullptr如果进程已经加载了具有相同名称的DLL。

nullptr

我不知道是不是允许两个DLL名称相同(但不同的位置)加载到同一个进程?我相信是的。那么为什么LoadLibraryEx返回nullptr?有没有可能在不加载DLL的情况下得到DLL的依赖关系?

+0

您需要解析PE(可移植可执行文件)文件(即.dll或.exe)的导入部分。 – VTT

+0

如文档['DONT_RESOLVE_DLL_REFERENCES'已被弃用,并且在所有版本的Windows中可能不支持](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v = vs。 85).aspx),您需要将其作为数据文件加载。 – Mgetz

+0

'LoadLibraryEx返回nullptr,如果进程已经加载了DLL的同名'这不是真的 – RbMm

将该溶液使用DLL文件内手动导航。解决方案的基础是RVAtoRAW函数,它将RVA地址转换为RAW文件(文件内的地址)。

//Defining in which section particular RVA address actually located (section number) 
DWORD RVAtoRAW(DWORD rva, PIMAGE_SECTION_HEADER sectionHeaderRAW, WORD sectionsCount) 
{ 
    int sectionNo; 
    for (sectionNo = 0; sectionNo < sectionsCount; ++sectionNo) 
    { 
     auto sectionBeginRVA = sectionHeaderRAW[sectionNo].VirtualAddress; 
     auto sectionEndRVA = sectionBeginRVA + sectionHeaderRAW[sectionNo].Misc.VirtualSize; 
     if (sectionBeginRVA <= rva && rva <= sectionEndRVA) 
      break; 
    } 
    //Evaluating RAW address from section & RVA 
    auto sectionRAW = sectionHeaderRAW[sectionNo].PointerToRawData; 
    auto sectionRVA = sectionHeaderRAW[sectionNo].VirtualAddress; 
    auto raw = sectionRAW + rva - sectionRVA; 

    return raw; 
} 

BSTR GetDllDependencies(const wchar_t* dllPath) 
{ 
    auto buffer = ReadFile(dllPath); 
    if (buffer.empty()) 
     return SysAllocString(L""); 

    //RAW - offset from beginnig of the file (absolute "address" within file) 
    auto baseRAW = buffer.data(); 
    auto dosHeaderRAW = (PIMAGE_DOS_HEADER)baseRAW; 
    auto peHeaderRAW = (PIMAGE_NT_HEADERS)(baseRAW + dosHeaderRAW->e_lfanew); 
    auto sectionHeaderRAW = (PIMAGE_SECTION_HEADER)(baseRAW + dosHeaderRAW->e_lfanew + sizeof(IMAGE_NT_HEADERS)); 

    auto sectionsCount = peHeaderRAW->FileHeader.NumberOfSections; 

    //RVA - Relative Virtual Address - relative (to ImageBase) address within virtual address space of the process which loads this DLL 
    auto importTableRVA = peHeaderRAW->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; 
    auto importTableRAW = RVAtoRAW(importTableRVA, sectionHeaderRAW, sectionsCount); 
    auto importTable = (PIMAGE_IMPORT_DESCRIPTOR)(baseRAW + importTableRAW); 

    std::wstring dependencies; 
    while (importTableRVA && importTable->OriginalFirstThunk) 
    { 
     auto nameRAW = RVAtoRAW(importTable->Name, sectionHeaderRAW, sectionsCount); 

     auto importedModuleName = (char*)(DWORD_PTR)(nameRAW + baseRAW); 
     dependencies 
      .append(importedModuleName, importedModuleName + std::strlen(importedModuleName)) 
      .append(L","); 

     importTable++; 
    } 

    auto result = SysAllocString(dependencies.c_str()); 

    return result; 
} 

你会发现这篇文章由马特Pietrek有趣。具体来说,看一下IMAGE_IMPORT_DESCRIPTOR数组中的段落“PE文件导入”

Peering Inside the PE: A Tour of the Win32 Portable Executable File Format