4

I need to programmatically obtain DLL's dependencies list. Here is how I'm trying to solve this task:

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;
}

It works. But, as you can see it loads the DLL into process. And I ran into a problem in this place: LoadLibraryEx returns nullptr if process already has loaded DLL with the same name.

nullptr

I'm not sure is it allowed to load two DLLs with the same name (but different location) into the same process? I believe yes. Then why LoadLibraryEx returns nullptr? Is it possible to somehow get DLLs dependencies without loading DLL?

Dmitry Katkevich
  • 883
  • 7
  • 26
  • You need to parse imports section of PE (Portable Executable) file (that is .dll or .exe). – user7860670 Jul 31 '17 at 16:49
  • As Mentioned in the documentation [`DONT_RESOLVE_DLL_REFERENCES` is deprecated and may not be supported in all versions of windows](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx), you'll need to load as a datafile instead. – Mgetz Jul 31 '17 at 18:11
  • `LoadLibraryEx returns nullptr if process already has loaded DLL with the same name` this is not true – RbMm Jul 31 '17 at 19:25
  • `Then why LoadLibraryEx returns nullptr` - then why you not call `GetLastError` or `RtlGetLastNtStatus` ? – RbMm Jul 31 '17 at 19:28

2 Answers2

1

You will find this article by Matt Pietrek interesting. In particular, take a look at the IMAGE_IMPORT_DESCRIPTOR array in the paragraph "PE File Imports".

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

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
1

This solution use manual navigation within DLL file. The basis of the solution is RVAtoRAW function which translates RVA addresses to RAW ones (addresses within file).

//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;
}
Dmitry Katkevich
  • 883
  • 7
  • 26