天天看點

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

上一講:小甲魚PE詳解之輸入表(導入表)詳解1

在此之前,我們已經對這個輸入表進行了一些實踐和了解,這有助于大家對這個概念更進一步的加深認識。小甲魚覺得,越是複雜的問題我們應該越是去動手操作它,認識它,這樣才容易熟悉它!

在上一節課我們像小鹿一樣的亂撞,終于撞到了輸入表裡邊包含的函數名稱,嘿嘿,不過位址,我們還是沒能找着……這節課我們将深入來剖析輸入表的結構,通過結合執行個體分析來幫助大家了解輸入表的工作原理。

輸入表結構

回顧一下,在 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明顯是親家,兩家夥首先名字就差不多哈。那他們有什麼不可告人的秘密呢?來,我們看下面一張圖(畫的很辛苦,大家仔細看哈)

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

我們看到: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 檔案裝載記憶體後準備執行時,剛剛的圖就會轉化為下圖:

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

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

輸入表執行個體分析:(具體過程将在視訊中示範,這裡不啰嗦啦~)

工具:PEinfo.exe, UltraEdit, LordPE

解剖對象:hello.exe

友情提示:以上工具均可在此處下載下傳無病毒無二次污染版本,在其他地方下載下傳謹慎病毒威脅。

下一講:小甲魚PE詳解之輸入表(導出表)

繼續閱讀