【问题标题】:Resolving RVA's for Import and Export tables within a PE file为 PE 文件中的导入和导出表解析 RVA
【发布时间】:2010-06-04 15:41:24
【问题描述】:

我目前正在编写一个 PE 解析器/加载器。 我已经使用标准 c 文件 io 成功地将 PE 文件加载到内存中,检索了有效的 DOS 和 PE 标头(可选标头),并获得了对 PE 部分的访问权限。 我的下一个目标是访问导出表以检索导出的符号。 为此,我使用了存储在索引 0 处的可选标头数据字典数组中的 RVA(我相信它指向导出表),并将此地址添加到加载到程序内存中的 PE 文件的地址中,然后将其转换为一个有效的导出表头。当我这样做时,我正在打开 NULL 地址和数据。这是一个小代码sn-p;

// 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 的信息似乎很少。 提前致谢。

【问题讨论】:

标签: c++ c winapi


【解决方案1】:

我喜欢你检查导入和导出目录的结构(IMAGE_DIRECTORY_ENTRY_EXPORTIMAGE_DIRECTORY_ENTRY_IMPORTIMAGE_DIRECTORY_ENTRY_IATIMAGE_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.NumberOfSections 的 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 *) 和对应于IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(IMAGE_BOUND_IMPORT_DESCRIPTOR *),以转储包含绑定信息(如果存在)的导入信息。

要转储来自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

【讨论】:

  • 为了澄清,你能说pbyFile来自哪里吗?
  • @DebugErr:和之前使用的pSrcFile一样。无需读取内存中的整个 EXE/DLL 文件。可以只使用内存映射方法来访问它。
  • 我明白了,我是用 C# 编写的,所以我现在有点懒,只需要文件中的地址(pSrcFile 将只是 0 用于计算)。我实现了你的功能,但除了 TimeDateStamp 字段,我所有的其他 ExportDataDirectory 字段看起来都非常错误:(
  • @DebugErr:抱歉,我无法调试您的代码。我只能猜测您可能会遇到 64 位/32 位的问题。见我的another old answer
  • 我不想让你 :) 我会再次阅读 MSDN 文章,如果我找到解决方案,我会发布我做错了什么。
【解决方案2】:

并非所有 PE 图像都有导出目录表。您需要检查可选标头的 Windows 特定的“NumberOfRvaAndSizes”字段。如果小于或等于IMAGE_DIRECTORY_ENTRY_EXPORT (0),则没有导出目录表(即ioh-&gt;DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] 处没有任何有效内容)。

请参阅this question 的答案以获取示例。

【讨论】:

    【解决方案3】:

    定义了宏来获取第一部分

    PIMAGE_SECTION_HEADER FisrtSection = IMAGE_FIRST_SECTION(NtHeaders)

    【讨论】:

      【解决方案4】:

      我已经编写了python代码来打印pe文件的所有导入和导出功能。您将了解如何在 C 中执行此操作。在数据目录中,我们必须检查导出目录 rva 和大小是否为“0”。如果不是'0',我们可以继续。您可以使用 Nt 核心网站上的工具“CFF explorer”。这是一个非常好的工具,它将显示数据如何映射到导出目录,然后您可以轻松解析它。这是blog

      【讨论】:

      • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-05
      • 2012-12-15
      • 2019-05-18
      • 1970-01-01
      • 2012-01-05
      相关资源
      最近更新 更多