RVA与FOA的转换
想象一下,如果你想通过逆向的方式改变一个全局变量的初始值,该怎么做?首先我们可以写一个程序,输出一个全局变量的地址和值:
int a = 0x12345678;
int main() {
printf("Address: 0x%x \n", &a);
printf("Value: 0x%x \n", a);
getchar();
return 0;
}
我们运行程序可以看见相应的值,那么我们可以是否可以在文件中直接搜索对应的值然后修改呢?这种方法没有毛病,但是文件中也许会存在很多个0x12345678,你无法准确的知道哪一个才是全局变量;那么,又是否可以通过已经给出的这个地址0x42ba30直接去寻找呢?当然也是不行的,因为在之前章节的学习中我们了解到,PE文件有2种状态(动静态),在这2种状态下,文件的对齐方式会发生变化,所以当前的地址是PE文件运行时(动态)的地址,你需要转换成在磁盘上(静态)的地址。
这两种状态的地址相互转换,我们可以称之为RVA与FOA的转换,RVA就是相对虚拟地址,FOA就是文件偏移地址;从RVA转换到FOA,就是从文件运行时(动态)的地址转换成在磁盘上(静态)的地址,按如下公式可以进行转换:
RVA地址由内存地址减去ImageBase地址(PE文件在内存中的开始位置是由扩展PE头中的ImageBase决定);
判断RVA地址是否位于PE头中:
如果是,那么RVA等于FOA;
如果不是,判断RVA位于哪个节:==》见后面逆向工程核心原理书中所述
当满足RVA地址大于等于节.VirtualAddress和RVA地址小于等于节.VirtualAddress加上当前节内存对齐后的大小时,就表示RVA地址在该节中。
RVA地址减去节.VirtualAddress等于差值,FOA地址就是根据节.PointerToRawData加上差值。
在一些较老的编译器中,编译出来的文件会区分文件对齐、内存对齐,但是在现在的编译器编译出来的程序,文件对齐与内存对齐时完全一样的,所以我们不用费这么大的周折,我们只需要算出RVA的值就可以得出FOA的值。
例如,在当前程序中就是这样,根据0x42BA30-0x400000(ImageBase)得出0x2BA30,其是RVA,也是FOA,直接使用Winhex打开找到:
可以直接修改它然后保存运行,这时候你就会发现全局变量的值已经发生了改变:
==》在我的机器上实践下:
32位,release编译,vs2017出来的结果:
二者并不一样!
换成64位,二者也不相同。
补充:
关于RVA和FOA(RAW)二者的转换,在逆向工程核心原理里提到过:
我们以书中第一个问题为例,核心计算方式就是section里的偏移是一致的!Image在内存中虽然变大,但是都是填充的NULL!