重定位表

PE結構之重定位表

什麼是重定位:
重定位就是你本來這個程序理論上要佔據這個地址,但是由於某種原因,這個地址現在不能讓你佔用,你必須轉移到別的地址,這就需要基址重定位。你可能會問,不是說過每個進程都有自己獨立的虛擬地址空間嗎?既然都是自己的,怎麼會被佔據呢?對於EXE應用程序來說,是這樣的。但是動態鏈接庫就不一樣了,我們說過動態鏈接庫都是寄居在別的應用程序的空間的,所以出現要載入的基地址被應用程序佔據了或者被其它的DLL佔據了,也是很正常的,這時它就不得不進行重定位了。

哪些數據需要重定位:
:00401000 55 push ebp
:00401001 8BEC mov ebp, esp
:0040100383C4FC add esp, FFFFFFFC
:00401006 A1FC0F4000 mov eax, dword ptr [00400FFC] ;mov eax,dwVar
:0040100B 8B45FC mov eax, dword ptr [ebp-04] ;mov eax,@dwLocal
:0040100E 8B4508 mov eax, dword ptr [ebp+08] ;mov eax,_dwParam
:00401011 C9 leave
:00401012 C20400 ret 0004
:00401015 68D2040000 push 000004D2
:0040101A E8E1FFFFFF call 00401000 ;invoke Proc1,1234

其中地址爲00401006h處的mov eax,dword ptr [00400ffc]就是一句需要重定位的指令,當整個程序的起始地址位於00400000h處的時候,這句代碼是正確的,但00400000h只是它自己期望的起始地址,也許exe希望把這個DLL加載到00500000h處,那麼句指令必須變成mov eax,dword ptr [00500ffc]纔是正確的。這就意味着它需要重定位。即地址變成了00400ffch+(00500000h-00400000h)。這些數據被表示成[00400FFC],其實FFC只是數據相對於程序自身首地址的一個偏移量,程序假設自己就是從400000處開始加載然後從偏移FFC處可以取到數據,但是,實際加載之後,有時候並不是從400000開始。

所以,重定位的算法可以描述爲:將直接尋址指令中的雙字地址加上模塊實際裝入地址與模塊建議裝入地址之差。爲了進行這個運算,需要有3個數據,首先是需要修正的機器碼地址;其次是模塊的建議裝入地址;最後是模塊的實際裝入地址。在這3個數據中,模塊的建議裝入地址已經在PE文件頭中定義了,即OptionalHeader中的ImageBase,而模塊的實際裝入地址是Windows裝載器確定的,到裝載文件的時候自然會知道。

事實上,PE文件的重定位表中保存的就是一大堆需要修正的代碼的地址。

重定位表的位置:
重定位表一般會被單獨存放在一個可丟棄的以“.reloc”命名的節中,但是和資源一樣,這並不是必然的,因爲重定位表放在其他節中也是合法的,惟一可以肯定的是,如果重定位表存在的話,它的地址肯定可以在PE文件頭中的數據目錄中找到。

我們可以這樣得到一個重定位表:

在這裏插入圖片描述

即利用OptionalHeader中的DataDirectory。

重定位表的結構:

在這裏插入圖片描述

pNewOptionalHead.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + pbBase指向的就是IMAGE_BASE_RELOCATION數組的第一個元素。

每個IMAGE_BASE_RELOCATION元素包含了VirtualAddress、SizeOfBlock,後邊跟着數目不定的重定位項。如下圖所示:

在這裏插入圖片描述

雖然重定位表中的有用數據是那些需要重定位機器碼的地址指針,但爲了節省空間,PE文件對存放的方式做了一些優化。

在正常的情況下,每個32位的指針佔用4個字節,如果有n個重定位項,那麼重定位表的總大小是4×n字節大小。

直接尋址指令在程序中還是比較多的,在比較靠近的重定位表項中,32位指針的高位地址總是相同的,如果把這些相近表項的高位地址統一表示,那麼就可以省略一部分的空間,當按照一個內存頁來分割時,在一個頁面中尋址需要的指針位數是12位(一頁等於4096字節,等於2的12次方),假如將這12位湊齊16位放入一個字類型的數據中,並用一個附加的雙字來表示頁的起始指針,另一個雙字來表示本頁中重定位項數的話,那麼佔用的總空間會是4+4+2×n字節大小,計算一下就可以發現,當某個內存頁中的重定位項多於4項的時候,後一種方法的佔用空間就會比前面的方法要小。
PE文件中重定位表的組織方法就是採用類似的按頁分割的方法,從PE文件頭的數據目錄中得到重定位表的地址後,這個地址指向的就是順序排列在一起的很多重定位塊,每一塊用來描述一個內存頁中的所有重定位項。

每個重定位塊以一個IMAGE_BASE_RELOCATION結構開頭,後面跟着在本頁面中使用的所有重定位項,每個重定位項佔用16位的地址(也就是一個word),結構的定義是這樣的:

在這裏插入圖片描述

VirtualAddress字段是當前頁面起始地址的RVA值,本塊中所有重定位項中的12位地址加上這個起始地址後就得到了真正的RVA值。SizeOfBlock字段定義的是當前重定位塊的大小,從這個字段的值可以算出塊中重定位項的數量,由於SizeOfBlock=4+4+2×n,(4字節VritualAddress,4字節SizeOfBlock,每個重定位項2字節),也就是sizeof IMAGE_BASE_RELOCATION+2×n,所以重定位項的數量n就等於(SizeOfBlock-sizeof IMAGE_BASE_RELOCATION)÷2。
IMAGE_BASE_RELOCATION結構後面跟着的n個字就是重定位項,每個重定位項的16位數據位中的低12位就是需要重定位的數據在頁面中的地址,剩下的高4位也沒有被浪費,它們被用來描述當前重定位項的種類。

在這裏插入圖片描述在這裏插入圖片描述

雖然高4位定義了多種重定位項的屬性,但實際上在PE文件中只能看到0和3這兩種情況。

所有的重定位塊最終以一個VirtualAddress字段爲0的IMAGE_BASE_RELOCATION結構作爲結束,讀者現在一定明白了爲什麼可執行文件的代碼總是從裝入地址的1000h處開始定義的了(比如裝入00400000h處的.exe文件的代碼總是從00401000h開始,而裝入10000000h處的.dll文件的代碼總是從10001000h處開始),要是代碼從裝入地址處開始定義,那麼第一頁代碼的重定位塊的VirtualAddress字段就會是0,這就和重定位塊的結束方式衝突了。

但凡涉及到直接尋址的指令都需要進行重定位處理

把內存中需要重定位的數據按頁的大小0x1000分爲若干個塊,而這個VirtualAddress就是每個塊的起始RVA。如上圖第一個重定位項是33f2,去掉高位的3,得到3f2,再加上這個重定位塊的VirtualAddress,00011000,就得到000113f2。只知道塊的RVA當然還不行,我們要知道每一個需要重定位數據的具體地址。在程序沒有被真正加載(得到真實的起始地址)之前,就用ImageBase作爲基址(這時的ImageBase是00400000),相加得到004113f2。下邊例子中的004112CB就是這樣得到的。

PE文件的重定位表中保存的就是一大堆需要修正的代碼。

舉例:
寫一個小程序testRelHello.exe,裏邊調用了HelloWorld()函數:
在這裏插入圖片描述

在這裏插入圖片描述
用IDA查看:

在這裏插入圖片描述

這裏沒表示成[0004XXXX],而是用 j_?HelloWorld@@YAXXZ代替了,但是雙擊跟進發現:

在這裏插入圖片描述

實際就是[004112CB]。

下面我們查看testRelHello的PE結構中的OptionalHeader.ImageBase的值:

在這裏插入圖片描述

即每次testRelHello都希望自身被加載到00400000處。

現在,我們利用GetModuleHandle()函數,運行testRelHello,看看實際上會被加載到哪裏:

在這裏插入圖片描述

我在運行之前先設好斷點:

在這裏插入圖片描述

現在在VS中查看其彙編代碼:

在這裏插入圖片描述

對HelloWorld()的調用就變成了call [0B312CB],而不再是[004112CB]。這就是因爲進行了重定位。

現在我們計算:004112CB+B20000-400000,結果等於:

在這裏插入圖片描述

這就是對HelloWorld()函數進行重定位的一個過程。

參考:

http://www.blogfshare.com/pe-relocate.html

http://www.51testing.com/html/87/300987-823223.html

http://blog.sina.com.cn/s/blog_a9303fd90101bwxj.html

http://blog.csdn.net/cosmoslife/article/details/17270475

轉載請聲明出處:http://www.cnblogs.com/predator-wang/p/4962775.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章