PE导出表--转发函数处理

发布时间 2023-11-11 23:35:58作者: DirWangK

遇事不决,OPENAI

查看导出表信息

E:\IDE\VisualStudio2019\community>dumpbin /exports C:\Windows\System32\version.dll
Microsoft (R) COFF/PE Dumper Version 14.29.30145.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Windows\System32\version.dll

File Type: DLL

  Section contains the following exports for VERSION.dll

    00000000 characteristics
    927B71E6 time date stamp
        0.00 version
           1 ordinal base
          17 number of functions
          17 number of names

    ordinal hint RVA      name

          1    0 00001080 GetFileVersionInfoA
          2    1 00002190 GetFileVersionInfoByHandle
          3    2 00001DF0 GetFileVersionInfoExA
          4    3 00001040 GetFileVersionInfoExW
          5    4 00001010 GetFileVersionInfoSizeA
          6    5 00001E00 GetFileVersionInfoSizeExA
          7    6 00001050 GetFileVersionInfoSizeExW
          8    7 00001060 GetFileVersionInfoSizeW
          9    8 00001070 GetFileVersionInfoW
         10    9 00001E10 VerFindFileA
         11    A 00002360 VerFindFileW
         12    B 00001E20 VerInstallFileA
         13    C 00002F80 VerInstallFileW
         14    D          VerLanguageNameA (forwarded to KERNEL32.VerLanguageNameA)
         15    E          VerLanguageNameW (forwarded to KERNEL32.VerLanguageNameW)
         16    F 00001020 VerQueryValueA
         17   10 00001030 VerQueryValueW

  Summary

        1000 .data
        1000 .pdata
        2000 .rdata
        1000 .reloc
        1000 .rsrc
        3000 .text

可以发现verson.dll有2个转发函数
VerLanguageNameA (forwarded to KERNEL32.VerLanguageNameA)
VerLanguageNameW (forwarded to KERNEL32.VerLanguageNameW)

导出表结构

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

关键判断:
如果functionRVA 位于导出表地址范围内(IMAGE_DATA_DIRECTORY),则该地址指向格式为 <MODULE>.<ExportName> 的转发字符串,

if (arFuncs[i] >= pExportEntry->VirtualAddress && arFuncs[i] < pExportEntry->VirtualAddress+pExportEntry->Size)
{
    //function address is RVA to Forwarder String; e.g. NTDLL.RtlDecodePointer
}
else
{
    //function address is RVA to actual code within current module
}

chatgpt 给的代码
第一次并没有给出正确代码,需要去不断纠正,比如指针运算的错误……

#include <windows.h>
#include <iostream>
#include <string>

FARPROC GetExportFunction(HMODULE Module, LPCSTR Name) {
    if (Module == NULL) {
        std::cerr << "Invalid module handle" << std::endl;
        return NULL;
    }

    if (Name == NULL) {
        std::cerr << "Invalid function name" << std::endl;
        return NULL;
    }

    // 获取导出函数地址
    PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(Module);
    PIMAGE_NT_HEADERS pNTHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<BYTE*>(Module) + pDosHeader->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY pExportDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(reinterpret_cast<BYTE*>(Module) + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

    DWORD* functions = reinterpret_cast<DWORD*>(reinterpret_cast<BYTE*>(Module) + pExportDir->AddressOfFunctions);
    WORD* ordinals = reinterpret_cast<WORD*>(reinterpret_cast<BYTE*>(Module) + pExportDir->AddressOfNameOrdinals);
    DWORD* names = reinterpret_cast<DWORD*>(reinterpret_cast<BYTE*>(Module) + pExportDir->AddressOfNames);

    for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i) {
        const char* functionName = reinterpret_cast<const char*>(reinterpret_cast<BYTE*>(Module) + names[i]);
        if (strcmp(functionName, Name) == 0) {
            // 找到导出函数
            DWORD functionRVA = functions[ordinals[i]];
            if (functionRVA >= pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress &&
                functionRVA < pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) {
                // 转发函数
                const char* forwarderString = reinterpret_cast<const char*>(reinterpret_cast<BYTE*>(Module) + functionRVA);

                // 解析转发字符串,加载相应的 DLL
                std::string strForwarder(forwarderString);
                size_t dotPos = strForwarder.find('.');
                if (dotPos != std::string::npos) {
                    std::string dllName = strForwarder.substr(0, dotPos);
                    std::string functionName = strForwarder.substr(dotPos + 1);

                    HMODULE hForwardModule = LoadLibraryA(dllName.c_str());
                    if (hForwardModule != NULL) {
                        // 递归调用 GetExportFunction 获取导出函数地址
                        FARPROC forwardFunction = GetExportFunction(hForwardModule, functionName.c_str());
                        FreeLibrary(hForwardModule);  // 加载后释放模块
                        return forwardFunction;
                    } else {
                        std::cerr << "Failed to load the forwarded DLL: " << dllName << std::endl;
                        return NULL;
                    }
                } else {
                    std::cerr << "Invalid forwarder string: " << forwarderString << std::endl;
                    return NULL;
                }
            } else {
                // 非转发函数
                return reinterpret_cast<FARPROC>(reinterpret_cast<BYTE*>(Module) + functionRVA);
            }
        }
    }

    return NULL;
}

int main() {
    // 使用示例
    HMODULE hModule = LoadLibraryA("YourLibrary.dll");
    if (hModule != NULL) {
        FARPROC functionAddress = GetExportFunction(hModule, "YourFunctionName");
        if (functionAddress != NULL) {
            // 使用获取的函数地址
            // 例如:reinterpret_cast<YourFunctionType>(functionAddress)(arguments);
        } else {
            std::cerr << "Failed to get the function address" << std::endl;
        }
        FreeLibrary(hModule);
    } else {
        std::cerr << "Failed to load the library" << std::endl;
    }

    return 0;
}

更多信息:
https://ferreirasc.github.io/PE-Export-Address-Table/
https://www.cnblogs.com/revercc/p/16703647.html
https://stackoverflow.com/questions/44279216/how-can-i-resolve-the-forwarded-api-from-the-iat-on-pe

P.S.

AI是大势所趋,"滚滚长江东逝水,浪花淘尽英雄"