5

我正在编写一个小的 PE 阅读器,所以我在我的测试应用程序旁边运行了 dumpbin 以确认这些值被正确读取。到目前为止一切正常,除了导出表。

我正在测试的文件是一个 DLL。我的应用程序将文件作为字节数组读取,然后传递给我的 PE 阅读器类。这些值与 dumpbin 的输出一致,包括导出数据目录的 RVA 和大小。

        E000 [     362] RVA [size] of Export Directory

问题是,字节数组的大小只有 42,496。正如您可能想象的那样,当我的 PE 阅读器尝试阅读 E000 (57,344) 时,我得到一个IndexOutOfRangeException. 但是,dumpbin 没有这样的问题,并且可以很好地读取导出目录。是的,整个文件确实被读入了字节数组。

这怎么可能?

4

1 回答 1

6

PE 文件包含“节”,这些节具有独立的基地址。PE 不是连续的内存映像。每个部分都是一个连续的内存映像。

首先,您必须阅读部分信息并制作其布局的内存映射。然后,您将能够将截面偏移与基于文件的偏移对齐。

顺便说一句,考虑看看 OllyDbg,它是一个免费软件、开源调试器和 Windows 反汇编器。它可能会帮助您测试自己的软件,并且可能会通过“滚动自己的”来满足您试图实现的目的。

输出示例dumpbin /all

第 1 节标题
   .text 名称
    BC14 虚拟大小
    1000 个虚拟地址(00401000 到 0040CC13)
    BE00 原始数据大小
     400 指向原始数据的文件指针(00000400 到 0000C1FF)
       0 指向重定位表的文件指针
       0 指向行号的文件指针
       0 搬迁次数
       0 行号数
60000020 个标志
         代码
         执行读取

在这种情况下,我的 .text 部分从 RVA 1000 开始并延伸到 RVA CE00。指向此部分的文件指针是 400。通过减去 600,我可以将 1000-CDFF 范围内的任何 RVA 转换为文件指针。(所有数值都是十六进制的。)

每当您遇到“RVA”(相对虚拟地址)时,使用以下方法将其解析为文件偏移量(或字节数组的索引):

  1. 确定 RVA 属于哪个部分。每个部分都包含从其虚拟地址到其大小的 RVA。部分不重叠。
  2. 从 RVA 中减去节的虚拟地址——这会给你相对于节的偏移量。
  3. 将部分的 PointerToRawData 添加到您在步骤 (2) 中获得的偏移量。这是与 RVA 对应的文件偏移量。

您可能使用的另一种方法是使用dwDesiredAccess参数中设置MapViewOfFileEx()的标志进行调用。此 API 将解析 PE 文件中的节头,并将节的内容读入它们相对于“模块库”的位置。FILE_MAP_EXECUTE

模块基址是将加载 PE 标头的基地址。使用LoadLibrary()函数加载 DLL 时,可以通过GetModuleInformation()函数的MODULEINFO成员lpBaseOfDll获得。

使用MapViewOfFileEx()时,模块库只是来自 的返回值MapViewOfFileEx()

在以这些方式加载模块的设置中,将 RVA 解析为正常的指针值是:

  1. 将模块基地址存储在char *
  2. 将 RVA 添加到char *
  3. 将其char *转换为实际的数据类型并取消引用它。

像这些方法一样让操作系统映射文件的一个缺点是,如果您使用此工具调查一些可疑文件并且不确定开发人员是否对节标题采取了奇怪的自由,您可能会错过一些有价值的信息通过让操作系统处理这部分解析。

于 2009-10-15T01:54:19.313 回答