PE格式解析-重定位表分析
vs中是否開啟隨機基址選項
一、隨機基址與非隨機基址的區別
1、非隨機基址(固定基址)
在非隨機基址的條件下,如果我們需要呼叫一個函式(函式地址為00408843),採取的彙編命令一般為JMP 00408843或 CALL 00408843
2、隨機基址
開啟隨機基址以後,基址00400000將在每次應用程式執行的時候產生基址的變化,可能變成 00430000、004d0000…,此時編譯器在編譯程式的時候,無法直接將基址寫成00400000,但是程式必須執行,此時就需要重定位的實現,使得程式在執行的時候,程式在記憶體中的地址隨著基址的變化而變化。
Windows根據重定位表來解決隨機基址的問題。在重定位表中,會記錄很多的專案,PE檔案載入器在載入應用程式到記憶體的時候,就會去查詢是否有重定位項,如果有,就重新計算對應專案的基址的值,更新記憶體資料。也就是說,當你的應用程式剛載入到應用程式的時候,Windows的PE載入器就負責幫你將基址都進行更新。
例如:在基址不變的情況下是 0x00400000,此時打開了隨機基址,使得程式在被PE載入器載入的時候,就將基址更新為0x00450000,那麼上面提到的呼叫函式地址就變成了JMP 00458843與CALL 00458843.
二、對重定位表的分析
PE平面結構圖:
測試例子:test.exe及完整PE平面結構圖
1、重定位表(basereloc)
前四位為重定位表的RVA值,後四位為重定位表的大小(SIZE)
重定位表RVA:0001D000->檔案偏移(00019800)
重定位表SIZE:0000018C
LoadPE檢視重定位:
從檔案偏移0x19800處擷取0x18C個位元組(重定位表的二進位制資訊)
注:以下為區段表資訊
所有重定位資料裡面會分成幾個大的部分,每個部分有一個頭部,佔用8個位元組,分別代表本部分的專案所對應的程式區段的起始RVA以及本部分的大小,剩下的每兩個位元組表示一個重定位專案。
本示例中,一共有兩大部分,即.text與.rdata部分
2、接下來我們就來以第一大部分分析,即.text部分
例如重定位表的前八個位元組:00 10 00 00 68 01 00 00
前四個位元組的值:00001000 代表.text區段的起始RVA
後四個位元組的值:00000168 代表該重定位部分的總大小0x168
接下來的每兩個位元組:例如 07 30=3007(高四位始終是3,Windows固定為3)真正有效的資料為0007,則根據起始RVA,計算出第一個需要重定位的專案位置為: 1000+0007=1007
第二個需要重定位的專案:1013
第三個需要重定位的專案:101E
第四個需要重定位的專案:102A
…
該部分需要重定位的總個數為 (0x168-0x8(前八個位元組))/2=0xB0=176d
同理.rdata部分
.rdata區段的起始RVA:00002000
.rdata區段的大小:00000024
專案個數:(0x24-0x8)/2=0xE=14d
第一個專案:2108
第二個專案:2110
第三個專案:211C
…
3、重定位的流程
使用bpx MessageBoxW對下MessageBoxW斷點
跳到斷點處,即對話方塊處,對話方塊中顯示的是全域性變數szTips
向MessageBoxW壓入szTips字串處的彙編指令的地址為 0x010E12D0,容易得出0x010E0000為程式的基址,所以szTips的RVA值為0x12D0(檔案偏移量為0x6D0)
在檔案偏移0xx6D0處,可以看到彙編指令為68 80 33 40 00(68 表示push指令),和使用OD檢視到的指令(載入到記憶體後)68 80330E01前3個位元組相同,但是後面兩個位元組,一個為 40 00 ,一個為 0E 01,兩個並不相同。這兩個的差別就是由於重定位的實現導致的。
Windows是如何進行重定位的?
1)讀取檔案中的值,例如 80 33 40 00->00403380
2)讀取檔案中的基址(從Image_Optional_Header中的ImageBase獲取)00400000
3)兩個相減 00403380-00400000=3380
4)讀取檔案實際載入的基址 例如本示例中的 010E0000
5)更新重定位項 010E0000+3380=010E3380->80330E01
注:如果在隨機基址下,直接載OD中修改重定位項將會出錯。
例如:直接修改的檔案中的值為 00909090
檔案的基址為00400000,相減得 00509090,此時檔案實際載入的基址為 01000000,那麼此時重定位項在記憶體中的位置為 01509090,下次檔案實際載入的基址為 02000000時,此時重定位項在記憶體中的位置為 02509090。可以發現,前後兩次載入程式後的重定位的位置01509090與02509090位置並不相同,如果盲目修改,在再次載入程式的時候將會出錯。
4、手動去掉示例程式的對話方塊內容(修改szTips值)
注:直接在OD中使用nop填充,會出現錯誤
通過上面我們知道,MesageBoxW重定位的引數RVA為0x12D1,檔案偏移為0x6D1(開始位置為0x6D0後的68 push指令之後,即多一個位元組0x6D1)
1)0x12D0可以看出,該部分屬於0x1000,即.text部分,所以我們要找的重定位專案為 0x12D1-0x1000=0x2D1=>D1 32
2)在.text部分查詢 D1 32
3)將D1 32填充為0,將會失敗,因為重定位的專案個數在前8個位元組中已經固定了,填充0後將會使得提前截斷,導致重定位的專案個數減少,產生未知的錯誤(本例中會產生亂碼)
即:
刪除重定位項的方法為:將要刪除的重定位項移到最後,再將隨後的二進位制填充為0。這樣就避免了重定位項的缺少以及其他重定位項的無法實現問題
4)我們還可以更加的改進完美,可以修改.text重定位部分的大小為0x166,同時將.rdata部分向前移動兩個位元組.
示例
對於重定位表的大小還要進行修改,從0x18C改為0x18A
總結以上步驟:找出需要重定位的專案在哪個部分->分析出重定位的專案是哪個->在該部分裡找到目標專案->對目標專案進行nop(其餘往前移動,該部分同時修改該部分的大小->另存.exe檔案
本文難免有所錯誤,如有問題歡迎留言