認識Import表

看雪資料發表於2015-11-15

認識Import表

著者: [yAtEs] [Jamesluton@hotmail.com]
譯者:hying[CCG]
標題:PE輸入表說明
例子:下載
    有很多介紹PE檔案的文章,但是我打算寫一篇關於輸入表的文章,因為它對於破解很有用。
    我想解釋它的最好的方法是舉一個例子,你可以跟著我逐步深入,一步一步的思考,最後你將完全明白,我選擇了一個我剛下載下來的小程式,它是用TASM編譯的,有一個比較小的輸入表,所以我想它應該是個不錯的範例。
    好了,讓我們開始吧。首先我們得找到輸入表,它的地址放在PE檔案頭偏移80處,所以我們用16進位制編輯器開啟我們的EXE檔案,我們先得找到PE檔案頭的起始點,這很簡單,因為它總是以PE,0,0開始,我們可以在偏移100處找到它。在一般的WIN32程式中檔案頭偏移被放在檔案0X3C處,在那我們通常可看到00 01 00 00,由於資料儲存時是低位在前,高位在後的,所以翻轉過來實際就是00000100,就象前面我們說的。接下來我們就可以在PE檔案中找到我們的輸入表,100+80=180在偏移180處我們看到0030 0000,翻轉一下,它其實應該是00003000,這說明輸入表在記憶體3000處,我們必須把它轉換成檔案偏移。
    一般來說,輸入表總是在某個段的起始處,我們可以用PE編輯器來檢視虛擬偏移,尋找3000並由此發現原始偏移。很簡單的。開啟我們看到:
-CODE  00001000 00001000 00000200 00000600
-DATA  00001000 00002000 00000200 00000800
.idata 00001000 00003000 00000200 00000A00
.reloc 00001000 00004000 00000200 00000C00
    找一下,我們就發現.idata段的虛擬偏移是3000,原始偏移是A00,3000-A00=2600,我們要記住2600,以便以後轉換其它的偏移。如果你沒找到輸入表的虛擬偏移,那麼就找一下最接近的段。
    來到偏移A00處,我們就看到被稱為IMAGE_IMPORT_DESCRIPTORs(IID)的東東,它用5個欄位表示每一個被呼叫DLL的資訊,最後以Null結束。
**************************************************************************
(IID) IMAGE_IMPORT_DESCRIPTOR的結構包含如下5個欄位:
OriginalFirstThunk, TimeDateStamp, ForwarderChain, Name, FirstThunk

OriginalFirstThunk
該欄位是指向一32位以00結束的RVA偏移地址串,此地址串中每個地址描述一個輸入函式,它在輸入表中的順序是不變的。

TimeDateStamp
一個32位的時間標誌,有特殊的用處。

ForwarderChain
輸入函式列表的32位索引。

Name
DLL檔名(一個以00結束的ASCII字串)的32位RVA地址。

FirstThunk
該欄位是指向一32位以00結束的RVA偏移地址串,此地址串中每個地址描述一個輸入函式,它在輸入表中的順序是可變的。
**************************************************************************

好了,你有沒有理解?讓我們看看我們有多少IID,它們從偏移A00處開始
3C30 0000 / 0000 0000 / 0000 0000 / 8C30 0000 / 6430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
0000 0000 / 0000 0000 / 0000 0000 / 0000 0000 / 0000 0000

    每三分之一是個分界,我們知道每個IID包含了一個DLL的呼叫資訊,現在我們有2個IID,所以我們估計這個程式呼叫了2個DLL。甚至我可以打賭,你能推測出我們將能找到什麼。
    每個IID的第四個欄位表示的是名字,透過它我們可以知道被呼叫的函式名。第一個IID的名字欄位是8C30 0000,翻轉過來也就是地址0000308C,將它減去2600可以得到原始偏移,308C-2600=A8C,來到檔案偏移A8C處,我們看到了什麼?啊哈!原來呼叫的是KERNEL32.dll。
    好了,接下來我們就要去找出KERNEL32.dll中被呼叫的函式。回到第一個IID。
    FirstThunk欄位包含了被呼叫的函式名的標誌,OriginalFirstThunk僅僅是FirstThunk的備份,甚至有的程式根本沒有,所以我們通常看FirstThunk,它在程式執行時被初始化。
    KERNEL32.dll的FirstThunk欄位值是6430 0000,翻轉過來也就是地址00003064,減去2600得A64,在偏移A64處就是我們的IMAGE_THUNK_DATA,它儲存的是一串地址,以一串00結束。如下:   
A430 0000/B230 0000/C030 0000/CE30 0000/DE30 0000/EA30 0000/F630 0000/0000 0000
    通常在一個完整的程式裡都將有這些。我們現在有了7個函式呼叫,讓我們來看其中的兩個:
DE30 0000翻轉後是30DE,減去2600後等於ADE,看在偏移ADE處的字串是ReadFile,
EA30 0000翻轉後是30EA,減去2600後等於AEA,看在偏移AEA處的字串是WriteFile,
    你可能注意到了,在函式名前還有2個這位元組的00,它是被作為一個提示。
    很簡單吧,你可以自己來試一下。回到A00,看第二個DLL的呼叫
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
    先找它的DLL檔名。9930翻轉為3099-2600 =A99,在偏移A99處找到USER32.dll。再看FirstThunk欄位值:8430翻轉為3084-2600=A84,偏移A84處儲存的地址為08310000,翻轉後3108-2600=B08,偏移B08處字串為MessageBoxA。明白了吧,接下來你就可以把這些用在你自己的EXE檔案上了。
    摘要:
    在PE檔案頭+80偏移處存放著輸入表的地址,輸入表包含了DLL被呼叫的每個函式的函式名和FirstThunk,通常還有Forward Chain和TimeStamp。
    當執行程式時系統呼叫GetProcAddress,將函式名作為引數,換取真正的函式入口地址,並在記憶體中寫入輸入表。當你對一個程式脫殼時你可能注意到你有了一個已經初始化的FirstThunk。例如,在我的WIN98上,函式GetProcAddress的入口地址是AE6DF7BF,在98上,所有的KERNEL32.dll函式呼叫地址看上去地址都象:xxxxF7BF,如果你在輸入表中看到這些,你可以利用orignal thunk重建它,或者重建這個PE程式。
    好了,我已經告訴你它們是如何工作的,我不是專家,如果你發現什麼錯誤,請告訴我。

 

相關文章