PE文件結構(三)
參考
書:《加密與解密》
視頻:小甲魚 解密系列 視頻
輸入表
輸入函數,表示被程序調用但是它的代碼不在程序代碼中的,而在dll中的函數。對於這些函數,磁盤上的可執行文件只是保留相關的函數信息,如函數名,dll文件名等。在程序運行前,程序是沒有保存這些函數在內存中的地址。當程序運行起來時,windows加載器會把相關的dll裝入內存,並且將輸入函數的指令與函數真在內存中正的地址聯繫起來。輸入表(導入表)就是用來保存這些函數的信息的。
在 IMAGE_OPTIONAL_HEADER 中的 DataDirectory[16] 數組保存了 輸入表的RVA跟大小。通過RVA可以在OD中加載程序通過 ImageBase+RVA 找到 輸入表,或者通過RVA計算出文件偏移地址,查看磁盤中的可執行文件,通過文件偏移地址找到輸入表。
輸入表是以一個IMAGE_IMPORT_DESCRIPTOR(IID)數組 開始的,每一個被PE文件隱式的鏈接進來的dll都有一個IID,IID數組的最後一個單元用NULL表示。
IMAGE_IMPORT_DESCRIPTOR 結構:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
_ANONYMOUS_UNION union { //00h
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp; //04h
DWORD ForwarderChain; //08h
DWORD Name; //0Ch
DWORD FirstThunk; //10h
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
其中Name是dll名字的指針。OriginalFirstThunk指向一個IMAGE_THUNK_DATA數組叫做輸入名稱表Import Name Table(INT),用來保存函數,FirstThunk也指向IMAGE_THUNK_DATA數組叫做輸入地址表Import Address Table(IAT)。
IMAGE_THUNK_DATA 結構:
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;
當IMAGE_THUNK_DATA 的值最高位爲1時,表示函數是以序號方式輸入,這時低31爲被當作函數序號。當最高位是0時,表示函數是以字符串類型的函數名方式輸入的,這時,IMAGE_THUNK_DATA 的值爲指向 IMAGE_IMPORT_BY_NAME 的結構的RVA。
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
Hint 表示這個函數在其所駐留dll的輸出表的序號,不是必須的。
Name 表示 函數名,是一個ASCII字符串以0結尾,大小不固定。
INT保存的是這個程序導入這個dll中函數信息,它是固定的不會被修改。但是IAT會在程序加載時被重寫,當程序加載時,它會被PE加載器重寫成 這些函數的在內存中的真正地址。即把它原來指向的IMAGE_IMPORT_BY_NAME 改成 函數真正的地址。
那爲什麼要兩個 IMAGE_THUNK_DATA 數組?
當程序加載時,IAT 會被PE加載器重寫,PE加載器先搜索INT,PE加載器迭代搜索INT數組中的每個指針,找出 INT所指向的IMAGE_IMPORT_BY_NAME結構中的函數在內存中的真正的地址,並把它替代原來IAT中的值。當完成後,INT就沒有用了,程序只需要IAT就可以正常運行了。
看下面圖,這個是可執行程序在磁盤中的時候:
這個是當程序被加載的是後:
實例分析:
先找到輸入表RVA,通過IMAGE_OPTIONAL_HEADER 中的最後一個項 IMAGE_DATA_DIRECTORY 可以知道 輸入表相對與PE文件頭的偏移量爲80h可以找到輸入表達RVA。
圖片1
但這個是RVA不是文件偏移地址。通過轉換可以知道,輸入表的文件偏移地址爲850h,
圖片2 IID數組
查看OriginalFirstThunk跟FirstThunk ,可以發現這裏INT跟IAT的內容是一樣的,先看看第一個函數的信息,因爲第一位爲1,所以這裏00002122h 表示 IMAGE_IMPORT_BY_NAME 的RVA,轉化爲文件偏移值爲922h
圖片3 INT跟IAT
查看922h 可以看函數名。
圖片4:
我們可以從上面看到程序在磁盤中時 INT 與IAT內容一樣,都是指向 IMAGE_IMPORT_BY_NAME 。用OD加載程序,查看INT與IAT的內容
圖片5 INT
圖片6 IAT
可以發現INT沒有發生變化,IAT變成了例如77D3C702h,IAT中的RVA被改成了函數的真正的地址。
查看77D3C702h,就可以看到這個函數
圖片7