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?

4
  • You need to parse imports section of PE (Portable Executable) file (that is .dll or .exe). Commented Jul 31, 2017 at 16:49
  • As Mentioned in the documentation DONT_RESOLVE_DLL_REFERENCES is deprecated and may not be supported in all versions of windows, you'll need to load as a datafile instead. Commented Jul 31, 2017 at 18:11
  • LoadLibraryEx returns nullptr if process already has loaded DLL with the same name this is not true Commented Jul 31, 2017 at 19:25
  • Then why LoadLibraryEx returns nullptr - then why you not call GetLastError or RtlGetLastNtStatus ? Commented Jul 31, 2017 at 19:28

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.