匯入表的位置和大小可以從pe檔案頭中image_optional_header32結構的資料目錄欄位中獲取,對應的專案是datadirectory欄位的第2個image_data_directory結構,
從結構的virtualaddress欄位得到匯入表的rva值。
匯入表由一系列的image_import_descriptor結構組成,結構的數量取決於程式要使用的dll檔案的數量,每個結構對應乙個dll檔案,在所有這些結構的最後,由乙個內容全為0的image_import_descriptor結構作為結束。
image_import_descriptor結構的定義如下:
image_import_descriptor = packed record
case integer of
0: characteristics: dword;
1: originalfirstthunk:dword;
end;
timedatestamp: dword;
forwarderchain: dword;
name: dword;
firstthunk: dword;
end;
結構中的name欄位是乙個rva,它指向此結構所對應的dll檔案的名稱,這個檔名是乙個以null結尾的字串。
originalfirstthunk欄位和firstthunk欄位,它們都指向乙個包含一系列image_thunk_data結構的陣列,陣列中的每個image_thunk_data結構定義了乙個匯入函式的資訊,陣列的最後以乙個內容為0的image_thunk_data結構作為結束。
乙個image_thunk_data結構實際上就是乙個雙字,之所以把它定義成結構,是因為它在不同的時刻有不同的含義,結構的定義如下:
image_thunk_data = record
case integer of
0: forwarderstring: dword;
1: function: dword;
2: ordinal: dword;
3: addressofdata: dword;
end;
end;
乙個image_thunk_data結構如何用來指定乙個匯入函式呢?當雙字(就是指結構)的最高位為1時,表示函式是以序號的方式匯入的,這時雙字的低位就是函式的序號。當雙字的最高位為0時,表示函式以字串型別的函式名方式匯入,這時雙字的值是乙個rva,指向乙個用來定義匯入函式名稱的image_import_by_name結構,此結構的定義如下:
image_import_by_name = record
hint: word;
name: array[0..0] of char;
end;
結構中的hint欄位也表示函式的序號,不過這個欄位是可選的,有些編譯器總是將它設定為0,name欄位定義了匯入函式的名稱字串,這是乙個以0為結尾的字串。
為什麼需要兩個一模一樣的image_thunk_data陣列呢?答案是當pe檔案被裝入記憶體的時候,其中乙個陣列的值將被改作他用,windows裝載器會將指令jmp dword ptr [******xx]指定的******xx處的rva替換成真正的函式位址,其實******xx位址正是由firstthunk欄位指向的那個陣列中的一員。
實際上,當pe檔案被裝入記憶體後,其中由firstthunk欄位指向的那個陣列中的每個雙字都被替換成了真正的函式入口位址,之所以在pe檔案中使用兩份image_thunk_data陣列的拷貝並修改其中的乙份,是為了最後還可以留下乙份拷貝用來反過來查詢位址所對應的匯入函式名。
PE檔案結構詳解(四)PE匯入表
pe檔案結構詳解 二 可執行檔案頭的最後展示了乙個陣列,pe檔案結構詳解 三 pe匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項 image directory entry import,即匯入表。也許大家注意到過,在image data directory中,有幾項的名字都和匯入...
PE檔案結構詳解(四)PE匯入表
pe檔案結構詳解 二 可執行檔案頭的最後展示了乙個陣列,pe檔案結構詳解 三 pe匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項 image directory entry import,即匯入表。也許大家注意到過,在image data directory中,有幾項的名字都和匯入...
PE檔案結構詳解(四)PE匯入表
pe檔案結構詳解 二 可執行檔案頭的最後展示了乙個陣列,pe檔案結構詳解 三 pe匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項 image directory entry import,即匯入表。也許大家注意到過,在image data directory中,有幾項的名字都和匯入...