我目前正在编写 PE 解析器/加载器。 我已经使用标准 c 文件 io 成功地将 PE 文件加载到内存中,检索到有效的 DOS 和 PE header (可选 header )以及获得对 PE 部分的访问权限。 我的下一个目标是访问导出表以检索导出的符号。 为此,我使用了存储在索引 0 处的可选 header 数据字典数组中的 RVA(我相信它指向导出表)并将该地址添加到加载到程序内存中的 PE 文件的地址,然后将其转换为有效的导出表头。当我这样做时,我正在打开 NULL 地址和数据。这是一个小代码片段;
// RVA from optional headers data dictionaries array cast to Export directory type
IMAGE_EXPORT_DIRECTORY* ied(
(IMAGE_EXPORT_DIRECTORY*)((void*)
((unsigned char*)buffer + ioh->DataDirectory[0].VirtualAddress)));
我必须使用内存映射 IO 才能正确执行此操作吗?我计算地址错误吗?关于 PE RVA 的信息似乎很少。 提前致谢。
最佳答案
我打开了一个我以前的项目,因为我喜欢你检查导入和导出目录的结构(IMAGE_DIRECTORY_ENTRY_EXPORT
、IMAGE_DIRECTORY_ENTRY_IMPORT
、IMAGE_DIRECTORY_ENTRY_IAT
和 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
)。我可以简而言之解释您遇到问题的部分。我的意思是如何找到指向例如 PE 内部的 IMAGE_EXPORT_DIRECTORY
的指针的部分。
首先,可以使用读/写文件操作来分析 PE 文件,但使用如下文件映射要容易得多:
hSrcFile = CreateFile (pszSrcFilename, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
hMapSrcFile = CreateFileMapping (hSrcFile, NULL, PAGE_READONLY, 0, 0, NULL);
pSrcFile = (PBYTE) MapViewOfFile (hMapSrcFile, FILE_MAP_READ, 0, 0, 0);
在我们有了指向 PE 文件的指针 pSrcFile
之后,我们可以在 PE 中找到另一个重要的地方:
pDosHeader = (IMAGE_DOS_HEADER *)pSrcFile;
IMAGE_NT_HEADERS32 *pNtHdr = (IMAGE_NT_HEADERS32 *)
((PBYTE)pDosHeader + pDosHeader->e_lfanew);
IMAGE_SECTION_HEADER *pFirstSectionHeader = (IMAGE_SECTION_HEADER *)
((PBYTE)&pNtHdr->OptionalHeader +
pNtHdr->FileHeader.SizeOfOptionalHeader);
现在我们拥有了任何目录所需的所有虚拟地址。例如,
pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
是导出目录的虚拟地址。之后要将虚拟地址转换为内存指针,我们应该找出PE里面有这个虚拟地址的段。为此,我们可以枚举 PE 的部分并找到大于或等于 0
且小于 pNtHdr->FileHeader.NumberOfSection
的 i
,其中
pFirstSectionHeader[i].VirtualAddress <=
pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
同时
pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
< pFirstSectionHeader[i].VirtualAddress + pFirstSectionHeader[i].Misc.VirtualSize
那么您应该在 pFirstSectionHeader[i]
部分中搜索导出数据:
IMAGE_SECTION_HEADER *pSectionHeader = &pFirstSectionHeader[i];
IMAGE_EXPORT_DIRECTORY *pExportDirectory =
(IMAGE_EXPORT_DIRECTORY *)((PBYTE)pbyFile + pSectionHeader->PointerToRawData +
pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress -
pSectionHeader->VirtualAddress);
您应该重复相同的过程以找到对应于 IMAGE_DIRECTORY_ENTRY_IMPORT
的 (IMAGE_IMPORT_DESCRIPTOR *)
和对应于 的
转储包含绑定(bind)信息(如果存在)的导入信息。(IMAGE_BOUND_IMPORT_DESCRIPTOR *)
>IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
要从 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
(对应于 delayimp.h 中定义的 (ImgDelayDescr *)
)转储信息,您还应该使用来自 IMAGE_DIRECTORY_ENTRY_IAT
的信息(对应于 (IMAGE_THUNK_DATA32 *)
)。
更多PE推荐给你http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
关于c++ - 解析 PE 文件中导入和导出表的 RVA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2975639/