所以,我试图读取DLL文件,直到到达可选头数据目录,特别是它的第一个成员,导出表,一切都很好。
我的问题是我无法移动阅读器的偏移量,因为虚拟地址成员基于内存VA,而我的阅读器基于文件偏移量。可能会有一个直观的示例帮助您:
如您所见,此PE查看器从数据目录(可选标题)的导出表地址读取的已加载虚拟地址为值0x00002630(从现在开始,将其称为hex1)。
但是,当我单击“导出表”以查看实际内容时,程序会将该地址从内存转换为文件偏移,结果将我重定向到该地址:
它重定向到我的地址是0x00001a30(从现在开始,将其称为hex2)。
我自己进行了一些测试,例如将hex1划分为8,因为我认为这可能是从内存对齐方式4096和文件对齐方式512过渡的结果,但是它给我的结果与hex2不同。我也做了一些奇怪的事情来尝试得出该公式,但是它给了我更奇怪的结果。
所以,我的问题是,如果我只知道数据目录(hex1)的内存偏移,我如何获取/计算文件偏移(hex2)?
假设您正在使用MSVC C / C ++,首先需要在“可选标头”之后找到IMAGE_SECTION_HEADER结构的数组。 SDK包含一个名为IMAGE_FIRST_SECTION(pNtHeaders)的宏,您只需在其中传递PE标头的指针即可简化此过程。它基本上只是跳过节头开始的内存中的可选头。此宏也可以在32位或64位Windows PE文件上使用。
一旦有了IMAGE_SECTION_HEADER数组的地址,就可以使用指针数学遍历直到FileHeader.NumberOfSections的结构。每个结构都描述了每个已命名PE部分的内存地址(VirtualAddress)的相对开始,以及相对于已加载文件中该部分的文件偏移量(PointerToRawData)。
文件内部分的大小为SizeOfRawData。至此,您已经拥有了将任何给定RVA转换为文件偏移量所需的一切。首先,使用您要查找的RVA检查每个IMAGE_SECTION_HEADER的VirtualAddress。即:
if (uRva >= pSect->VirtualAddress && (uRva < (pSect->VirtualAddress + pSect->SizeOfRawData))
{
//found
}
如果找到匹配的部分,则从查找RVA中减去VirtualAddress,然后添加PointerToRawData偏移量:
uFileOffset = uRva - pSect->VirtualAddress + pSect->PointerToRawData
这会导致从文件开头开始,对应于该RVA的偏移量。此时,您已将RVA转换为文件偏移量。
[注意:由于填充,错误的PE文件等,您可能会发现并非所有RVA都将映射到文件中的某个位置,此时您可能会显示错误消息。