在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
为什么需要进行此调整?
答案 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