C ++: получить собственные зависимости dll, не загружая их в процесс

Мне нужно программно получить список зависимостей 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?


person Dmitry Katkevich    schedule 31.07.2017    source источник
comment
Вам необходимо проанализировать раздел импорта PE-файла (переносимого исполняемого файла) (то есть .dll или .exe).   -  person user7860670    schedule 31.07.2017
comment
Как указано в документации, DONT_RESOLVE_DLL_REFERENCES is устарело и может не поддерживаться во всех версиях Windows, вам нужно будет загрузить вместо этого файл данных.   -  person Mgetz    schedule 31.07.2017
comment
LoadLibraryEx returns nullptr if process already has loaded DLL with the same name это неправда   -  person RbMm    schedule 31.07.2017
comment
Then why LoadLibraryEx returns nullptr - тогда почему бы вам не позвонить GetLastError или RtlGetLastNtStatus?   -  person RbMm    schedule 31.07.2017


Ответы (2)


Вы найдете эту статью Мэтта Пьетрека интересной. В частности, обратите внимание на массив IMAGE_IMPORT_DESCRIPTOR в абзаце «Импорт файлов PE».

Пиринг внутри PE: обзор переносимого формата исполняемых файлов Win32

person Mr.C64    schedule 31.07.2017

Это решение использует ручную навигацию внутри файла 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;
}
person Dmitry Katkevich    schedule 03.08.2017