c++ - IMAGE_SECTION_HEADER的VirtualAddress和PointerToRawData的区别

标签 c++ windows portable-executable

this文章,定义是

DWORD 虚拟地址

In EXEs, this field holds the RVA to where the loader should map the section. To calculate the real starting address of a given section in memory, add the base address of the image to the section's VirtualAddress stored in this field.

DWORD PointerToRawData

This is the file-based offset of where the raw data emitted by the compiler or assembler can be found. If your program memory maps a PE or COFF file itself (rather than letting the operating system load it), this field is more important than the VirtualAddress field. You'll have a completely linear file mapping in this situation, so you'll find the data for the sections at this offset, rather than at the RVA specified in the VirtualAddress field

同时 RVA 定义为

Many fields in PE files are specified in terms of RVAs. An RVA is simply the offset of some item, relative to where the file is memory-mapped

To convert an RVA into a usable pointer, simply add the RVA to the base address of the module. The base address is the starting address of a memory-mapped EXE or DLL

手头的问题是到达 PE 文件的 import section

hFile = CreateFile(..);
hFileMapping = CreateFileMapping(..);
lpFileBase = MapViewOfFile(..);
ImageBase = (PIMAGE_DOS_HEADER)lpFileBase;
PEHeader = (ImageBase + ImageBase->e_lfanew);

现在获取导入表

PIMAGE_OPTIONAL_HEADER PEImageOptionalHeader = &(PEHeader->OptionalHeader);
IMAGE_DATA_DIRECTORY importTable = PEImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 

因为 importTable.VirtualAddress 是一个 RVA,为了获得可用的指针,我可以添加图像文件的基地址。

所以 ImageBase + importTable.virtualAddress 应该让我导入部分。但它没有。为什么?

然后,如果我到达正确的部分标题(通常是 .idata)并执行此操作。

ImageBase + pointerToSection->PointerToRawData;

上面正确地将我带到了 IMAGE_IMPORT_DESCRIPTORS 数组。我知道使用 pointerToSection->virtualAddress 而不是上面的 PointerToRawData 是行不通的因为我自己映射 PE 文件。

现在要获取项目的 name,加载的模块依赖于,我可以使用指向 IMAGE_IMPORT_DESCRIPTORS 的指针,使用字段 name这又是一个RVA。要转换一个RVA,我只需添加ImageBase..

 LPSTR libname = (PCHAR)((DWORD)ImageBase+ImageimportDescriptor->Name);

但它不起作用。为什么?要转换RVA,我们只需添加图像的基地址。下面的工作

ImageBase+ImageimportDescriptor->Name + pointerToSection->PointerToRawData - pointerToSection->virtualAddress

每次我需要一个部分中的一些信息时,我都需要进行此调整

pointerToSection->PointerToRawData - pointerToSection->virtualAddress

为什么需要进行此调整?

最佳答案

首先,这一行:

PEHeader = (ImageBase + ImageBase->e_lfanew);

不正确。 ImageBasePIMAGE_DOS_HEADERS 类型,所以当你向它添加 ImageBase->e_lfanew 时,这是一个 DWORD 你是做 pointer arithmetic ,也就是说,您向 ImageBase 添加了与 (ImageBase->e_lfanew)*sizeof(IMAGE_DOS_HEADERS) 一样多的字节,这不是您想要的。你想要的是从 ImageBase 指向的地方推进 ImageBase->e_lfanew 字节。您可以通过执行以下操作实现此目的:

PIMAGE_NT_HEADERS PEheader = (PIMAGE_NT_HEADERS) ((PBYTE)(ImageBase) + dosHeader->e_lfanew);

注意转换为 PBYTE,这使得操作逐字节推进。

这在处理 PE 文件时很常见,因为很多时候你想从指针前进 n 个字节,而不是指针指向的 n 个数据结构。 在article you refered to它甚至在评论中说:

// Ignoring typecasts and pointer conversion issues for clarity...
pNTHeader = dosHeader + dosHeader->e_lfanew;

现在回答关于 VirtualAddressPointerToRawData你的问题:

ImageBase + importTable.virtualAddress 行也不正确,原因如下:

PE Loader 不会像您在文件映射中那样将所有可执行文件连续映射到内存中,它会将每个部分映射到其相应的 VirtualAddress。在后一种情况下,要从 RVA 获取 VA,只需将 ImageBase 添加到 RVA 即可,因为磁盘上的文件代表PE 加载程序在内存中映射的内容。但是,由于您没有将每个部分映射到 PE 加载程序将映射它们的位置,因此将 ImageBase 添加到 RVA 就像您所做的那样不起作用。

您需要一个给定 RVA 的函数,为您提供磁盘上文件中与该 RVA 相对应的文件偏移量。要获得 VA,您只需将此文件偏移量添加到指向映射文件的字节指针。

只要你有一个 RVA(比如导入部分的 RVA,或者一个名称字符串等等),为了访问它,你必须执行以下操作。

PBYTE actualAddress = (PBYTE) (lpFileBase + RVAtoFileOffset(pNTHeader, RVA))

函数如下:I posted it on pastebin因为我无法在此处正确设置格式。

它是这样工作的:当你有一个 RVA 时,该 RVA 必须位于一个部分内,因此你遍历所有部分标题(紧接在可选标题之后),当你找到 RVA 所在的部分时,您计算该 RVA 在该部分 RVA - VirtualAddress 中的偏移量,并将其添加到该部分的 PointerToRawData,现在您有了 RVA 的文件偏移量。 如果 RVA 无效(它不在任何部分内),该函数返回 0。

您需要这样做才能访问导入目录或每个导入描述符的名称,因为您拥有的是 RVA。

希望对您有所帮助。

关于c++ - IMAGE_SECTION_HEADER的VirtualAddress和PointerToRawData的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45212489/

相关文章:

c++ - 具有 STL 容器成员的模板类

linux - 我混淆了一些关于在 Linux 0.11 中的 boot/setup.s 文件中启用 PE 的汇编代码

c++ - 在编译时截断字符串

c++ - 在非 MFC 应用程序中链接 MFC dll

windows - Windows 上的 PID 可以为负数吗?

windows - 无法从 Windows ping 到多播地址

windows - 为什么 PE 文件中有 MS-DOS 的 stub ?

assembly - 为程序集中的PE文件创建和使用节(NASM)

c++ - 调整窗口大小时 MFC 程序崩溃,出现错误 "A required resource was not found."

windows - 从 Windows 批处理脚本中的文件中读取值