PE知識復習之PE的RVA與FOA的轉換
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的內存狀態.以及文件狀態形式. 那麽轉換就很好理解了.
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
程序截圖:
此時我們已經知道了全局變量地址.那麽我們要轉換到文件中.將這個全局變量地址進行修改.也就是說.我們通過修改文件.達到修改我們的全局變量值的一種手段.
思路:
1.計算出RVA. RVA怎麽計算我們也知道了.我們需要查看PE中擴展頭的ImageBase成員的值. 這裏我已經查看好了.值為0x400000. 那麽我們的RVA = 19000
2.判斷屬於哪個節,計算出差值偏移
在我們的.data節中.差值偏移計算出結果為0.
3.計算FOA位置.
因為現在編譯器的文件對齊以及內存對齊都是一樣了.所以我們不許要進行計算了. 直接就是文件偏移就是FOA位置.
否則我們差值偏移加文件偏移 = = FOA. 現在我們的差值偏移是 0 0 + 節偏移 就是全局變量在文件中的位置.
7400 就是我們的FOA
4.跳轉到FOA修改全局變量的值
跳轉到我們的FOA位置,可以看到我們全局變量的初始值為小端模式的 0x12345678,那麽我們進行修改.進行文件保存即可.
5.修改文件重新打開程序
修改為0x55555555了,重新打開程序觀看結果.
這就是內存轉文件偏移的實戰. 如果學過逆向的人應該接觸過OD.或者x64DBG. 如果我們在內存中修改後.要保存到文件.那麽計算公式就是這個.
PE知識復習之PE的RVA與FOA的轉換