天天看點

PE檔案結構(三) 輸入表 輸入表

PE檔案結構(三)

參考

書:《加密與解密》

視訊:小甲魚 解密系列 視訊

輸入表

    輸入函數,表示被程式調用但是它的代碼不在程式代碼中的,而在dll中的函數。對于這些函數,磁盤上的可執行檔案隻是保留相關的函數資訊,如函數名,dll檔案名等。在程式運作前,程式是沒有儲存這些函數在記憶體中的位址。當程式運作起來時,windows加載器會把相關的dll裝入記憶體,并且将輸入函數的指令與函數真在記憶體中正的位址聯系起來。輸入表(導入表)就是用來儲存這些函數的資訊的。

    在   IMAGE_OPTIONAL_HEADER 中的 DataDirectory[16]  數組儲存了 輸入表的RVA跟大小。通過RVA可以在OD中加載程式通過 ImageBase+RVA 找到 輸入表,或者通過RVA計算出檔案偏移位址,檢視磁盤中的可執行檔案,通過檔案偏移位址找到輸入表。

    輸入表是以一個IMAGE_IMPORT_DESCRIPTOR(IID)數組 開始的,每一個被PE檔案隐式的連結進來的dll都有一個IID,IID數組的最後一個單元用NULL表示。

IMAGE_IMPORT_DESCRIPTOR 結構:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
	_ANONYMOUS_UNION union {              //00h
		DWORD Characteristics;
		DWORD OriginalFirstThunk; 
	} DUMMYUNIONNAME;
	DWORD TimeDateStamp;                  //04h
	DWORD ForwarderChain;                 //08h
	DWORD Name;                           //0Ch
	DWORD FirstThunk;                     //10h
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
           

    其中Name是dll名字的指針。OriginalFirstThunk指向一個IMAGE_THUNK_DATA數組叫做輸入名稱表Import Name Table(INT),用來儲存函數,FirstThunk也指向IMAGE_THUNK_DATA數組叫做輸入位址表Import Address Table(IAT)。

IMAGE_THUNK_DATA 結構:

typedef struct _IMAGE_THUNK_DATA32 {
	union {
		DWORD ForwarderString;
		DWORD Function;
		DWORD Ordinal;
		DWORD AddressOfData;
	} u1;
} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;
           

    當IMAGE_THUNK_DATA 的值最高位為1時,表示函數是以序号方式輸入,這時低31為被當作函數序号。當最高位是0時,表示函數是以字元串類型的函數名方式輸入的,這時,IMAGE_THUNK_DATA 的值為指向 IMAGE_IMPORT_BY_NAME 的結構的RVA。

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

Hint 表示這個函數在其所駐留dll的輸出表的序号,不是必須的。

Name 表示 函數名,是一個ASCII字元串以0結尾,大小不固定。

    INT儲存的是這個程式導入這個dll中函數資訊,它是固定的不會被修改。但是IAT會在程式加載時被重寫,當程式加載時,它會被PE加載器重寫成 這些函數的在記憶體中的真正位址。即把它原來指向的IMAGE_IMPORT_BY_NAME 改成 函數真正的位址。

那為什麼要兩個 IMAGE_THUNK_DATA 數組?

    當程式加載時,IAT 會被PE加載器重寫,PE加載器先搜尋INT,PE加載器疊代搜尋INT數組中的每個指針,找出 INT所指向的IMAGE_IMPORT_BY_NAME結構中的函數在記憶體中的真正的位址,并把它替代原來IAT中的值。當完成後,INT就沒有用了,程式隻需要IAT就可以正常運作了。

看下面圖,這個是可執行程式在磁盤中的時候:

PE檔案結構(三) 輸入表 輸入表

這個是當程式被加載的是後:

PE檔案結構(三) 輸入表 輸入表

執行個體分析:

    先找到輸入表RVA,通過IMAGE_OPTIONAL_HEADER 中的最後一個項  IMAGE_DATA_DIRECTORY 可以知道 輸入表相對與PE檔案頭的偏移量為80h可以找到輸入表達RVA。

圖檔1

PE檔案結構(三) 輸入表 輸入表

    但這個是RVA不是檔案偏移位址。通過轉換可以知道,輸入表的檔案偏移位址為850h,

    檢視850h,即IID,可以看到這個程式有兩個IID,即連結了 兩個dll,看到一個IID,可以知道它的OriginalFirstThunk 是 2098h  FirstThunk  為200Ch,它們轉換後的檔案偏移位址分别為898h 和 80Ch

圖檔2  IID數組

PE檔案結構(三) 輸入表 輸入表

  檢視OriginalFirstThunk跟FirstThunk ,可以發現這裡INT跟IAT的内容是一樣的,先看看第一個函數的資訊,因為第一位為1,是以這裡00002122h 表示 IMAGE_IMPORT_BY_NAME 的RVA,轉化為檔案偏移值為922h

圖檔3  INT跟IAT

PE檔案結構(三) 輸入表 輸入表

    檢視922h 可以看函數名。

圖檔4:

PE檔案結構(三) 輸入表 輸入表

    我們可以從上面看到程式在磁盤中時 INT 與IAT内容一樣,都是指向 IMAGE_IMPORT_BY_NAME 。用OD加載程式,檢視INT與IAT的内容

圖檔5 INT

PE檔案結構(三) 輸入表 輸入表

圖檔6 IAT

PE檔案結構(三) 輸入表 輸入表

    可以發現INT沒有發生變化,IAT變成了例如77D3C702h,IAT中的RVA被改成了函數的真正的位址。

    檢視77D3C702h,就可以看到這個函數

圖檔7

PE檔案結構(三) 輸入表 輸入表

繼續閱讀