IMAGE_SECTION_HEADER的VirtualAddress和PointerToRawData差异

时间:2017-07-20 10:44:58

标签: c++ windows portable-executable

this文章中,定义是

DWORD VirtualAddress

  

在EXE中,此字段将RVA保存到加载程序应映射该部分的位置。要计算内存中给定部分的实际起始地址,请将图像的基址添加到此字段中存储的部分的VirtualAddress中。

DWORD PointerToRawData

  

这是基于文件的偏移量,可以找到编译器或汇编器发出的原始数据。如果程序存储器本身映射PE或COFF文件(而不是让操作系统加载它),则此字段比VirtualAddress字段更重要。在这种情况下,您将拥有完全线性的文件映射,因此您可以找到此偏移处的节的数据,而不是VirtualAddress字段中指定的RVA

同样RVA定义为

  

PE文件中的许多字段都是根据RVAs指定的。 RVA只是某个项的偏移量,相对于文件内存映射的位置

  

要将RVA转换为可用指针,只需将RVA添加到模块的基址即可。基址是内存映射的EXE或DLL的起始地址

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

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

现在抓住import table

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

为什么需要进行此调整?

2 个答案:

答案 0 :(得分:1)

首先,这一行:

PEHeader = (ImageBase + ImageBase->e_lfanew);

不正确。 ImageBase类型为PIMAGE_DOS_HEADERS,因此当您添加ImageBase->e_lfanew时,DWORD正在执行pointer arithmetic,即您正在添加{ {1}}与ImageBase一样多的字节,这不是你想要的。您想要的是从(ImageBase->e_lfanew)*sizeof(IMAGE_DOS_HEADERS)个位置前进ImageBase->e_lfanew个字节。您可以通过执行以下操作来实现此目的:

ImageBase

注意转换为PIMAGE_NT_HEADERS PEheader = (PIMAGE_NT_HEADERS) ((PBYTE)(ImageBase) + dosHeader->e_lfanew);,这使得操作逐字节前进。

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

PBYTE

现在您的问题关于// Ignoring typecasts and pointer conversion issues for clarity... pNTHeader = dosHeader + dosHeader->e_lfanew; VirtualAddress

由于以下原因,行PointerToRawData也不正确:

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

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

每当您拥有RVA(如导入部分的RVA,或名称字符串或其他)时,为了访问它,您必须执行以下操作。

RVA

以下是功能:我posted it on pastebin,因为我无法在此处正确设置格式。

以下是它的工作原理:当你有一个RVA时,那个RVA必须位于一个区域内,所以你遍历所有区段标题(紧跟在可选标题之后),当你找到RVA所在的区域时,您计算该部分PBYTE actualAddress = (PBYTE) (lpFileBase + RVAtoFileOffset(pNTHeader, RVA))内该RVA的偏移量并将其添加到该部分的RVA - VirtualAddress,现在您具有RVA的文件偏移量。 如果RVA无效(它不在任何部分内),则该函数返回0.

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

我希望我有所帮助。

答案 1 :(得分:0)

一旦我们有了有效的PE / NT头地址,我们就可以直接跳转到第一部分:

PIMAGE_SECTION_HEADER sectionHeader;
sectionHeader = IMAGE_FIRST_SECTION(peHeader);

我们也可以通过屏蔽找到部分的名称

sectionHeader->Characteristics