[PE結構分析] 8.輸入表結構和輸入地址表(IAT)


在 PE文件頭的 IMAGE_OPTIONAL_HEADER 結構中的 DataDirectory(數據目錄表) 的第二個成員就是指向輸入表的。每個被鏈接進來的 DLL文件都分別對應一個 IMAGE_IMPORT_DESCRIPTOR (簡稱IID) 數組結構。

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;

在這個 IID數組中,並沒有指出有多少個項(就是沒有明確指明有多少個鏈接文件),但它最后是以一個全為NULL(0) 的 IID 作為結束的標志。

下面只摘錄比較重要的字段:

OriginalFirstThunk

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

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。就個人理解而言:

1. 在文件中時,他們都分別指向一個RVA地址。這個地址轉換到文件中,分別對應兩個以 IMAGE_THUNK_DATA 為元素的的數組,這兩個數組是以一個填充為 0 的IMAGE_THUNK_DATA作為結束標識符。雖然他們這兩個表位置不同,但實際內容是一模一樣的。此時,每個 IMAGE_THUNK_DATA 元素指向的是一個記錄了函數名和相對應的DLL文件名的 IMAGE_IMPORT_BY_NAME結構體。

2. 為什么會有兩個一模一樣的數組呢?是有原因的:

OriginalFirstThunk 指向的數組通常叫做  hint-name table,即 HNT ,他在 PE 加載到內存中時被保留了下來且永遠不會被修改。但是在 Windows 加載過 PE 到內存之后,Windows 會重寫 FirstThunk 所指向的數組元素中的內容,使得數組中每個 IMAGE_THUNK_DATA 不再表示指向帶有函數描述的 IMAGE_THUNK_DATA 元素,而是直接指向了函數地址。此時,FirstThunk 所指向的數組就稱之為輸入地址表(Import Address Table ,即經常說的 IAT)。

重寫前:

重寫后:

 

(以上兩張圖片來自:http://www.dematte.org/2006/03/04/InterceptingWindowsAPIs.aspx

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE  指向一個轉向者字符串的RVA
        DWORD Function;             // PDWORD 被輸入的函數的內存地址
         DWORD Ordinal;              // 被輸入的 API 的序數值
         DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME   指向 IMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

根據 _IMAGE_THUNK_DATA32 所指虛擬地址轉到文件地址可以得到實際的 _IMAGE_IMPORT_BY_NAME 數據

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD   Hint;     // 序號 
    CHAR   Name[1];  // 實際上是一個可變長的以0為結尾的字符串
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

例如有程序:

00 源代碼圖片

文字版:

#include <windows.h>
int WINAPI WinMain(_In_ HINSTANCE hInstance, 
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd)
{
    MessageBoxA(0, "hello", "my message", MB_OK);
    SetWindowTextA(0, "Si Wang");
    
    return 0;
}

此程序使用了兩個 Windows API : MessageBoxA 和 SetWindowTextA

編譯得到程序(為簡化說明,區段位置由軟件計算出):

0001c

0001a

我們試着找出 MessageBoxA。首先分析 PE 頭文件,找到導出表在文件中的位置:

001 輸入表地址

輸入表位置在 .rdata 區段內, 0x2264 – 0x2000 = 0x0264 得到偏移量。加上文件地址 0x0E00 得到實際文件偏移量(0x0E00 + 0x264 = 0x1064):0x1064。

接下來查看 0x1064 處:

002 dll表

可以得到三個 DLL 的描述,最后一個_IMAGE_IMPORT_DESCRIPTOR 以0填充表示結束:

那么只要一個個查看每個DLL對應的數據就能找到,不過之前我把所有的數據都看了下,在第一個DLL中

根據第一個DLL描述的 OriginalFirstThunk 的 0x2350 轉換可以知道,_IMAGE_THUNK_DATA32 在文件的 0x1150處,FirstThunk 指向的數據相同:

003 IMAGE_THUNK_DATA 表

於是就得到了文件中的 MessageBoxA 的信息。

最后,在內存中 FirstThunk 所指位置上的_IMAGE_THUNK_DATA32 數組被 Windows 加載后被重寫后就成了傳說中的 IAT ,Import Address Table,輸入地址表。使用 OllyDbg 查看運行時情況:

內存情況


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM