一丶何為重定位(注意,不是重定位表格)
首先,我們先看一段代碼,比如調用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模塊地址