this文章中,定义为
DWORD VirtualAddress

DWORD PointerToRawData


另外RVA定义为







当前的问题是到达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_DESCRIPTORSname字段使用指向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);

是不正确的。 ImageBase的类型为PIMAGE_DOS_HEADERS,因此,当您将ImageBase->e_lfanew添加到DWORD时,您正在执行pointer arithmetic,即,您要向ImageBase添加的字节数与(ImageBase->e_lfanew)*sizeof(IMAGE_DOS_HEADERS)一样多,这不是您想要的字节数。您想要的是将ImageBase->e_lfanew字节提前到ImageBase指向的位置。您可以通过执行以下操作来实现:
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加载程序不会像在文件映射中那样连续地在内存中映射所有可执行文件,而是将每个部分映射到其对应的VirtualAddress。在后一种情况下,要从RVA获取VA,只需将ImageBase添加到RVA即可,因为它在磁盘上的文件是PE加载器在内存中映射内容的表示形式。但是,由于您没有映射PE加载程序将它们映射到的每个部分,因此像您那样将ImageBase添加到RVA中是行不通的。

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

只要有RVA(例如导入部分的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/

10-10 21:29