小甲魚PE詳解之輸入表(導入表)詳解2(PE詳解08)

輸入表結構

回顧一下,在 PE文件頭的 IMAGE_OPTIONAL_HEADER 結構中的 DataDirectory(數據目錄表) 的第二個成員就是指向輸入表的。而輸入表是以一個 IMAGE_IMPORT_DESCRIPTOR(簡稱IID) 的數組開始。每個被 PE文件鏈接進來的 DLL文件都分別對應一個 IID數組結構。在這個 IID數組中,並沒有指出有多少個項(就是沒有明確指明有多少個鏈接文件),但它最後是以一個全爲NULL(0) 的 IID 作爲結束的標誌。


IMAGE_IMPORT_DESCRIPTOR 結構定義如下:
IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics              DWORD   ?
OriginalFirstThunk        DWORD   ?
ends
TimeDateStamp                     DWORD   ?
ForwarderChain                     DWORD   ?
Name                                    DWORD   ?
FirstThunk                            DWORD   ?
IMAGE_IMPORT_DESCRIPTOR ENDS

成員介紹:

OriginalFirstThunk
它指向first thunk,IMAGE_THUNK_DATA,該 thunk 擁有 Hint 和 Function name 的地址。

TimeDateStamp
該字段可以忽略。如果那裏有綁定的話它包含時間/數據戳(time/data stamp)。如果它是0,就沒有綁定在被導入的DLL中發生。在最近,它被設置爲0xFFFFFFFF以表示綁定發生。

ForwarderChain
一般情況下我們也可以忽略該字段。在老版的綁定中,它引用API的第一個forwarder chain(傳遞器鏈表)。它可被設置爲0xFFFFFFFF以代表沒有forwarder。

Name
它表示DLL 名稱的相對虛地址(譯註:相對一個用null作爲結束符的ASCII字符串的一個RVA,該字符串是該導入DLL文件的名稱,如:KERNEL32.DLL)。

FirstThunk
它包含由IMAGE_THUNK_DATA定義的 first thunk數組的虛地址,通過loader用函數虛地址初始化thunk。在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。


這個
OriginalFirstThunk 和 FirstThunk明顯是親家,兩傢伙首先名字就差不多哈。那他們有什麼不可告人的祕密呢?來,我們看下面一張圖(畫的很辛苦,大家仔細看哈):




我們看到:OriginalFirstThunk 和 FirstThunk 他們都是兩個類型爲IMAGE_THUNK_DATA 的數組,它是一個指針大小的聯合(union)類型。每一個IMAGE_THUNK_DATA 結構定義一個導入函數信息(即指向結構爲IMAGE_IMPORT_BY_NAME 的傢伙,這傢伙稍後再議),然後數組最後以一個內容爲0 的 IMAGE_THUNK_DATA 結構作爲結束標誌。

我們得到
IMAGE_THUNK_DATA 結構的定義如下:
IMAGE_THUNK_DATA STRUC
union u1
ForwarderString       DWORD  ?        ; 指向一個轉向者字符串的RVA
Function                      DWORD  ?        ; 被輸入的函數的內存地址
Ordinal                       DWORD  ?        ; 被輸入的API 的序數值
AddressOfData         DWORD  ?        ; 指向 IMAGE_IMPORT_BY_NAME
ends
IMAGE_THUNK_DATA ENDS

我們可以看出由於是union結構,所以IMAGE_THUNK_DATA 事實上是一個雙字大小。該結構在不同時候賦予不同的意義(偉大神奇不得鳥……)。其實union這種數據結構很容易理解:說白了就是當時窮,能省就省,再說白了,就是幾兄弟姐妹輪流穿一條褲子去相親!理解了吧?哈哈~

那我們怎麼來區分何時是何意義呢?
規定如下:
當 IMAGE_THUNK_DATA 值的最高位爲 1時,表示函數以序號方式輸入,這時候低 31位被看作一個函數序號。
IMAGE_THUNK_DATA 值的最高位爲 0時,表示函數以字符串類型的函數名方式輸入,這時雙字的值是一個 RVA,指向一個 IMAGE_IMPORT_BY_NAME 結構。(演示請看小甲魚解密系列視頻講座)

好,那接着我們討論下指向的這個
IMAGE_IMPORT_BY_NAME 結構IMAGE_IMPORT_BY_NAME 結構僅僅只有一個字型數據的大小,存有一個輸入函數的相關信息結構。其結構如下:
IMAGE_IMPORT_BY_NAME STRUCT
Hint        WORD    ?
Name      BYTE      ?
IMAGE_IMPORT_BY_NAME ENDS
結構中的 Hint 字段也表示函數的序號,不過這個字段是可選的,有些編譯器總是將它設置爲 0,Name 字段定義了導入函數的名稱字符串,這是一個以 0 爲結尾的字符串。
整個過程看起來有點繞有點煩,別急,後邊我們有演示哈。


輸入地址表(IAT)


爲什麼由兩個並行的指針數組同時指向 IMAGE_IMPORT_BY_NAME 結構呢?第一個數組(由 OriginalFirstThunk 所指向)是單獨的一項,而且不能被改寫,我們前邊稱爲 INT。第二個數組(由 FirstThunk 所指向)事實上是由 PE 裝載器重寫的。

好了,那麼 PE 裝載器的核心操作時如何的呢?這裏就給大家揭祕啦~

PE 裝載器首先搜索 OriginalFirstThunk ,找到之後加載程序迭代搜索數組中的每個指針,找到每個 IMAGE_IMPORT_BY_NAME 結構所指向的輸入函數的地址,然後加載器用函數真正入口地址來替代由 FirstThunk 數組中的一個入口,因此我們稱爲輸入地址表(IAT)。所以,當我們的 PE 文件裝載內存後準備執行時,剛剛的圖就會轉化爲下圖:




此時,輸入表中其他部分就不重要了,程序依靠 IAT 提供的函數地址就可正常運行。


輸入表實例分析:(具體過程將在視頻中演示,這裏不囉嗦啦~)


工具:PEinfo.exe, UltraEdit, LordPE
解剖對象:hello.exe
發佈了19 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章