PE文件結(jié)構(gòu)詳解(二)可執(zhí)行文件頭的最后展示了一個數(shù)組,,PE文件結(jié)構(gòu)詳解(三)PE導出表中解釋了其中第一項的格式,本篇文章來揭示這個數(shù)組中的第二項:IMAGE_DIRECTORY_ENTRY_IMPORT,,即導入表,。
也許大家注意到過,在IMAGE_DATA_DIRECTORY中,,有幾項的名字都和導入表有關(guān)系,,其中包括:IMAGE_DIRECTORY_ENTRY_IMPORT,IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,,IMAGE_DIRECTORY_ENTRY_IAT和IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT這幾個導入都是用來干什么的,,他們之間又是什么關(guān)系呢?聽我慢慢道來,。
- IMAGE_DIRECTORY_ENTRY_IMPORT就是我們通常所知道的導入表,,在PE文件加載時,會根據(jù)這個表里的內(nèi)容加載依賴的DLL,,并填充所需函數(shù)的地址,。
- IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT叫做綁定導入表,在第一種導入表導入地址的修正是在PE加載時完成,,如果一個PE文件導入的DLL或者函數(shù)多那么加載起來就會略顯的慢一些,,所以出現(xiàn)了綁定導入,,在加載以前就修正了導入表,這樣就會快一些,。
- IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT叫做延遲導入表,一個PE文件也許提供了很多功能,,也導入了很多其他DLL,,但是并非每次加載都會用到它提供的所有功能,也不一定會用到它需要導入的所有DLL,,因此延遲導入就出現(xiàn)了,,只有在一個PE文件真正用到需要的DLL,這個DLL才會被加載,,甚至于只有真正使用某個導入函數(shù),,這個函數(shù)地址才會被修正。
- IMAGE_DIRECTORY_ENTRY_IAT是導入地址表,,前面的三個表其實是導入函數(shù)的描述,,真正的函數(shù)地址是被填充在導入地址表中的。
舉個實際的例子,,看一下下面這張圖:
這個代碼調(diào)用了一個RegOpenKeyW的導入函數(shù),,我們看到其opcode是FF 15 00 00 19 30氣質(zhì)FF 15表示這是一個間接調(diào)用,即call dword ptr [30190000] ;這表示要調(diào)用的地址存放在30190000這個地址中,,而30190000這個地址在導入地址表的范圍內(nèi),,當模塊加載時,PE 加載器會根據(jù)導入表中描述的信息修正30190000這個內(nèi)存中的內(nèi)容,。
那么導入表里到底記錄了那些信息,,如何根據(jù)這些信息修正IAT呢?我們一起來看一下導入表的定義:
- typedef struct _IMAGE_IMPORT_DESCRIPTOR {
- union {
- DWORD Characteristics; // 0 for terminating null import descriptor
- DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
- } DUMMYUNIONNAME;
- DWORD TimeDateStamp; // 0 if not bound,
- // -1 if bound, and real date\time stamp
- // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
- // O.W. date/time stamp of DLL bound to (Old BIND)
-
- DWORD ForwarderChain; // -1 if no forwarders
- DWORD Name;
- DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
- } IMAGE_IMPORT_DESCRIPTOR;
- typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
使用RtlImageDirectoryEntryToData并將索引號傳1,,會得到一個如上結(jié)構(gòu)的指針,,實際上指向一個上述結(jié)構(gòu)的數(shù)組,每個導入的DLL都會成為數(shù)組中的一項,,也就是說,,一個這樣的結(jié)構(gòu)對應一個導入的DLL。
Characteristics和OriginalFirstThunk:一個聯(lián)合體,,如果是數(shù)組的最后一項Characteristics為0,,否則OriginalFirstThunk保存一個RVA,指向一個IMAGE_THUNK_DATA的數(shù)組,,這個數(shù)組中的每一項表示一個導入函數(shù),。
TimeDateStamp:映象綁定前,這個值是0,,綁定后是導入模塊的時間戳,。
ForwarderChain:轉(zhuǎn)發(fā)鏈,,如果沒有轉(zhuǎn)發(fā)器,這個值是-1,。
Name:一個RVA,,指向?qū)肽K的名字,所以一個IMAGE_IMPORT_DESCRIPTOR描述一個導入的DLL,。
FirstThunk:也是一個RVA,,也指向一個IMAGE_THUNK_DATA數(shù)組。
既然OriginalFirstThunk與FirstThunk都指向一個IMAGE_THUNK_DATA數(shù)組,,而且這兩個域的名字都長得很像,,他倆有什么區(qū)別呢?為了解答這個問題,,先來認識一下IMAGE_THUNK_DATA結(jié)構(gòu):
- typedef struct _IMAGE_THUNK_DATA32 {
- union {
- DWORD ForwarderString; // PBYTE
- DWORD Function; // PDWORD
- DWORD Ordinal;
- DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
- } u1;
- } IMAGE_THUNK_DATA32;
- typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
ForwarderString是轉(zhuǎn)發(fā)用的,,暫時不用考慮,Function表示函數(shù)地址,,如果是按序號導入Ordinal就有用了,,若是按名字導入AddressOfData便指向名字信息。可以看出這個結(jié)構(gòu)體就是一個大的union,,大家都知道union雖包含多個域但是在不同時刻代表不同的意義那到底應該是名字還是序號,,該如何區(qū)分呢?可以通過Ordinal判斷,,如果Ordinal的最高位是1,,就是按序號導入的,這時候,,低16位就是導入序號,,如果最高位是0,則AddressOfData是一個RVA,,指向一個IMAGE_IMPORT_BY_NAME結(jié)構(gòu),,用來保存名字信息,由于Ordinal和AddressOfData實際上是同一個內(nèi)存空間,,所以AddressOfData其實只有低31位可以表示RVA,,但是一個PE文件不可能超過2G,所以最高位永遠為0,,這樣設計很合理的利用了空間,。實際編寫代碼的時候微軟提供兩個宏定義處理序號導入:IMAGE_SNAP_BY_ORDINAL判斷是否按序號導入,IMAGE_ORDINAL用來獲取導入序號,。
這時我們可以回頭看看OriginalFirstThunk與FirstThunk,,OriginalFirstThunk指向的IMAGE_THUNK_DATA數(shù)組包含導入信息,在這個數(shù)組中只有Ordinal和AddressOfData是有用的,,因此可以通過OriginalFirstThunk查找到函數(shù)的地址,。FirstThunk則略有不同,,在PE文件加載以前或者說在導入表未處理以前,他所指向的數(shù)組與OriginalFirstThunk中的數(shù)組雖不是同一個,,但是內(nèi)容卻是相同的,,都包含了導入信息,而在加載之后,,FirstThunk中的Function開始生效,,他指向?qū)嶋H的函數(shù)地址,因為FirstThunk實際上指向IAT中的一個位置,,IAT就充當了IMAGE_THUNK_DATA數(shù)組,加載完成后,,這些IAT項就變成了實際的函數(shù)地址,,即Function的意義。還是上個圖對比一下:
上圖是加載前,。
上圖是加載后,。
最后總結(jié)一下:
- 導入表其實是一個IMAGE_IMPORT_DESCRIPTOR的數(shù)組,每個導入的DLL對應一個IMAGE_IMPORT_DESCRIPTOR,。
- IMAGE_IMPORT_DESCRIPTOR包含兩個IMAGE_THUNK_DATA數(shù)組,,數(shù)組中的每一項對應一個導入函數(shù)。
- 加載前OriginalFirstThunk與FirstThunk的數(shù)組都指向名字信息,,加載后FirstThunk數(shù)組指向?qū)嶋H的函數(shù)地址,。
by
evil.eagle 轉(zhuǎn)載請注明出處。
http://blog.csdn.net/evileagle/article/details/12357155
|