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

 


免責聲明!

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



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