PE知識複習之PE的重定位表

一丶何爲重定位  編碼

    重定位的意思就是修正偏移的意思.  如一個地址位 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偏移.

相關文章
相關標籤/搜索