參考文章:
重定位表(relocation table)用於在程式載入到記憶體中時,進行記憶體位址的修正。
為什麼要進行記憶體位址的修正?
我們舉個例子來說:test.exe可執行程式需要三個動態鏈結庫dll(a.dll,b.dll,c.dll),假設test.exe的imagebase為400000h,而a.dll、b.dll、c.dll的基址imagebase均為1000000h。
那麼作業系統的引導程式在將test.exe載入進記憶體時,直接複製其程式到400000h開始的虛擬記憶體中,接著繼續載入a.dll、b.dll、c.dll:假設先載入a.dll,如果test.exe的imagebase + sizeofimage + 1000h不大於1000000h,則a.dll直接複製到1000000h開始的記憶體中;當b.dll載入時,雖然其基址也為1000000h,但是由於1000000h已經被a.dll占用,則b.dll需要重新分配基址,比如引導程式經過計算將其分配到1200000h的位址,c.dll同樣經過計算將其載入到150000h的位址。如下圖所示:
比如b.dll中存在乙個call 0x01034560,這是乙個絕對位址,其相對於imagebase的位址為δ = 0x01034560 - 0x01000000 = 0x34560h;而此時的記憶體中b.dll存在的位址是1200000h開始的記憶體,載入器分配的imagebase和b.dll中原來預設的imagebase(1000000h)相差了200000h,因此該call的值也應該加上這個差值,被修正為0x01234560h,那麼δ = 0x01234560h - 0x01200000h = 0x34560h則相對不變。否則call的位址不修正會導致call指令跳轉的位址不是實際要跳轉的位址,獲取不到正確的函式指令,程式則不能正常執行。
一般每乙個pe檔案都有乙個重定位表。當載入器引導程式時,如果載入器為某pe(.exe、.dll)分配的基址與其自身預設記錄的imagebase不相同,那麼該程式檔案載入完畢後就需要修正重定位表中的所有需要修正的位址。如果載入器分配的基址和該程式檔案中記錄預設的imagebase相同,則不需要修正,重定位表對於該dll也是沒有效用的。
所以說當pe檔案分配的基址與其自身預設記錄的imagebase不相同,並且其中存在上面類似的call(絕對位址)的話,那麼需要修正的位址可能有很多,所以用一張表記錄那些"絕對位址"的位址,將來載入進記憶體時,可能需要被修正,那麼儲存這些"絕對位址"的表就叫做重定位表!
例子**:
#include int main()
8: printf("1");
00401028 push offset string "%x" (0042201c)
0040102d call printf (00401080) //可以發現該call的位置是固定的,0x00401080
00401032 add esp,4
當前程式的imagebase是0x400000,所以如果程式能夠按照預定的imagebase來載入的話,那麼就不需要重定位表,這也是為什麼exe很少有重定位表,而dll大多都有重定位表的原因
那麼如果沒有預定呢?
結果就是該函式無法進行呼叫,呼叫的位置還是0x00401080,但是這個位置是別的程序在使用!
所以乙個exe中,需要修正的地方會很多,那我們如何來記錄都有哪些地方需要修正呢?
重定位就是來解決這樣的事情!
重定位就是 我們要把偏移,以及各種需要修正的位置,變為正確的
重定位表中所記錄的資訊就是記錄了 在哪個位址上是需要來進行重定位修復的
重定位表位於資料目錄項第六個結構,該結構體中的虛擬位址所指向的結構體名稱就是_image_base_relocation
typedef struct _image_base_relocation image_base_relocation;
這裡和匯出表有不一樣的地方:
1、重定位表是由許多個重定位塊組成的
2、重定位表的特點,結尾的時候會有virtualaddress
和sizeofblock
以4個位元組的0結尾來作為結束標誌的重定位塊
void printfrelocation(pvoid pfilebuffer)
} prelocationdirectory = (pimage_base_relocation)((dword)prelocationdirectory + (dword)prelocationdirectory->sizeofblock); //上面的for迴圈完成之後,跳轉到下個重定位塊 繼續如上的操作
}}
PE重定位表解析
在模組被載入到記憶體中,如果該模組沒有裝載到期待的位置,裡面以固定形式而不是以偏移形式硬編碼的位址就需要修正,這樣程式才能被正確載入。例如 call 401203。這個在編譯器編譯的時候將call後面的函式位址以硬編碼的形式固定住,那麼一旦模組不是被載入到40000的基址,而是被載入到100000,...
PE檔案結構詳解(六)重定位
前面兩篇 pe檔案結構詳解 四 pe匯入表 和 pe檔案結構詳解 五 延遲匯入表 在這裡,ie的iexplorer.exe匯入了kernel32.dll的getcommandlinea函式,可以看到這是個間接call,00401004這個位址的記憶體裡儲存了目的位址,根據圖中顯示的符號資訊可知,00...
PE檔案結構詳解(六)重定位
前面兩篇 pe檔案結構詳解 四 pe匯入表 和 pe檔案結構詳解 五 延遲匯入表 介紹了pe檔案中比較常用的兩種匯入方式,不知道大家有沒有注意到,在呼叫匯入函式時系統生成的 是像下面這樣的 在這裡,ie的iexplorer.exe匯入了kernel32.dll的getcommandlinea函式,可...