当导出表出现在大于文件本身的文件偏移量时,dumpbin 如何读取导出表?

发布于 2024-08-07 18:06:21 字数 435 浏览 8 评论 0原文

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

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

        E000 [     362] RVA [size] of Export Directory

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

这怎么可能?

I'm writing a little PE reader, so I run dumpbin alongside my test application to confirm that the values are being read correctly. Everything it working so far, except for the export table.

The file I'm testing with is a DLL. My application reads the file in as a byte array, which gets passed to my PE reader class. The values align with those output by dumpbin, including the RVA and size of the export data directory.

        E000 [     362] RVA [size] of Export Directory

The problem is, the byte array's size is only 42,496. As you can probably imagine, when my PE reader attempts to read at E000 (57,344), I get an IndexOutOfRangeException. dumpbin, however, has no such problem and reads the export directory just fine. And yes, the entire file is indeed being read into the byte array.

How is this possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

反目相谮 2024-08-14 18:06:21

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

首先,您必须读取部分信息并对其布局进行内存映射。然后您将能够将部分偏移量与基于文件的偏移量对齐。

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

dumpbin /all 输出示例:

SECTION HEADER #1
   .text name
    BC14 virtual size
    1000 virtual address (00401000 to 0040CC13)
    BE00 size of raw data
     400 file pointer to raw data (00000400 to 0000C1FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         Code
         Execute Read

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

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

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

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

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

使用 MapViewOfFileEx() 时,模块基址只是 MapViewOfFileEx() 的返回值。

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

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

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

The PE file contains "sections", and the sections have independent base addresses. The PE is not a contiguous memory image. Each section is a contiguous memory image.

First you will have to read the section information and make memory-map of their layout. Then you will be able to align the section offsets with the file-based offsets.

As an aside, consider looking at OllyDbg, which is a freeware, open-source debugger and disassembler for Windows. It will possibly help you test your own software, and might server the very purpose you are trying to fill by "rolling your own."

Example from dumpbin /all output:

SECTION HEADER #1
   .text name
    BC14 virtual size
    1000 virtual address (00401000 to 0040CC13)
    BE00 size of raw data
     400 file pointer to raw data (00000400 to 0000C1FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         Code
         Execute Read

In this case, my .text section begins at RVA 1000 and extends to RVA CE00. The file pointer to this section is 400. I can translate-to-file-pointer any RVAs in the range 1000-CDFF by the work of subtracting 600. (All numeric values hexadecimal.)

Whenever you encounter an "RVA" (Relative Virtual Address), you resolve it to a file offset (or an index into your byte array), using this method:

  1. Determine to which section the RVA belongs. Each section contains the RVAs from its virtual address through its size. Sections do not overlap.
  2. Subtract the section virtual address from the RVA -- this gives you the offset relative to the section.
  3. Add the section's PointerToRawData to the offset you obtain in step (2). This is the file offset corresponding to the RVA.

Another approach that you might use is to call MapViewOfFileEx() with the flag FILE_MAP_EXECUTE set in dwDesiredAccess argument. This API will parse the section headers from the PE file, and read the contents of the sections into their locations relative to the "module base."

The module base is the base address at which the PE header will be loaded. When loading DLLs using LoadLibrary() functions, this can be obtained via GetModuleInformation() function's MODULEINFO member lpBaseOfDll.

When using MapViewOfFileEx(), the module base is simply the return value from MapViewOfFileEx().

In the setting of loading the module in these ways, resolving the RVA to a normal pointer value is a matter of:

  1. Store the module base address in a char *
  2. Add the RVA to the char *
  3. Cast the char * to the actual datatype and dereference that.

A drawback of letting the OS map the file as in these approaches is that if you are using this tool to investigate some suspect file and are not sure if a developer has taken strange liberties with the section headers, it is possible you miss some valuable information by letting the OS handle this part of the parsing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文