一丶何爲重定位 編碼
重定位的意思就是修正偏移的意思. 如一個地址位 0x401234 ,Imagebase = 0x400000 . 那麼RVA就是 1234. 若是Imagebase 變了成了0x300000, 那麼修正以後就是 ImageBase + RVA = 0X300000+1234 = 0x301234.spa
首先咱們知道.一個EXE文件.會調用不少DLL(PE) 有多個PE文件組成.操作系統
exe文件啓動的基址 (ImageBase) 是0x40000. 假設咱們調用三個DLL A B C. 設計
A DLL 在EXE展開的基址位置是0x100000003d
那麼恰巧 B DLL 展開的位置也是 0x1000000 兩個DLL位置展開地方是同樣的.那麼就出現問題了.code
以下圖:blog
這時候操做系統就會給咱們進行修正. 將B DLL 換個內存位置. 進行展開. 這也是爲何不少遊戲外掛.等等.都選擇DLL注入. 由於系統幫你重定位了各類信息. 代碼寫在DLL中便可.遊戲
以下圖: B DLL 從0x20.... 展開了.規避了使用相同地址內存
雖然這樣解決了入口基址不同.內存展開不同. 可是咱們知道.PE文件中有不少RVA .RVA 是相對於ImageBase的偏移進行存放的. 若是PE文件中都是 RVA 那就好辦了.博客
可是不必定呀.
如一下代碼所示:
#include <stdio.h> #include <stdlib.h> #include <Windows.h> int g_Value; int main() { g_Value = 10; }
查看其反彙編
咱們發現,在給全局變量賦值的的時候.地址是一個固定的.他不是 RVA
再次運行截圖:
咱們發現地址變了. 並且硬編碼 是一個固定的. 0x012c813c ,他直接編譯到二進制文件中了.
他把 ImageBase + RVA的值. 直接編譯到二進制當中. 就是說.這個全局變量的地址. 是一個 RVA + IMAGEBASE 的地址. 可是他是直接編譯到二進制中的.
問題所在:
假設A編譯的全局變量的地址是 RVA + iMAGEbase 假設是 0x1012345,那麼A展開的位置是 0x10..... 那麼全局變量地址是正確的. 可是若是B編譯的時候.地址也是1012345. 可是模塊基址加載不同.那麼就會出問題了.以下圖:
根據上面咱們發現了問題所在.因此如今咱們須要一張表.記錄那個地方須要進行重定位. 咱們把這個地方的值改一下便可.
也就是要記錄咱們修改須要重定位的位置.以上圖的代碼進行反彙編查看.
也就是記錄須要重定位的地方便可.
重定位表就是記錄全部須要修正的地址.只要有了重定位表.咱們就不用擔憂咱們的ImageBase 沒有佔住位置.
很是重要的一張表.
二丶重定位表的定位以及結構
重定位表.的定位在擴展頭中的數據目錄中. 數據目錄的第6項就是重定位表的 RVA偏移.以及重定位表的大小.
定位到重定位表,那麼有額外的結構體來描述重定位表.
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; //存儲的值以字節爲單位.字節多大.表示了一個重定位快有多大. // WORD TypeOffset[1]; } IMAGE_BASE_RELOCATION; typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
看着重定位表就兩個成員. 其實很是複雜. 咱們設 VirtualAddress 爲 x 設 SizeofBlock爲Y
以下圖所示,一個格子爲1個字節.
第一行四個字節爲x.也就是 Virtualaddress.. y則是第二個成員
SizeOfBlock 成員你的意思. 以字節爲單位.表明重定位的快由多大. 咱們知道.一個PE文件須要不少地方進行重定位的.好比這個記錄的
大小爲16. 也就是兩個重定位塊,那麼咱們的重定位表的大小就是以下圖所示:
下面則是新的重定位表.結構就是重定位表的結構,若是SzieofBlock大小爲20個字節.那麼重定位表大小就是20個本身.
第三個依次類推,重定位表的結束是不停的往下找,知道最後一個重定位表的結構成員你都爲0
這樣設計的緣由:
算術題解惑.
好比咱們有地址 101234 101235 101236 這種修正的地址有10000個.
那麼每一個地址有4個字節的. 那麼 4 * 10000 = 4萬個字節. 也就是咱們要準備一張4萬個字節的表來保存重定位的.
可是咱們發現一個規律.咱們要修正的表的偏移都很近, 1234 1235 ....
那麼咱們可不能夠這樣那. 咱們把 100000取出來. 兩個字節存儲1234 另外兩個地址存儲1235,不用準備四個字節了.小的偏移咱們兩個字節存儲.這樣的話咱們的表的字節就會縮小一半.
VirtuallAddress 就是存儲了 100000這個值,也就是須要 ""基址"" 公用的地址.
SizeofBlock 就是下面的偏移由多大, 咱們要修正的偏移是 VirtualAddress + sizeofBlock下面的值.
以下圖:
咱們的重定位表,須要修正的基址是 0x11000,大小是54. 那麼須要修正的偏移是 "36b0" "36bc" "36e0"....
咱們基址 + 偏移就是要修正的位置.
偏移的概念:
重定位表,是按照一個物理頁(4kb)進行存儲的. 也就是一個4kb內存,有一個重定位塊, 一個重定位表只管本身當前的物理頁的重定位.
一個重定位表的記錄偏移的大小是2個字節,也就是16位. 而記錄偏移的大小. 是由 SizeofBlock決定的.
可是咱們記錄偏移的位置,12位就夠了. 高4位.挪做他用. 並非記錄的纔會修正偏移.只有高4位爲3的時候.纔會進行重定位(基址 + 偏移)
真正修復的位置 virtualaddress + (高四位爲3 ? 低12位偏移 : 無所謂的值.)
也就是高四位爲3 Vir + 低12位偏移就等於真正要修復的RVA 例如 36b0 高位爲3 低12位就是6b0 要修復的RVA = vir + 6b0 ,若是加上當前DLL的ImagebASE 纔是真正要修復的虛擬地址 (VA) 咱們計算出的是RVA
若是高位不爲3,那麼這個值是無所謂了.由於內存對齊的緣由.
例如上圖重定位表. 0x11000表明了當前要進行修復的塊位置. 要修復偏移的地址第一個是36b0 . 高位爲3是要進行修復.
因此低位爲6B0. 因此修復的位置是 0x116b0的位置. 0x116b0 + 當前PE文件的ImageBase就是要進行重定位的位置
當前PE的Imagebase爲0x400000 重定位地方爲 0x4116b0位置.
咱們第一個修正的位置是4116b0位置,從內存中.反彙編查看. 4116b0的位置的值是0x0041813c. 也就是說.這個位置的值.是咱們須要重定位的. 也就是咱們上面寫的程序.爲全局變量賦值的時候.全局變量的地址.須要進行更改.
而須要重定位的值. 則是 全局變量的RVA值 + Imagebase 填寫到這裏面了. 全局變量是在內存中的data節存儲着.因此觀看前幾篇博客.能知道如何定位全局變量在文件的位置.
三丶總結重定位
重定位表有兩個成員. VirtuallAddress sizeofBlock
1.virtualladdress 記錄了當前物理頁須要進行重定位的起始地址.
2.sizeofBlock 記錄了重定位表多大.去掉8個字節(重定位表大小) 下面都是記錄了重定位表須要重定位的偏移.
3.偏移是2個字節存儲. 12位存儲偏移. 高4位存儲是否進行重定位. 高4位爲3則須要進行重定位. virtuall + 低12位 就是要修正的 RVA偏移.