1. 程式人生 > >PE格式第七講,重定位表

PE格式第七講,重定位表

作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文連結進行轉載:)

一丶何為重定位(注意,不是重定位表格)

首先,我們先看一段程式碼,比如呼叫Printf函式,使用OD檢視.

那麼大家有沒有想過這麼一個問題,函式的字串偏移是00407030位置,函式Call的地址是00401020的位置

但是如果模組首地址申請不到了,變為了00100000的位置,那麼此時的偏移是不是都是錯的了?

首先說下,一般重定位表格都是DLL中的,因為滿足不了模組首地址的需求,所以會遇到函式的重定位問題.

那麼如果磨壞地址變為了00100000的位置,那麼對應的字串位置是不是也要變為00107030的位置,而Call的地址,是不是也要變為00101020的位置

那麼這個就叫做重定位,我們要把偏移,以及各種需要修正的位置,變為正確的.

二丶重定位表格如何設計?

首先我們自己先想一下,重定位的表格要如何設計?

我猜想,你要儲存模組的地址  ,修改地址,偏移, 以及大小.

新的模組 ImageBase

舊的模組 iMageBase

修改的地址 

偏移

修改的大小

那麼如果這樣設計會不會出現問題?

會出現很多問題,比如佔得位元組太多了,如果是Kerner32.dll裡面都是這樣設計,那麼得要多少記憶體.

那麼進一步的優化

可不可以一個分頁,儲存修改的偏移,以及長度

分頁: page  (DWORD) 佔4個位元組

大小: size (DWORD)     偏移:offset(DWORD)

表格設計為上面的,

感覺這樣可以了.但是感覺還可以進一步的優化,大小,以及偏移都佔4個位元組,是不是浪費了

而且如果記錄一個分頁中的重定位的資料,那麼偏移是不會超過12位的(二進位制12位,轉為10進位制是1024),  那麼如果一個DWORD儲存檔案偏移,那麼高4位是沒有用的.

而且我們發現,大小也是很佔位置的.大小一個位元組就可以表示了,比如0 做對齊使用,1修改高16位的偏移,2修改低16位的偏移,3修改4個位元組大小

那麼是不是可以合併了

page  (DWORD)

sizeofoffset

0x3005    代表的意思就是看高位,3代表我要修改4個位元組,005代表修改的當前頁的偏移位置.

三丶真正的重定位表格

看下重定位表格的真正的結構體吧.

程式碼:

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;            頁儲存的RVA
    DWORD   SizeOfBlock;              word型別陣列的個數,也就是下面註釋的
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

那麼看看是不是和我們猜想的一樣,我們隨便找個DLL,在資料目錄中定位重定位表格

1.尋找資料目錄RVA偏移

我們首先要找到資料目錄中重定位表格的RVA偏移然後判斷屬於哪個節,通過公式轉化,得到在檔案中的實際偏移位置.

得出RVA = 6000h

2.判斷屬於哪個節

我們發現,新增加了一個節,這個節就是重定位的節然後虛擬地址是6000位置,而且在檔案偏移的位置也是6000h

那麼我們就得出 FA = RVA了,那麼就不用算了,可以確定,檔案偏移位置就是6000就是重定位表的位置

 

3.定位檔案偏移處,檢視排列

然後可以看出 前八個位元組分別儲存頁的RVA偏移,以及大小,.我們使用計算器計算一下,看看有多大

計算的出 160h,這個大小,儲存的是陣列大小+上我們八個位元組的總大小,也就是說160 - 8 = 陣列的大小了.

可以看出確實是怎麼大,然後就到記錄下一個分頁了.

四丶陣列解析檢視

 那麼按照我們的想法看上面重定位表中的陣列的第一個,按照小尾方式讀取則是

0x3005  那麼高位是3那麼就是要修改大小是4個位元組,005則是代表偏移.

至於高位怎麼檢視,VC++6.0中的巨集已經定義了.

程式碼:

複製程式碼

#define IMAGE_REL_I386_ABSOLUTE         0x0000  // Reference is absolute, no relocation is necessary
#define IMAGE_REL_I386_DIR16            0x0001  // Direct 16-bit reference to the symbols virtual address
#define IMAGE_REL_I386_REL16            0x0002  // PC-relative 16-bit reference to the symbols virtual address
#define IMAGE_REL_I386_DIR32            0x0006  // Direct 32-bit reference to the symbols virtual address
#define IMAGE_REL_I386_DIR32NB          0x0007  // Direct 32-bit reference to the symbols virtual address, base not included
#define IMAGE_REL_I386_SEG12            0x0009  // Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address
#define IMAGE_REL_I386_SECTION          0x000A
#define IMAGE_REL_I386_SECREL           0x000B
#define IMAGE_REL_I386_REL32            0x0014  // PC-relative 32-bit reference to the symbols virtual address

複製程式碼

這裡只需要知道0 1 2 代表的意思即可,因為0x3005的高位是 用位運算 | 上去的,所以3代表的是1 和 2的組合

0  對齊使用

1.修改高16位  

2.修改低16位

1和2 使用位運算|起來就是修改4個位元組.

1.定位修改位置

那麼怎麼定位要修改的位置那?

 公式:

現在的ImageBase(模組地址) +  當前分頁大小的虛擬地址 +5的位置等於要修改的位置:

比如假設我們的現在的模組地址是00400000位置,而DLL以前的位置是10000000  而它以前的字串的偏移是   10003045

首先定位修改地址:

00400000 + 1000 + 005 = 401005  那麼在401005的位置就是你要修改的位置

比如我們在寫一個

0x3096 =  400000 + 1000 + 96 = 401096  那麼定位的位置就是401096是你要修改的偏移,大小是4個位元組,高位為3  為什麼是4個位元組,一會看下內部儲存

2.修改的偏移計算

比如我們呼叫一個printf 

push 10003096    "HelloWorld"

call    10004086  

那麼我們要修正他的偏移

我們現在得知,以前的DLL偏移是  10000000    以前的字串偏移是  10003096 ,不過因為DLL的模組地址沒有滿足,那麼現在的模組地址變為了00400000的位置

那麼我們要修正偏移

公式:

現在的ImageBase (00400000) - 以前的ImageBase(10000000) + 以前的偏移(10003096)

這樣寫彙編程式碼好寫,如果便於理解的話,可以寫成下面那樣,只不過你需要知道的是彙編程式碼就是上面這種寫法就行

以前的偏移(10003096)  - 以前的ImageBase(10000000) + 現在的ImageBase(00400000)  

= 3096 + 400000

= 403096  (計算出來的偏移)

那麼push的位置就成了 403096了,已經重定位了.

 

五丶實戰演練檢視

 因為DLL中的重定位表中的項太多,所以這裡使用一個EXE(沒有匯出函式的EXE),然後注入這個DLL,那麼這個EXE就有重定位表格了.

首先我們先看DLL, 3005的位置要重定位

按照公式我們得出,要修改的位置是

現在模組地址 + 當前表中記錄分頁 + 陣列中後三位的偏移(上面說過,如果按照分頁儲存,那麼3位就可以表達一個分頁需要記錄的了)

那麼現在  我們的EXE的模組地址是00500000 + 1000(重定位表中第一項成員) + 005  (這是一個數組,第一個成員是0x3005  取出後三位則是005)

得出修改的位置是  00501005的位置,我們OD中CTRL+ G看看這個位置是不是要修正.

程式碼亂了,那麼我們可能斷掉指令了,那麼此時CTRL + A重新分析一下.

 

 可以看出,我們修正的位置是501005的位置,不過彙編程式碼在501004才能顯示出來,501005後面正好是要修正的地址,那麼只需要計算偏移填進去就可以了,大小是按照高4個位元組, 現在0x3005 高位是3那麼代表了要修正4個位元組的偏移.

算出偏移位置:

偏移位置我們要進行反推了

因為OD已經幫我們重定位好了.

503000 = 現在的ImageBase - 以前的ImageBase + 以前的偏移 = 現在的偏移(503000)

那麼現在計算以前的偏移

以前的偏移 = 現在的偏移 - 現在的ImageBase + 以前的ImageBase 

=  503000 - 50000 + 60000000 

= 3000 + 60000000

= 60003000 (以前的偏移)

那麼算出了以前的偏移,我們就計算這4個位元組要填寫的偏移,也就是503000怎麼得出來的

公式上面說了:

 Cur (縮寫,代表當前的意思)  Old(代表舊的意思)  offset(代表偏移的意思)

CurImageBase - OldImageBase + OldOffset = 要填入的偏移

代入公式:

00500000  -  60000000  + 60030000 = 00503000 (要填寫的檔案偏移)

看下我們當前的模組地址:

Inject是我們當前的EXE的名稱,模組地址在00500000的位置

DLL的模組地址是60000000  這個地址是我們通過修改DLL中的選項頭中的ImageBase得到的.

 

 六丶總結

 上面講的很細緻

今天主要就是結構體會看,偏移會算即可.

總結一下公式

1.定位重定位的地址  (也就是在哪裡修改)

首先從陣列取出一項,(2個位元組大小)

比如0x3005

公式:

定位修改地址  = 現在模組 + 當前結構記錄分頁的RVA  + 取出陣列的2個位元組的低3位

例子: 00401000 + 1000 + 005 = 世紀你要修改的地址,修改大小和取出word自己的第一位有關

2.計算出偏移地址,填寫到定位地址的位置,使其偏移正確

現在的模組地址  - DLL模組地址 + 以前的偏移 = 實際修改的偏移

便於理解的公式:

以前的偏移 - DLL模組地址 + 現在模組地址 

3.計算出以前偏移

要計算出以前的偏移,你首先要得出現在的偏移,好在OD已經寫好了,其實檔案中也有儲存的.(自己找吧)

以前的偏移 = 現在的偏移 - 現在模組地址 + DLL模組地址

 

作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文連結進行轉載:)

堅持兩字,簡單,輕便,但是真正的執行起來確實需要很長很長時間.當你把堅持兩字當做你要走的路,那麼你總會成功.

分類: PE檔案格式

標籤: PE重定位表格PE重定位PE格式詳解