返回

如何从磁盘获取的 DLL 中检索导出地址表?

windows

从刚从磁盘获取的 DLL 中检索导出地址表

简介

在计算机科学中,从磁盘加载 DLL(动态链接库)时,需要检索其导出地址表以访问其函数。导出地址表包含函数的虚拟地址,该地址可用于从 DLL 调用函数。本文将详细介绍如何从刚从磁盘获取的 DLL 中检索导出地址表。

数据目录

PE 文件格式包含一个称为数据目录的结构,其中存储着指向文件不同部分的指针。数据目录中的第 2 个条目指向导入地址表,而导入地址表又包含指向导出地址表的指针。

检索数据目录

要检索数据目录,需要首先读取文件头(DOS 头和 PE 头)。DOS 头位于文件开头,其后是 PE 头,其中包含数据目录结构。

// 从文件开始读取 DOS 头
IMAGE_DOS_HEADER dosHeader;
FILE *file = fopen("dll.dll", "rb");
fread(&dosHeader, sizeof(IMAGE_DOS_HEADER), 1, file);

// 读取 PE 头
IMAGE_NT_HEADERS64 ntHeaders;
fseek(file, dosHeader.e_lfanew, SEEK_SET);
fread(&ntHeaders, sizeof(IMAGE_NT_HEADERS64), 1, file);

// 获取数据目录
IMAGE_DATA_DIRECTORY *dataDirectories = ntHeaders.OptionalHeader.DataDirectory;

检索导入地址表

数据目录的第 2 个条目指向导入地址表。

// 获取导入地址表 RVA
DWORD importDirectoryRva = dataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

检索导出地址表

导入地址表包含指向导出地址表的指针。

// 读取导入地址表
IMAGE_IMPORT_DESCRIPTOR *importDescriptor;
fseek(file, importDirectoryRva, SEEK_SET);
fread(&importDescriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, file);

// 获取导出地址表 RVA
DWORD exportDirectoryRva = importDescriptor->OriginalFirstThunk;

导出地址表的结构

导出地址表是一个包含 DWORD 数组的结构。每个 DWORD 指向一个函数的虚拟地址。

// 读取导出地址表
IMAGE_EXPORT_DIRECTORY *exportDirectory;
fseek(file, exportDirectoryRva, SEEK_SET);
fread(&exportDirectory, sizeof(IMAGE_EXPORT_DIRECTORY), 1, file);

// 获取函数地址
DWORD *functionAddresses = (DWORD *)((char *)exportDirectory + exportDirectory->AddressOfFunctions);

使用导出地址表

一旦检索到导出地址表,就可以使用它来调用 DLL 中的函数。

// 调用 DLL 中的函数
void (*func)() = (void (*)())functionAddresses[0];
func();

常见问题解答

  1. 如何确定数据目录的偏移量?

    • 数据目录位于 PE 文件中的 OptionalHeader 结构中,其偏移量为 DOS 头中 e_lfanew 字段的偏移量。
  2. 如何确定导入地址表的大小?

    • 导入地址表的大小存储在数据目录的 Size 字段中。
  3. 如何确定导出地址表的大小?

    • 导出地址表的大小存储在导出地址表结构的 SizeOfNamePointers 字段中。
  4. 如何从函数地址获取函数名称?

    • 函数名称存储在导出地址表结构的 AddressOfNames 字段中。
  5. 如何从函数名称获取函数地址?

    • 无法直接从函数名称获取函数地址,但可以使用导出地址表的 AddressOfOrdinals 字段来查找函数的序号,然后使用序号从函数地址数组中获取地址。