PE 文件包含“节”,这些节具有独立的基地址。PE 不是连续的内存映像。每个部分都是一个连续的内存映像。
首先,您必须阅读部分信息并制作其布局的内存映射。然后,您将能够将截面偏移与基于文件的偏移对齐。
顺便说一句,考虑看看 OllyDbg,它是一个免费软件、开源调试器和 Windows 反汇编器。它可能会帮助您测试自己的软件,并且可能会通过“滚动自己的”来满足您试图实现的目的。
输出示例dumpbin /all
:
第 1 节标题
.text 名称
BC14 虚拟大小
1000 个虚拟地址(00401000 到 0040CC13)
BE00 原始数据大小
400 指向原始数据的文件指针(00000400 到 0000C1FF)
0 指向重定位表的文件指针
0 指向行号的文件指针
0 搬迁次数
0 行号数
60000020 个标志
代码
执行读取
在这种情况下,我的 .text 部分从 RVA 1000 开始并延伸到 RVA CE00。指向此部分的文件指针是 400。通过减去 600,我可以将 1000-CDFF 范围内的任何 RVA 转换为文件指针。(所有数值都是十六进制的。)
每当您遇到“RVA”(相对虚拟地址)时,使用以下方法将其解析为文件偏移量(或字节数组的索引):
- 确定 RVA 属于哪个部分。每个部分都包含从其虚拟地址到其大小的 RVA。部分不重叠。
- 从 RVA 中减去节的虚拟地址——这会给你相对于节的偏移量。
- 将部分的 PointerToRawData 添加到您在步骤 (2) 中获得的偏移量。这是与 RVA 对应的文件偏移量。
您可能使用的另一种方法是使用dwDesiredAccess参数中设置MapViewOfFileEx()
的标志进行调用。此 API 将解析 PE 文件中的节头,并将节的内容读入它们相对于“模块库”的位置。FILE_MAP_EXECUTE
模块基址是将加载 PE 标头的基地址。使用LoadLibrary()
函数加载 DLL 时,可以通过GetModuleInformation()
函数的MODULEINFO
成员lpBaseOfDll获得。
使用MapViewOfFileEx()
时,模块库只是来自 的返回值MapViewOfFileEx()
。
在以这些方式加载模块的设置中,将 RVA 解析为正常的指针值是:
- 将模块基地址存储在
char *
- 将 RVA 添加到
char *
- 将其
char *
转换为实际的数据类型并取消引用它。
像这些方法一样让操作系统映射文件的一个缺点是,如果您使用此工具调查一些可疑文件并且不确定开发人员是否对节标题采取了奇怪的自由,您可能会错过一些有价值的信息通过让操作系统处理这部分解析。