PE檔案詳解(五) 匯出表

2021-07-09 10:21:55 字數 2406 閱讀 3114

當pe 檔案被執行的時候,windows 載入器將檔案裝入記憶體並將匯入表(export table) 登記的動態鏈結庫(一般是dll 格式)檔案一併裝入位址空間,再根據dll 檔案中的函式匯出資訊對被執行檔案的iat 進行修正。

基礎補充:很多朋友可能看到這裡會有點懵,各位看官請允許小甲魚囉嗦一下,照顧初學者。我們都明白windows 在載入乙個程式後就在記憶體中為該程式開闢乙個單獨的虛擬位址空間,這樣的話在各個程式自己看來,自己就擁有幾乎任意位址的支配權,所以他自身的函式想放在哪個位址自己說了算。

有一些函式很多程式都會用到,為每乙個程式寫乙個相同的函式看起來似乎有點浪費空間,因此windows就整出了動態鏈結庫的概念,將一些常用的函式封裝成動態鏈結庫,等到需要的時候通過直接載入動態鏈結庫,將需要的函式整合到自身中,這樣就大大的節約了記憶體中資源的存放。如圖:

有乙個重要的概念需要記住:

匯出表結構

匯出表(export table)中的主要成分是乙個**,內含函式名稱、輸出序數等。序數是指定dll 中某個函式的16位數字,在所指向的dll 檔案中是獨一無二的。在此我們不提倡僅僅通過序數來索引函式的方法,這樣會給dll 檔案的維護帶來問題。例如當dll 檔案一旦公升級或修改就可能導致呼叫改dll 的程式無法載入到需要的函式。

資料目錄表的第乙個成員指向匯出表,是乙個image_export_directory(以後簡稱ied)結構,ied 結構的定義如下:

image_export_directory struct characteristics dword ? ; 未使用,總是定義為0 timedatestamp dword ? ; 檔案生成時間 majorversion word ? ; 未使用,總是定義為0 minorversion word ? ; 

未使用,總是定義為0

name dword ? ; 模組的真實名稱 base dword ? ; 基數,加上序數就是函式位址陣列的索引值 numberoffunctions dword ? ; 匯出函式的總數 numberofnames dword ? ; 以名稱方式匯出的函式的總數 addressoffunctions dword ? ; 指向輸出函式位址的rva addressofnames dword ? ; 指向輸出函式名字的rva addressofnameordinals dword ? ; 指向輸出函式序號的rva image_export_directory ends

這個結構中的一些欄位並沒有被使用,有意義的字段說明如下。

1. 從序號查詢函式入口位址

下邊小甲魚帶大家來模擬一下windows 裝載器查詢匯出函式入口位址的整個過程。如果已知函式的匯出序號,如何得到函式的入口位址呢 ?

windows 裝載器的工作步驟如下:

定位到pe 檔案頭

從pe 檔案頭中的 image_optional_header32 結構中取出資料目錄表,並從第乙個資料目錄中得到匯出表的rva

從匯出表的 base 字段得到起始序號

將需要查詢的匯出序號減去起始序號,得到函式在入口位址表中的索引

檢測索引值是否大於匯出表的 numberoffunctions 欄位的值,如果大於後者的話,說明輸入的序號是無效的

用這個索引值在 addressoffunctions 字段指向的匯出函式入口位址表中取出相應的專案,這就是函式入口位址的rva 值,當函式被裝入記憶體的時候,這個rva 值加上模組實際裝入的基位址,就得到了函式真正的入口位址

2. 從函式名稱查詢入口位址

如果已知函式的名稱,如何得到函式的入口位址呢?與使用序號來獲取入口位址相比,這個過程要相對複雜一點!

windows 裝載器的工作步驟如下:

最初的步驟是一樣的,那就是首先得到匯出表的位址

從匯出表的 numberofnames 字段得到已命名函式的總數,並以這個數字作為迴圈的次數來構造乙個迴圈

從 addressofnames 字段指向得到的函式名稱位址表的第一項開始,在迴圈中將每一項定義的函式名與要查詢的函式名相比較,如果沒有任何乙個函式名是符合的,表示檔案中沒有指定名稱的函式

如果某一項定義的函式名與要查詢的函式名符合,那麼記下這個函式名在字串位址表中的索引值,然後在 addressofnamesordinals 指向的陣列中以同樣的索引值取出陣列項的值,我們這裡假設這個值是x

最後,以 x 值作為索引值,在 addressoffunctions 字段指向的函式入口位址表中獲取的 rva 就是函式的入口位址

一幫情況下病毒程式就是通過函式名稱查詢入口位址的,因為病毒程式作為一段額外的**被附加到可執行檔案中的,如果病毒**中用到某些 api 的話,這些 api 的位址不可能在宿主檔案的匯出表中為病毒**準備好。

因此只能通過在記憶體中動態查詢的方法來實現獲取api 的位址。關於病毒**具體的實現分析,小甲魚在今後將跟大家共同研究討論這個話題~

PE檔案結構詳解(三)PE匯出表

上篇文章 pe檔案結構詳解 二 可執行檔案頭 的結尾出現了乙個大陣列,這個陣列中的每一項都是乙個特定的結構,通過函式獲取陣列中的項可以用rtlimagedirectoryentrytodata函式,datadirectory中的每一項都可以用這個函式獲取,函式原型如下 base 模組基位址。dire...

PE檔案結構詳解(三)PE匯出表

上篇文章 pe檔案結構詳解 二 可執行檔案頭 的結尾出現了乙個大陣列,這個陣列中的每一項都是乙個特定的結構,通過函式獲取陣列中的項可以用rtlimagedirectoryentrytodata函式,datadirectory中的每一項都可以用這個函式獲取,函式原型如下 base 模組基位址。dire...

PE檔案結構詳解(三)PE匯出表

上篇文章 pe檔案結構詳解 二 可執行檔案頭 的結尾出現了乙個大陣列,這個陣列中的每一項都是乙個特定的結構,通過函式獲取陣列中的項可以用rtlimagedirectoryentrytodata函式,datadirectory中的每一項都可以用這個函式獲取,函式原型如下 base 模組基位址。dire...