PE文件结构详解(六)重定位
前面兩篇PE文件結(jié)構(gòu)詳解(四)PE導(dǎo)入表和PE文件結(jié)構(gòu)詳解(五)延遲導(dǎo)入表介紹了PE文件中比較常用的兩種導(dǎo)入方式,不知道大家有沒(méi)有注意到,在調(diào)用導(dǎo)入函數(shù)時(shí)系統(tǒng)生成的代碼是像下面這樣的:
在這里,IE的iexplorer.exe導(dǎo)入了Kernel32.dll的GetCommandLineA函數(shù),可以看到這是個(gè)間接call,00401004這個(gè)地址的內(nèi)存里保存了目的地址,
根據(jù)圖中顯示的符號(hào)信息可知,00401004這個(gè)地址是存在于iexplorer.exe模塊中的,實(shí)際上也就是一項(xiàng)IAT的地址。這個(gè)是IE6的
exe中的例子,當(dāng)然在dll中如果導(dǎo)入其他dll中的函數(shù),結(jié)果也是一樣的。這樣就有一個(gè)問(wèn)題,代碼里call的地址是一個(gè)模塊內(nèi)的地址,而且是一個(gè)
VA,那么如果模塊基地址發(fā)生了變化,這個(gè)地址豈不是就無(wú)效了?這個(gè)問(wèn)題如何解決?
答案是:Windows使用重定位機(jī)制保證以上代碼無(wú)論模塊加載到哪個(gè)基址都能正確被調(diào)用。聽起來(lái)很神奇,是怎么做到的呢?其實(shí)原理并不很復(fù)雜,這個(gè)過(guò)程分三步:
1.編譯的時(shí)候由編譯器識(shí)別出哪些項(xiàng)使用了模塊內(nèi)的直接VA,比如push一個(gè)全局變量、函數(shù)地址,這些指令的操作數(shù)在模塊加載的時(shí)候就需要被重定位。
2.鏈接器生成PE文件的時(shí)候?qū)⒕幾g器識(shí)別的重定位的項(xiàng)紀(jì)錄在一張表里,這張表就是重定位表,保存在DataDirectory中,序號(hào)是IMAGE_DIRECTORY_ENTRY_BASERELOC。
3.PE文件加載時(shí),PE 加載器分析重定位表,將其中每一項(xiàng)按照現(xiàn)在的模塊基址進(jìn)行重定位。
以上三步,前兩部涉及到了編譯和鏈接的知識(shí),跟本文的關(guān)系不大,我們直接看第三步,這一步符合本系列的特征。
在查看重定位表的定義前,我們先了解一下他的存儲(chǔ)方式,有助于后面的理解。按照常規(guī)思路,每個(gè)重定位項(xiàng)應(yīng)該是一個(gè)DWORD,里面保存需要重定位的RVA,這樣只需要簡(jiǎn)單操作便能找到需要重定位的項(xiàng)。然而,Windows并沒(méi)有這樣設(shè)計(jì),
原因是這樣存放太占用空間了,試想一下,加入一個(gè)文件有n個(gè)重定位項(xiàng),那么就需要占用4*n個(gè)字節(jié)。所以Windows采用了分組的方式,按照重定位項(xiàng)所在的頁(yè)面分組,每組保存一個(gè)頁(yè)面起始地址的RVA,頁(yè)內(nèi)的每項(xiàng)重定位項(xiàng)使用一個(gè)WORD保存重定位項(xiàng)在頁(yè)內(nèi)的偏移,這樣就大大縮小了重定位表的大小。
有了上面的概念,我們現(xiàn)在可以來(lái)看一下基址重定位表的定義了:
[cpp] view plaincopy
typedefstruct_IMAGE_BASE_RELOCATION{
DWORDVirtualAddress;
DWORDSizeOfBlock;
//WORDTypeOffset[1];
}IMAGE_BASE_RELOCATION;
typedefIMAGE_BASE_RELOCATIONUNALIGNED*PIMAGE_BASE_RELOCATION;
VirtualAddress:頁(yè)起始地址RVA。
SizeOfBlock:表示該分組保存了幾項(xiàng)重定位項(xiàng)。
TypeOffset:這個(gè)域有兩個(gè)含義,大家都知道,頁(yè)內(nèi)偏移用12位就可以表示,剩下的高4位用來(lái)表示重定位的類型。而事實(shí)上,Windows只用了一種類型IMAGE_REL_BASED_HIGHLOW,數(shù)值是3。
好了,有了以上知識(shí),相信大家可以很容易的寫出自己修正重定位表的代碼,不如自己做個(gè)練習(xí)驗(yàn)證一下吧。
本文by evil.eagle 轉(zhuǎn)載的時(shí)候請(qǐng)注明出處。http://blog.csdn.net/evileagle/article/details/12886949
最后,還是總結(jié)一下,哪些項(xiàng)目需要被重定位呢?
1.代碼中使用全局變量的指令,因?yàn)槿肿兞恳欢ㄊ悄K內(nèi)的地址,而且使用全局變量的語(yǔ)句在編譯后會(huì)產(chǎn)生一條引用全局變量基地址的指令。
2.將模塊函數(shù)指針賦值給變量或作為參數(shù)傳遞,因?yàn)橘x值或傳遞參數(shù)是會(huì)產(chǎn)生mov和push指令,這些指令需要直接地址。
3.C++中的構(gòu)造函數(shù)和析構(gòu)函數(shù)賦值虛函數(shù)表指針,虛函數(shù)表中的每一項(xiàng)本身就是重定位項(xiàng),為什么呢?大家自己考慮一下吧,不難哦~
總結(jié)
以上是生活随笔為你收集整理的PE文件结构详解(六)重定位的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MySQL 索引 :哈希索引、B+树索引
- 下一篇: Http请求方法之Get、Post原理