PE文件与虚拟内存间的映射
在调试漏洞时,可能经常需要做这样两种操作。
(1)静态反汇编工具看到的PE文件中某条指令的位置是相当于磁盘文件而言的,即所谓的文件偏移,我们可能还需要知道这条指令在内存中所处的位置,即虚拟内存地址(VA)。
(1)静态反汇编工具看到的PE文件中某条指令的位置是相当于磁盘文件而言的,即所谓的文件偏移,我们可能还需要知道这条指令在内存中所处的位置,即虚拟内存地址(VA)。
(2)反之,在调试时看到的某条指令的地址是虚拟内存地址,我们也经常需要回到PE文件中找到这条指令对应的机器码。为此,我们需要弄清楚PE文件地址和虚拟内存地址之间的映射关系。首先,我们先看几个重要的概念。
<1>文件偏移地址(File Offset Address)
数据在PE文件中的地址叫文件偏移地址。这是文件在磁盘上存放时相对于文件开头的偏移。
<2>装载基址(Image Base)
PE装入内存时的基地址。默认情况下,EXE文件在内存中的基地址是0x00400000,DLL文件是0x10000000。这些位置可以通过修改编译选项来更改。
PE装入内存时的基地址。默认情况下,EXE文件在内存中的基地址是0x00400000,DLL文件是0x10000000。这些位置可以通过修改编译选项来更改。
<3>虚拟内存地址(Virtual Address,VA)
PE文件中的指令被装入内存后的地址。
<4>相对虚拟地址(Relative Virtual Address,RVA)
相对虚拟地址是内存地址相对于映射基址的偏移量。
虚拟内存地址、映射基址、相对虚拟内存地址三者之间有如下关系。(见图2-PE文件与虚拟内存的映射关系)
虚拟内存地址、映射基址、相对虚拟内存地址三者之间有如下关系。(见图2-PE文件与虚拟内存的映射关系)
文件偏移是相对于文件开始处0字节的偏移,RVA(相对虚拟地址)则是相对于装载基址0x00400000处的偏移。由于操作系统在进行装载时“基本”上保持PE中的各种数据结构,所以文件偏移地址和RVA有很大的一致性。
之所以说“基本”上一致是因为还有一些细微的差异。这些差异是由于文件数据的存放单位与内存数据存放单位不同而造成的。
(1)PE文件中的数据按照磁盘数据标准存放,以0x200字节为基本单位进行组织。当一个数据节(section)不足0x200字节时,不足的地方将被0x00填充;当一个数据节超过0x200字节时,下一个0x200块将分配给这个节使用。因此PE数据节的大小永远是0x200的整数倍。
(2)当代码装入内存后,将按照内存数据标准存放,并以0x1000字节位基本单位进行组织。类似的,不足将被补全,若超出将分配下一个0x1000为其所用。因此,内存中的节总是0x1000的整数倍。
(表一)中列出的文件偏移地址和RVA之间的对应关系可以让大家更直接地理解这种“细微的差异”。
由于内存中数据节相对于装载基址的偏移量和文件中数据节的偏移量有上述差异,所以进行文件偏移到虚拟内存地址之间的换算时,还要看所转换的地址位于第几个节内。
我们把这种由存储单位差异引起的节基址差称作节偏移,在上例中:
.text节偏移=0x1000-0x400=0xc00
.rdata节偏移=0x7000-0x6200=0xE00
.data节偏移=0x9000-0x7400=0x1C00
.rsrc节偏移=0x2D000-0x7800=0x25800
文件偏移地址与虚拟内存地址之间的换算关系可以用下面的公式来计算。
文件偏移地址=虚拟内存地址(VA)-装载基址(Image Base)-节偏移=RVA-节偏移
一些PE工具提供了这类地址转换功能,Lord PE就是其中出色的一款。