返回
如何从磁盘获取的 DLL 中检索导出地址表?
windows
2024-03-14 19:36:15
从刚从磁盘获取的 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();
常见问题解答
-
如何确定数据目录的偏移量?
- 数据目录位于 PE 文件中的 OptionalHeader 结构中,其偏移量为 DOS 头中 e_lfanew 字段的偏移量。
-
如何确定导入地址表的大小?
- 导入地址表的大小存储在数据目录的 Size 字段中。
-
如何确定导出地址表的大小?
- 导出地址表的大小存储在导出地址表结构的 SizeOfNamePointers 字段中。
-
如何从函数地址获取函数名称?
- 函数名称存储在导出地址表结构的 AddressOfNames 字段中。
-
如何从函数名称获取函数地址?
- 无法直接从函数名称获取函数地址,但可以使用导出地址表的 AddressOfOrdinals 字段来查找函数的序号,然后使用序号从函数地址数组中获取地址。