PE檔案格式詳解(五)
0x00 前言
前一篇瞭解了區塊虛擬地址和檔案地址轉換的相關知識,這一篇該把我們所學拿出來用用了。這篇我們將瞭解更為重要的一個知識點——輸入表和輸出表的知識。
0x01 輸入表
首先我們有疑問。這個輸入表是啥?為啥有輸入表?其實輸入表就是記錄PE輸入函式相關資訊的一張表。那為什麼要有這張表?答:原來PE檔案執行過程中並不是獨立執行的,它必須要藉助window系統的需對函式才能完成其功能。常見的如USER32,KERNEL32等DLL。
輸入表所起的作用就是幫助載入的PE找到所需呼叫的函式。
在PE檔案中,有個專門的陣列,他們分別對每個被輸入的
在之前講過的PE檔案頭的IMAGE_OPTIONAL_HAEDER結構中的資料目錄表即DataDirectory[16]中第二個成員Imaport Table指向輸入表。輸入表是一個由IMAGE_IMPORT_DESCRIPORT(簡稱IID)的結構組成的陣列。IID的結構如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD OriginalFirstThunk;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
下面介紹幾個重要欄位:
OriginalFirstThunk:這個欄位包含指向輸入名稱表(簡稱
直接看這個描肯定感覺很繞,下面看看這幾個結構的關係圖,希望能夠幫助理解:
其實就是1指向2再指向3
Name:輸入的DLL的名字指標,它是一個以00結尾的ASCII字元的RVA地址,該字串包含輸入的DLL名。例如:KERNEL32.DLL,或者USER32.DLL。
FirstThunk:包含指向輸入地址表(IAT)的RVA。IAT也是指向IAMGE_THUNK_DATA結構。
這裡的FristThunk和OringinalFristThunk極為相似,作用也很類似,但是作用的先後有不同,後面將會做詳細講解。
下面來重點看看這個起著巨大作用的IMAGE_THUNK_DATA結構。
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
ForwarderString 指向一個轉向者字串的RVA;
Function 被輸入的函式的記憶體地址;
Ordinal 被輸入的API的序數值
AddressOfData 指向IMAGE_IMPORT_BY_NAME
標識位黃色的這幾個欄位都很重要,是的這個結構的欄位全都很重要!
下面來看看AddressOfData欄位指向的IMAGE_IMPORT_BY_NAME結構
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME;
這個結構很簡單,只有兩個欄位:
Hint欄位:指示本函式在其所駐留的輸出表的中序號該域被PE裝載器用來在DLL的輸出表裡快速查詢。該值不是必須的,一些連結器將此值設為0;
NAME欄位:這個欄位比較重要。它含有輸入函式的函式名,函式名是一個ASCII碼字串,並以NULL結尾。注意,這裡雖然將NAME的大小定義為位元組,其實他是可變的。
0x02 輸入地址表
接下來要講的才是本文裡最為關鍵的部分。通過上面的瞭解大概我們都會疑惑為啥這兩個陣列都要指向IMAGR_IMPORT_BY_NAME結構?原因如下:
第一,第一個由OriginalFrist通過IMAGE_THUNK_DATA結構所指向的IMAGE_IMPORT_BY_NAME是單獨的一項,而且IMAGE_THUNK_DATA的值不可以更改,這個IMAGE_THUNK_DATA組成的陣列就是INT,其實它是為FristThunk做為提示用的。
第二,第二個由FristThunk所指向的IMAGE_THUNK_DATA的值是由PE裝載器填寫的,他們的值構成了IAT。PE裝載器首先搜尋OringinalFristThunk,通過它所指向的INT結構中的每個IMAGE_IMPORT_BY_NAME所指向的每個被載入函式的地址。然後通過載入器將值填充到FristThunk指向的IAT表中。
接下來對比一下載入前後的INT表和IAN表值變化:
載入前
載入後,顯示IAT的值已經填充了函式地址
我們最後要用到的即使PE載入後的ITA表。
0x03 例項講解如何找到輸入表的FileOffset
前面我們已經詳細講解了怎麼找到資料目錄表,這裡就不再綴述。我們直接找到資料目錄表的第二項,它的位置在180h處如下圖:
由此可知輸入表的RVA值為3000h,大小為52h。
其實我們也可以可以直接在lordPE的區段表中找到FileOffset的值如下圖:
值為A00h。
用hexwrokshop直接跳轉到該位置(大小為52h)。如下圖:
由於IMAGE_THUNK_DATA的五個欄位都是雙字,因此按照八個位元組依次讀出陣列中第一個結構的每個欄位:第一個OriginnalFristThunk為:0000 3028 第一個TimeDateStamp為00000000。第一個ForwardChain為:00000000。第一個Name值為:00003038,第一個FristThunk值為:00003030
也可以用LordPE檢視IMAGE_THUNK_DATA的欄位值及名字如下圖:
可以知道呼叫了的是USE32.DLL。
陣列中的其餘值也可以依次讀寫出來