在前面几节中经常提到相对虚拟地址RVA,在这篇博客中主要说明这个概念。本来是想接着转载小甲鱼的,但是我自己根据这篇文章和他的视频来学习的时候,发现在RVA与文件的相对偏移地址进行转化的时候,那块我看不懂,不知道为什么要这样转化,而且前面很多东西都反复讲了好多遍,比如对齐的问题,所以,这篇我就自己根据自己掌握的情况来写,还是在此处放上原文的连接:
原文(上)传送门
原文(下)传送门
什么是RVA
某个位置的RVA是该位置在内存中的地址相对于整个文件在内存中首地址的偏移值,举个例子,当PE文件中某个全局区中的一个数值所在内存的地址为0x0040871234,而PE文件被系统加载到内存时,它的的首地址为0x00400000,那么这个数值的RVA就是0x0040871234 - 0x00400000 = 0x871234
RVA到文件偏移的转化
有了RVA之后,发现所有数据在内存中进行索引都十分的方便,只要有了基地址和RVA后,通过基地址 + RVA就可以找到对应的数据,但是在PE中并没有类似值来记录某个数据到文件头的偏移,而且由于PE文件在内存中和在磁盘中的对齐值不同,造成某个数据在内存中的偏移和在磁盘中的偏移也不同,这样在磁盘中找到某些值就比较困难
虽然PE文件在内存中的对齐值与在磁盘中的不同,各个区块在内存中的地址与在文件中的不同,但是各个区块里面数据的摆放基本是一样的,也就是在区块中,某个值相对于区块首地址的偏移是一样的,现在定义这样几个变量:
Rva:某个数值在内存中的偏移
Voffset:节点首地址在内存中相当于基地址的偏移
Roffset:节点首地址在磁盘中相当于文件头的偏移
fRva:某个数值在磁盘中相对于文件头的偏移
根据上面的等量关系,可以得到这样的方程:
fRva - Roffset = Rva - Voffset
==>fRva = Rva- Voffset + Roffset
根据这个公式来计算时需要进行如下的步骤:
1. 根据某个Rva落在哪个区块中:取每个节区在内存中的首地址(IMAGE_SECTION_HEADER中的VirtualAddress值) + 区块真实大小(IMAGE_SECTION_HEADER中的VirtualSize),如果Rva小于这个值得话,说明是落在这个节中
2. 通过第一步,我们可以知道Rva处于哪个区块,这样就可以得到区块的首地址在磁盘中相对于文件头的偏移就也就是Voffset的值。
3. 在文件中根据IMAGE_SECTION_HEADER中的PointerToRawData值可以得到这个区块在磁盘中相当于文件头的首地址,这样通过上述公式可以计算出Rva在文件中的偏移
最终得到的公式如下:
fRva = Rva - VirtualAddress + PointerToRawData
举个例子,用lordPE这个工具打开一个.exe文件:
我们来在磁盘中找到ImportTable在磁盘中的入口位置,即0x0001B1C4这个RVA对应在磁盘中的位置
一个个对比发现
.textbss:
这个区块的RVA从0x00001000~0x00011000,这个值不在这个区块内
.text:
这个区块的RVA从0x00011000~0x00016060,这个值不在这个区块内
.rdata
这个区块的RVA从0x00017000~0x0001903d,这个值不在这个区块内
.data
这个区块的RVA从0x0001A000~0x000A158C,这个值不在这个区块内
.idata
这个区块的RVA从0x0001B000~0x0001BAF2,这个值在这个区块内
根据上面的信息可以得到
fRva = Rva - VirualAddress + PointerToRawData = 0x0001B1C4 - 0x0001B000 + 0x00007A00 = 7BC4