如何在 64 位 DLL 中查找导出函数的地址?

Posted

技术标签:

【中文标题】如何在 64 位 DLL 中查找导出函数的地址?【英文标题】:How to find exported functions' addresses in 64bit DLLs? 【发布时间】:2013-12-28 17:02:03 【问题描述】:

我正在分析 32 位和 64 位 DLL。我想知道导出函数的地址是什么。我已经处理了 32 位 DLL,但相同的代码不适用于 64 位模块。

    DWORD address = (*module)->getImageBaseAddress();
    DWORD headerAddress = address + ((PIMAGE_DOS_HEADER)address)->e_lfanew;
    PIMAGE_NT_HEADERS header = (PIMAGE_NT_HEADERS)headerAddress;
    PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)(address + header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    PVOID names = (BYTE *)address + exports->AddressOfNames;
    PVOID moduleFunctions = (BYTE *)address + exports->AddressOfFunctions;
    std::cout << "Characteristics: " << exports->Characteristics << endl;
    std::cout << "TimeDateStamp: " << exports->TimeDateStamp << endl;
    std::cout << "Major version: " << exports->MajorVersion << endl;
    std::cout << "Minor version: " << exports->MinorVersion << endl;
    std::cout << "Name: " << exports->Name << endl;
    std::cout << "Base: " << exports->Base << endl;
    std::cout << "Number of fun: " << exports->NumberOfFunctions << endl;
    std::cout << "Number of names: " << exports->NumberOfNames << endl;

    for (int i = 0; i < exports->NumberOfFunctions; i++) 
        std::cout << std::string((char*)((BYTE *)address + ((DWORD *)names)[i])) << " @ " << ((DWORD *)moduleFunctions)[i] << endl;
    

第一个输出行看起来不错(TimeDateStamp 具有正确的值,函数名称已正确解析等)。不幸的是,当我在 DLL 文件分析后将我的函数的图像基偏移量与 IDA 给出的偏移量进行比较时,结果会有所不同。例如。对于第一个模块,我得到的偏移量等于11d0b,由于 IDA,没有有效指令从该地址开始(imageBase + 0x11d0b)。

我在 64 位 DLL 中获取函数地址的方法是否正确?为什么我会得到不同的结果?为什么 32 位模块一切正常?

【问题讨论】:

64 位代码中的指针将不是 32 位。我的意思是 DWORD 不应该工作。 ***.com/questions/4989928/… IMAGE_NT_HEADERS 不再准确。今天还有 IMAGE_NT_HEADERS64。使用 IMAGE_FILE_HEADER.Machine 知道你得到了哪一个。等等。 你能告诉我带有 IMAGE_NT_HEADERS64 的代码应该是什么样子吗?如果我只是用 64 位结构替换 32 位结构,我不会得到想要的结果。 【参考方案1】:

如果您使用 64 位二进制文​​件,optional header 结构的大小会有所不同。官方参考请看PE COFF spec。

您需要关闭可选标题(第一个字段)中的“幻数”,以确定要使用的结构格式。

编辑:添加代码 sn-p 显示选择正确的标题格式:

char* address = (*module)->getImageBaseAddress();
char* headerAddress = address + ((PIMAGE_DOS_HEADER)address)->e_lfanew;
PIMAGE_NT_HEADERS32 header32 = (PIMAGE_NT_HEADERS32)headerAddress;
PIMAGE_NT_HEADERS64 header64 = (PIMAGE_NT_HEADERS64)headerAddress;
PIMAGE_EXPORT_DIRECTORY exports = NULL;

if (header32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) // PE32
    exports = (PIMAGE_EXPORT_DIRECTORY)(address + header32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
else if (header32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) // PE32+
    exports = (PIMAGE_EXPORT_DIRECTORY)(address + header64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
else
    return 0;

另请注意,如果您尝试从文件手动加载的图像(例如文件的原始映射)执行此操作,则需要通过查找内存地址将 RVA 转换为文件偏移量部分列表以识别文件指针位置。

【讨论】:

您认为带有 IMAGE_NT_HEADERS64 的 64 位 DLL 的代码如何?我写了一些,但它仍然不能正常工作。 PIMAGE_NT_HEADERS 是 PIMAGE_NT_HEADERS32 或 PIMAGE_NT_HEADERS64 的 typedef,具体取决于是否定义了 _WIN64。那么这有什么必要呢? @jhoffman0x 因为您不一定要尝试获取有关您自己的图像的信息。无论您如何构建,上面的代码都可以让您分析 32 位或 64 位标头。 @SilverbackNet 谢谢,这是有道理的。

以上是关于如何在 64 位 DLL 中查找导出函数的地址?的主要内容,如果未能解决你的问题,请参考以下文章

如何查看DLL中的函数信息

在 MS VC 2013 Express 中将 C++ dll 从 32 位转换为 64 位

sqlite.interop.dll 到不到指定模块 64位 winserver2008

定位Dll加载异常的方法

X64下 FF 25 + 00 00 00 00 + 导出表函数地址小测试

导入表与导出表(1)