1. 程式人生 > >PE檔案格式學習(四):匯入表

PE檔案格式學習(四):匯入表

UPDATE:

  • 在文章的末尾更新了一張圖,在網上找的,有助於理解匯入表的結構

1.概述

匯入表是逆向和病毒分析中比較重要的一個表,在分析病毒時幾乎第一時間都要看一下程式的匯入表的內容,判斷程式大概用了哪些功能。

匯入表是資料目錄表中的第2個元素,排在匯出表的後面。

2.匯入表解析

先來了解一下匯入表在PE檔案中的結構體:

struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics; 
        DWORD   OriginalFirstThunk;         
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp; 
    DWORD   ForwarderChain;         
    DWORD   Name;
    DWORD   FirstThunk;              
} IMAGE_IMPORT_DESCRIPTOR;

在說明欄位之前,需要說明的是,匯入表是一個數組,每個元素都是上面的結構體,長度是14h,並且以14h位元組的“0”作為陣列結尾,如果程式匯入了多個模組,那麼這個陣列的長度應該是14h*(n+1)。

以下是欄位的說明。

DWORD TimeDateStamp:一般為空,對應上圖的0x00000000

DWORD ForwarderChain:對應上圖的0x00000000

DWORD Name:匯入模組名的RVA,對應上圖的0x0000230c,

DWORD Characteristics:跟OrginalFirstThunk是聯合體,對應上圖的0x0000227c

DWORD OrginalFirstThunk:跟Characteristics是聯合體,一般情況下是OrginalFirstThunk生效,它是IMAGE_THUNK_DATA陣列的RVA,對應上圖的0x0000227c

我們來看看IMAGE_THUNK_DATA結構體

struct _IMAGE_THUNK_DATA32{
    union {
        DWORD ForwarderString 
        DWORD Function ; 被輸入的函式的記憶體地址
        DWORD Ordinal ; 被輸入的API的序數值
       DWORD AddressOfData ; 高位為0則指向IMAGE_IMPORT_BY_NAME 結構體二
    }u1;
}IMAGE_THUNK_DATA32;

我們知道指向IMAGE_THUNK_DATA的RVA是0x0000227c,轉為offset是107ch,從107ch開始到0x00000000結束是一個完整的匯入函式陣列,如下圖:

我們對照圖來一一講解IMAGE_THUNK_DATA的欄位。

ForwarderString:只有當IMAGE_IMPORT_DESCRIPTOR中的ForwarderChain有值時,它才有效

Function:匯入函式的實際記憶體地址,只有在載入記憶體中時才有效

Ordinal:匯入函式的匯出序號,匯出表中允許使用序號的方式匯出函式,匯入表也要有相應的機制。只有當IMAGE_THUNK_DATA的最高位是1的時候才有效

AddressOfData:當IMAGE_THUNK_DATA的最高位為0的時候有效,指向一個IMAGE_IMPORT_BY_NAME結構體陣列,有沒有一種可能是最高位為1,並且是以函式名的方式匯入函式的呢?我們知道最高位為1的最小數也是0x80000000,這個位置是系統空間,程式不可訪問,因此是不可能的。

IMAGE_IMPORT_BY_NAME結構體如下:

struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;                 
    BYTE    Name[1];            
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

Hint:指出函式在所在的dll的輸出表中的序號

Name:指出要輸入的函式的函式名

接下來是IMAGE_IMPORT_DESCRIPTOR的最後一個欄位。

DWORD FirstThunk:對應的是IMAGE_IMPORT_DESCRIPTOR那張圖中的0x00002000,在磁碟中它指向的位置跟OriginalFirstThunk是一樣的,同樣是指向IMAGE_THUNK_DATA陣列。如果PE檔案被載入進記憶體,FirstThunk指向的IMAGE_THUNK_DATA陣列中的Function儲存的就是真實記憶體中的函式地址,OriginalFirstThunk指向的同樣是IMAGE_THUNK_DATA陣列,但是儲存的並不是真實記憶體中的函式地址,而是IMAGE_IMPORT_BY_NAME陣列的RVA,儲存的是匯入函式的檔名。

3.總結

1.重點需要分清在磁碟中和在記憶體中OriginalFirstThunk和FirstThunk的區別,在磁碟中二者都一樣,在記憶體中前者的IMAGE_THUNK_DATA結構生效的是AddressOfData欄位,因此指向的是IMAGE_IMPORT_BY_NAME陣列的RVA,所以它也被稱為INT,後者儲存的是匯入函式在記憶體中的真實地址,所以它被稱為IAT。

2.需要釐清一點概念就是,PE中匯入表,也就是IMAGE_IMPORT_DESCRIPTOR結構在一個數組中,意味著一個PE檔案中可能有多個匯入表,每個匯入表中只有一個OriginalFirstThunk和FirstThunk,但是他們指向的IMAGE_THUNK_DATA是一個數組,陣列的元素個數代表函式的個數,如果是IMAGE_THUNK_DATA中的AddressOfData欄位生效,它指向的是一個IMAGE_IMPORT_BY_NAME陣列,這個陣列中的元素個數跟IMAGE_THUNK_DATA中的可能不一樣,因為有的函式沒有名字。

3.凡是陣列的最後一定是以0填充,長度是陣列元素的大小,字串以00作為結束。