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


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

一丶何為重定位  

    重定位的意思就是修正偏移的意思.  如一個地址位 0x401234 ,Imagebase = 0x400000 . 那么RVA就是 1234.  如果Imagebase 變了成了0x300000, 那么修正之后就是 ImageBase + RVA = 0X300000+1234 = 0x301234.

    首先我們知道.一個EXE文件.會調用很多DLL(PE) 有多個PE文件組成.

exe文件啟動的基址 (ImageBase) 是0x40000. 假設我們調用三個DLL  A B C. 

A DLL 在EXE展開的基址位置是0x10000000

那么恰巧 B DLL 展開的位置也是 0x1000000 兩個DLL位置展開地方是一樣的.那么就出現問題了.

如下圖:

這時候操作系統就會給我們進行修正. 將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偏移.

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM