PE知识复习之PE的RVA与FOA的转换

一丶简介PE的两种状态

  首先我们知道PE有两种状态.一种是内存展开.一种是在文件中的状态.那么此时我们有一个需求.

我们想改变一个全局变量的初始值.此时应该怎么做.你知道虚拟地址.或者文件位置了.那么你怎么自己进行转换.

也就是说通过文件中的节数据找到在内存中这块数据的位置.或者反之.

寻找之前我们要先弄前几个概念.

ImageBase:  模块基址.程序一开始的地址.

VA: 全名virtualAddress 虚拟地址. 就是内存中虚拟地址. 例如 0x00401000

RVA: RVA就是相对虚拟偏移. 就是偏移地址. 例如 0x1000. 虚拟地址0x00401000的RVA就是 0x1000. RVA = 虚拟地址-ImageBase

FOA: 文件偏移. 就是文件中所在的地址.

二丶因为PE的两种状态.所以需要转换.

上面简介了一下什么是VA RVA 以及FOA 那么我们为什么要转换.

原因是这样的. 我们程序的数据.在PE文件中的地址假设是0x400, 那么在内存中展开的时候就是0x1000位置处.

那么我们如何通过内存位置.找到文件中这个数据的位置. 或者反之. 如果找到就可以进行修改了.

原因就是PE有两种状态.有内存对齐跟文件对齐. 如果内存对齐跟文件对齐一样.那么不管在内存中还是在文件中.数据的位置都是一样的.

例如文件对齐是0x1000,内存也是一样. 那么文件中0x1000位置存放的值.跟PE在内存中展开的时候存放的值是一样的.所以就不需要转换了.直接在文件中更改或者在内存中更改就行了.

因为对齐值不一样.所以我们才需要进行转换.

例如下图:

文件对齐值是0x200,内存对齐是0x1000

PE知识复习之PE的RVA与FOA的转换-LMLPHP

三丶转换方法

既然上方了解了PE的内存状态.以及文件状态形式. 那么转换就很好理解了.

1.内存转文件偏移计算

  1.1.计算RVA

  这一个讲的就是内存转文件偏移.就是知道一个内存地址.我们要看看在文件中是哪里存储的.

第一步: 我们知道PE在内存中展开.是在ImageBase位置展开的.头跟文件是一样的.只不过节数据展开位置不一样.

  所以首先就是 我们的内存地址-Image得出RVA

下方我们的内存地址我就设为x了.

  x - ImageBase == RVA  得出了我们的x位置在内存中的相对偏移.相对偏移就是我们计算的这个地址在开始位置的什么地方.

  ImageBase是在扩展头中存放的.我们可以查看一下.具体可以看看前几讲.属性解析.

  注意都是16进制进行加算的.

  根据上方我们得出的RVA.然后我们就在文件中从开头数RVA个字节,去寻找我们的这个数据.这样是不行的.因为文件对齐跟内存对齐是不一样的.所以我们要考虑对齐方式. 如果文件对齐跟内存对齐一样.那么这样就可以去找.

  2.寻址FOA

 既然找到了RVA了.那么就找一下FOA在哪里.也就是文件偏移在哪里.寻找这个值很简单.需要几个步骤.

  2.1.判断RVA属于哪个节/头. 

    如果RVA属于头(DOS+NT)那么不需要进行计算了.因为头在文件中根内存中都是一样展开的.直接从开始位置寻找到RVA个字节即可.

    如果不在头,就要判断在那个节里面. 判断节开始位置.跟结束位置. 我们的RVA在这个值里面.

    其中节虚拟地址结束位置 就是用节数据对齐后的大小+虚拟地址大小. 具体可以参考上一讲节表解析.

    公式: RVA  >= 节.VirtualAddress  && RVA <= (节.VirtualAddress + 节.SizeofRawData)

  2.2 计算差值偏移. 虚拟地址距离节数据的开始位置的偏移.

    然后计算差值偏移:

    差值  = RVA - 节.VirtuallAddress

差值偏移:

  为什么要计算差值.因为我们计算的差值偏移就是我们的 RVA距离我们节数据开始位置 的偏移是多少. 因为这个位置是不会改变的.

例如: 节数据开始位置是 0x1000  我们的RVA = 0x1024  那么差值是0x24. 如果文件中节数据开始的位置是0x400. 那么我们的差值偏移是不会变的. 那么文件偏移 + 差值偏移. 那么就是在文件中的位置. 例如 0x424

  2.3 计算FOA

  FOA就很好计算了.  差值偏移已经得出来了. 就知道我们的RVA距离节数据开始位置的偏移. 那么我们加上文件偏移就是FOA

公式: FOA = 差值偏移 + 节.PointToRawData

内存转文件偏移总结:

    1.计算RVA 公式: x - ImageBase == RVA

    2.计算差值偏移.  RVA - 节.VirtualAddress == 差值偏移.

    3.计算FOA         差值偏移 + 节.PointerToRawData == FOA

2.文件偏移转内存虚拟地址

  上面讲解了我们根据虚拟地址可以定位到在文件中的那个位置.那么反之.我们也可以通过文件位置.定位到虚拟地址.

需要理解的还是差值偏移. 只不过角色互换了. .

         设x 为节数据的任意一位置

      1.计算差值偏移:    x - 节.PointerToRawData(节数据在文件中开始的位置) == 差值偏移.

      2.计算RVA            差值偏移 + 节.VirtuallAddress(节数据在内存中展开的位置) == RVA

      3.计算虚拟地址:   RvA + ImageBase == VA

需要注意的就是我们的 x在哪一个节中.  x <= 节.PointerToRawData + 节.SizeofRawData

四丶实战演练

  我们写一个程序.其代码如下:

#include <stdio.h>
#include <stdlib.h> int g_TestValue = 0x12345678;
int main(int argc, char *argv[])
{
printf("全局变量地址 = %p \r\n", &g_TestValue);
printf("全局变量值 = %X \r\n", g_TestValue); getchar();
}

PS: 如果是VS系列编译器,请在属性 -> 连接 中去掉随机基址. 不然你需要计算一下.或者自己在PE中将文件头的文件属性更改. 更改为. 0x0103

程序截图:

  PE知识复习之PE的RVA与FOA的转换-LMLPHP

此时我们已经知道了全局变量地址.那么我们要转换到文件中.将这个全局变量地址进行修改.也就是说.我们通过修改文件.达到修改我们的全局变量值的一种手段.

思路:

  1.计算出RVA.  RVA怎么计算我们也知道了.我们需要查看PE中扩展头的ImageBase成员的值. 这里我已经查看好了.值为0x400000. 那么我们的RVA = 19000

  2.判断属于哪个节,计算出差值偏移

PE知识复习之PE的RVA与FOA的转换-LMLPHP 在我们的.data节中.差值偏移计算出结果为0.

  3.计算FOA位置.

  因为现在编译器的文件对齐以及内存对齐都是一样了.所以我们不许要进行计算了. 直接就是文件偏移就是FOA位置.

否则我们差值偏移加文件偏移 = = FOA.  现在我们的差值偏移是 0 0 + 节偏移 就是全局变量在文件中的位置.

PE知识复习之PE的RVA与FOA的转换-LMLPHP

7400 就是我们的FOA

  4.跳转到FOA修改全局变量的值

PE知识复习之PE的RVA与FOA的转换-LMLPHP

跳转到我们的FOA位置,可以看到我们全局变量的初始值为小端模式的 0x12345678,那么我们进行修改.进行文件保存即可.

 5.修改文件重新打开程序

PE知识复习之PE的RVA与FOA的转换-LMLPHP

  修改为0x55555555了,重新打开程序观看结果.

PE知识复习之PE的RVA与FOA的转换-LMLPHP

这就是内存转文件偏移的实战. 如果学过逆向的人应该接触过OD.或者x64DBG. 如果我们在内存中修改后.要保存到文件.那么计算公式就是这个.

04-26 10:50