天天看點

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

目錄

PE中的導出表

導出表作用、連結庫功能、獲得導出函數位址:

構造含導出表的 PE 檔案:

安轉MASM32軟體包:

DLL 源代碼,編寫def,Inc頭檔案、編譯連結:

使用導出函數:

導出表資料結構

導出表定位:

導出目錄結構 IMAGE_EXPORT_DIRECTORY:

導出表資料結構執行個體分析:

導出表程式設計:

根據編号查找函數位址:

根據名字查找函數位址:

周遊導出表:

導出表的應用:

導出函數覆寫:

導出私有函數:

PE中的導出表

這裡重點介紹與導入表剛好相反的導出表,它描述了導出表所在 PE 檔案向其他程式提供的可供調用的函數的情況。

導出表作用、連結庫功能、獲得導出函數位址:

導出表的作用:

代碼重用機制提供了重用代碼的動态連結庫,它會向調用者說明庫裡的哪些函數是可以被别人使用的,這些用來說明的資訊便組成了導出表。

通常情況下,導出表存在于動态連結庫檔案裡。但我們不能簡單地認為 EXE 中沒有導出表,例如 WinWord.exe 檔案裡就有 ;也不能簡單地認為所有的 DLL 中都有導出表,例如一些專門存放資源檔案的 DLL 裡就沒有導出表。

不過可以這樣了解 :

EXE 檔案中很少有導出表,大部分的 DLL 檔案中都有導出表。

提示:

如果你想開發一個類庫供自己或他人使用,不僅要通過導出機制聲明每個庫檔案的公用函數,還要詳細編寫每個函數的說明文檔。

裝載過程:

Windows 裝載器在進行 PE 裝載時,會将導入表中登記的所有 DLL 一并裝入,然後根據 DLL 的導出表中對導入函數的描述修正導入表的 IAT 值。通過導出表,DLL 檔案向調用它的程式或系統提供導出函數的名稱、序号(編号),以及入口位址等資訊。

綜上所述,可以得出結論,導出表的作用有兩個:

一是可以通過導出表分析不認識的動态連結庫檔案所能提供的功能。

二是向調用者提供輸出函數指令在子產品中的起始位址。

分析動态連結庫功能:

很多時候,我們都得不到某些動态連結庫中輸出函數的完整說明,這時候隻能函數的名字猜測。

下面是從一個安裝程式中擷取到的某動态連結庫檔案的導出表:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

根據輸出函數的英文提示不難看出,該動态連結庫提供了程式的 License 驗證功能 (通過系列函數PSA_GetLicenseXXXXXX 可以看出),而且程式具有限制并發連接配接數功能 (通過函數 PSA_GetNumberOfConnections 可以看出)。對于猜測出功能的函數,可以嘗試通過程式調用來測試函數的參數以及調用方法,進而為自己所用。

獲得導出函數位址:

對一個動态連結庫裡導出的函數的調用,既可以通過函數名稱來進行,也可以通過函數在導出表的索引來進行。Windows 加載器将與程序相關的 DLL 加載到虛拟位址空間以後,會根據導入表中登記的與該動态連結庫相關的由 INT 指向的名稱或編号來周遊 DLL 所在虛拟位址空間,通過函數名或編号查找導出表結構,進而确定該導出函數在虛拟位址空間中的起始位址 VA,并将該 VA 覆寫導入表的 IAT 相關項。

在覆寫 IAT 的過程中,導出表起到了參照和指引的作用。如果一個動态連結庫沒有定義導出表,其内部包含的所有函數都無法被其他程式透明地調用。這裡所說的透明,是指公開調用。當然,隻要你掌握了動态連結庫的内部編碼,即使沒有導出表,你也可以随意地引用裡面的函數,哪怕這些函數是私有的

構造含導出表的 PE 檔案:

将編寫并建立一個動态連結庫檔案,該檔案輸出的函數可以被其他的基于 Windows GUI 的程式調用,用來增加程式視窗顯示或退出時的動态效果。

本節的例子是本書第一個以 DLL 為擴充名的 PE 檔案,它提供了兩種視窗顯示或退出時可以使用的特效:

口 逐級縮放

口 漸入漸出

使用一個 DLL 檔案需要經過以下四步:

步驟1 編寫 DLL 檔案的源代碼。

步驟2 編寫函數導出聲明檔案(擴充名為 def)。

步驟3 使用特殊參數編譯連結生成最終的 DLL 檔案。

步驟4 編寫包含檔案(擴充名為 inc )。

在其他源代碼中,可以通過靜态引用和動态加載兩種技術使用新編寫的 DLL 檔案中聲明的導出函數。

安轉MASM32軟體包:

可以從網站 http://www.masm32.com/ 上獲得 MASM32 SDK 的最新版本:

步驟1 :運作安裝程式 installexe,安裝彙編環境:

首先選擇安裝路徑,此處我們選擇的路徑為D:(backup),然後單擊“Start”按鈕。此後,中間過程所有的按鈕均選擇預設的設定,即可完成軟體安裝。安裝結束後,會顯示IDE 彙編內建環境 QEditor 的界面,下圖是該環境加載了 intro.txt 文檔後的界面。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

步驟2:建立自己的工作區:

筆者選擇将軟體安裝到非系統盤 (D 盤),建議大家也這樣做。為了能存放自己編寫的彙編代碼,筆者在 D:masm32 中建立立了一個檔案夾 source。所有要做的準備工作都已經完成,不過現在還不能開始編寫彙程式設計式,因為還設有告訴電腦彙程式設計式所依賴的環境,以及要調用的 API 函數庫都在哪裡。

步驟3:設定系統環境變量:

打開環境變量選項,在頁籤上單擊“環境變量”按鈕,在使用者的環境變量中增加以下三個環境變量;

口include=d:masm32\include

口lib=d:masm32\lib

口path=d:\masm32\bin

步驟4:測試環境變量設定是否成功:

打開cmd,輸入 ml 和 link ,如果視窗顯示提示就說明安裝成功:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

DLL 源代碼,編寫def,Inc頭檔案、編譯連結:

編寫 DLL 源代碼和編寫其他程式唯一不同之處是,在源代碼内部必須定義 DLL 的入口函數。該入口函數必須符合一定的格式。

顯示視窗的特殊效果代碼見 (chapter5\winResult.asm)

以上代碼中一共定義了6個函數,其中有 4 個是可以被導出的公有函數,有一個私有函數,還有一個動态連結庫必須具備的入口函數。

4 個公有函數分别代表了 4 種顯示視窗的動态效果:

口AnimateOpen (視窗放大進入)

口AnimateClose(視窗縮小退出)

口FadeInOpen (視窗淡入)

口FadeOutClose (視窗淡出)

入口函數名為 DIEntry,由于我們沒有初始化代碼,是以隻是簡單地傳回了TRUE。入口函數負責處理動态連結庫的生命周期中發生的各種消息,如庫被裝載、禦載、新線程建立和新線程結束等操作。入口函數的名字可以随意命名,但格式必須遵循一定規範(這些規範包括人口參數的個數和傳回值的類型 ) 。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

編寫 def 檔案:

為了能讓系統識别哪些是私有函數,哪些是導出函數,還需要在源代碼外另外附加一個檔案,在檔案中列出要導出的函數的名字。這個檔案就是 def 檔案。連結器會根據這個 def 檔案的内容在導出表中加入由 EXPORTS 指定的函數名。

以下是編寫 def 檔案的過程 :

在記事本中輸入如下内容,并儲存為檔案"winResult.def"。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

編譯和連結:

連結時需要額外增加兩個連結參數:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

新增加的兩個連結參數分别是“-DLL”和“-Def ”。前者表示生成的最終檔案是一個動态連結庫,擴充名為 dll ,後者表示在生成的連結庫的導出表中,加入該參數指定的 def 檔案中的函數。

連結後生成以下兩個相關檔案:

口 winResult.dll 是動态連結庫檔案。該檔案可以共享給使用其他進階語言如VC++、Delphi、VB 等的開發者使用。

口 winResult.lib 是彙編語言環境下的庫檔案。使用 winResult.dll 的彙程式設計式必須使用該庫檔案和下面要介紹的 inc 包含檔案。

編寫頭檔案:

包含檔案擴充名為“.inc”,該檔案類似于C語言的“.h”頭檔案,是以又稱為頭檔案。該檔案中包含了動态連結庫中導出函數的聲明。将如下内容輸入記事本程式,并儲存為“winResult.inc”。

有了動态連結庫、相關的 lib 檔案和頭檔案,就可以在任何一個程式的代碼中使用這個DLL 裡導出的函數了。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

使用導出函數:

接下來使用剛生成的三個檔案 , winResult.dll、winResult.lib 和 winResult.inc 來編寫一個漸入式視窗顯示程式:(代碼在chapter5\FirstWindow.asm)

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

與引入其他動态連結庫的方法一樣,在代碼行 19 引入 winResult.lib,在行 20 引入頭檔案 winResult.inc。建立視窗以後,顯示時不再使用如下所示的正常的顯示代碼;

invoke ShowWindow,hwinMain,SW_SHONNORMRL

而是使用了具有動畫特效的代碼 (如下所示),該代碼由動态連結庫 winResult.dll 輸出。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

可以看出,調用公用函數的方法和調用其他動态連結庫的方法(使用正常方法編譯連結程式,生成 FirstWindow.exe,然後運作它以便檢視視窗顯示的動畫效果) 是完全一樣的。

最後使用編譯指令編譯生成可執行.exe 檔案:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

注意要和dll、lib、inc檔案一起放在同一目錄中:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

導出表資料結構

接下來簡單分析到目前為止接觸的兩種不同類型的 PE 檔案:

口 FirstWindow.exe (EXE 可執行檔案)

口 winResult.dll (DLL 動态連結庫)

使用PEInfo 小工具檢視兩者結構,通過比較可以看出,兩種不同類型的 PE 檔案在内容上存在很大差異,如頭檔案中的裝載基位址、人入口位址、檔案屬性均不相同。EXE 檔案的 PE格式中不存在重定位表項,也沒有導出表; 而 DLL 檔案的 PE 格式中這兩項都存在。

導出表定位:

首先回顧一下PE頭中的擴充PE頭IMAGE_OPTIONAL HEADER32中的最後一個字段IMAGE_DATA_DIRECTORY資料目錄結構 :

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

現在鎖定winResult.dll 的資料目錄中導出表的RVA和導出表資料大小:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

加框部分為導出表資料目錄項資訊。通過以上位元組碼可以獲得與導出表有關的兩條資訊:

口 導出表所在位址 RVA=0x000002140

口 導出表資料大小 =0000008fh

下圖是PEinfo顯示的資料分析:

根據 RVA 與FOA 的換算關系,可以得到:導出表資料所在檔案的偏移位址為: 0x00000940 。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

導出目錄結構 IMAGE_EXPORT_DIRECTORY:

導出資料的第一個結構是 IMAGE_EXPORT_ DIRECTORY。該結構詳細定義如下:

導入表的 IMAGE_IMPORT_DESCRIPTOR 個數與調用的動态連結庫個數相等,而導出表的IMAGE_EXPORT_DIRECTORY 隻有一個。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

IMAGE_EXPORT_DIRECTORY.nName:(檔案名字元串所在位址)

+000ch,雙字。該字段訓示的位址指向了一個以“\0”結尾的字元串,字元串記錄了導出表所在的檔案的最初檔案名。

IMAGE_EXPORT_DIRECTORY.NumberOfFunctions:(函數個數)

+0014h,雙字。該字段定義了檔案中導出函數的總個數。

IMAGE_EXPORT_DIRECTORY.NumberOfNames:(函數名字個數)

+0018h,雙字。在導出表中,有些函數是定義名字的,有些是沒有定義名字的。該字段記錄了所有定義名字函數的個數。如果這個值是0,則表示所有的函數都沒有定義名字。NumberOfNames 和NumberOfFunctions 的關系是前者小于等于後者。

IMAGE_EXPORT_DIRECTORY.AddressOfFunctions:(函數RVA值所在位址)

+001ch,雙字。該指針指向了全部導出函數的入口位址的起始。從入口位址開始為雙字數組,數組的個數由字段 IMAGE_EXPORT_ DIRECTORY.NumberOfFunctions 決定。導出函數的每一個位址按函數的編号順序依次往後排開。在記憶體中,我們可以通過函數編号來定位某個函數的位址。大緻代碼如下:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

IMAGE_EXPORT_DIRECTORY.nBase:(編号起始值)

+0010h,雙字。導出函數編号的起始值。DLL 中的第一個導出函數并不是從 0 開始的,某導出函數的編号等于從 AddressOfFunctions 開始的順序号(索引)加上這個值。

大緻示意圖如圖下圖所示:

Funl 的函數編号為 nBase+0=200h, Fun2 的函數編号為 nBase+1=201h,以此類推。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

IMAGE_EXPORT_DIRECTORY.AddressOfNames:(指向存儲函數名字元串的位址)

+0020h,雙字。該值為一個指針。該指針指向的位置是一連串的雙字值,這些雙字值均指向了對應的定義了函數名的函數的字元串位址。這一連串的雙字個數為NumberOfNames字段的值。

IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals:(左邊索引值所在位址)

+0024h,雙字。該值也是一個指針,與 AddressOfNames 是一一對應關系 注意,是一一對應),所不同的是,AddressOfNames 指向的是字元串的指針數組,而 AddressOfNameOrdinals 則指向了該函數在 AddressOfFunctions 中的索引值。

注意:

索引值是一個字,而非雙字。該值與函數編号是兩個不同的概念,兩者之間的關系為:索引值 =編号 - nBase

關系圖描述字段之間關系:

上面所述的字段之間的關系可以用下圖表示:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

如圖所示,AddressOfNames 中的函數是從 Function 2 開始的,也就是說這裡假設Function 1 隻提供編号通路 ; 其 nBase 為 200h,是以對應的 AddressOfNameOrdinals 是 0000h,但最終函數 Function 1 的編号為: 索引值 + nBase 的值,即 0200h。

提示:在以前的導入表圖中中最後指向的結構“Hint/Name”中的Hint 值是AddressOfFunctions 的索引值,并非函數編号:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

導出表資料結構執行個體分析:

下面以動态連結庫 winResult.dll 為例,分析該 PE 檔案的導出表結構及其資料組織。使用小工具PEDump 擷取 winResult.dll 的導出表位元組碼内容如下(從檔案位址偏移0x00000940 開始):

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

其中,主要字段所對應的位元組碼解釋如下:

>>90 21 00 00

對應 IMAGE_EXPORT _DIRECTORY.nName 字段(檔案名字元串所在位址),指向檔案偏移 0x00000990。該處的值為字元串“winResult.dll”,是動态連結庫的最初的名字。

>>01 00 00 00

對應 IMAGE_EXPORT_DIRECTORY.nBase 字段(編号起始值),表示起始編号為 1。

>>04 00 00 00

對應 IMAGE_EXPORT_DIRECTORY.NumberOfFunctions 字段(函數個數),表示共有 4 個導出函數。

>>04 00 00 00

對應 IMAGE_EXPORT_DIRECTORY.NumberOfNames 字段(函數名字個數),表示 4 個導出函數均為按照名稱導出。

>>68 21 00 00

對應 IMAGE_EXPORT_DIRECTORY.AddressOfFunctions 字段(函數RVA值所在位址)。從該位置取出連續4個位址(個數由IMAGE_EXPORT_DIRECTORYNumberOfFunctions 字段決定),這些位址分别對應 4 個函數的RVA。

AddressOfFunctions 的值如下圖:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

>>78 21 00 00

對應 IMAGE_EXPORT_DIRECTORY.AddressOfNames 字段(指向存儲函數名字元串的位址)。

從該位置取出的連續4個位址依次為:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

>>88 21 00 00

對應 IMAGE_EXPORT_DIRECTORY. AddressOfNameOrdinals 字段(左邊索引值所在位址、單字)。

從該位置取出的連續 4 個單字索引依次為:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

這些索引的值存在于字段 IMAGE_EXPORT _ DIRECTORY.AddressOfFunctions 所指向的函數RVA位址清單中,注意函數是在實際的運作檔案FirstWindow.exe中。最終 4 個函數的編号将分别是此處的索引值加上 nBase 的值,即 0001、0002、0003 和 0004。

函數名對應的索引值可以在調用了該動态連結庫的程式 FirstWindow.exe 的導入表資料中查找到:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

導出表程式設計:

如前所述,通過導出表可以擷取相關函數的位址。函數可以通過索引值定位,也可以通過函數名定位。

通過程式設計查找函數位址有兩個不同方法,分别是:

口 根據編号查找函數位址

口 根據名字查找函數位址

根據編号查找函數位址:

要通過編号查找函數位址,其步驟如下;

步驟1 定位到PE 頭。

步驟2 從PE檔案頭中找到資料目錄表,表項的第一個雙字值是導出表的起始 RVA。

步驟 3 從導出表的 nBase 字段得到起始序号。

步驟4 函數編号減去起始序号得到的是函數在 AddressOfFunctions 中的索引号。

步驟5 通過查詢 AddressOfFunctions 指定索引位置的值,找到虛拟位址。

步驟6 将虛拟位址加上該動态連結庫在被導入到位址空間後的基位址,即為函數的真實入口位址。

不建議使用編号查找函數位址。因為有很多的動态連結庫中辨別的編号與對應的函數并不一緻,通過這種方法找到的函數位址往往是錯誤的。

根據名字查找函數位址:

要根據函數名字從導出表結構中查找函數的位址,步驟如下:

步驟1 定位到PE 頭。

步驟2 從PE檔案頭中找到資料目錄表,表項的第一個雙字值是導出表的起始 RVA。

步驟3 從導出表中擷取 NumberOfNames 字段的值,以便構造一個循環,根據此值确定循環的次數。

步驟4 從 AddressOfNames 字段指向的函數名稱數組的第一項開始,與給定的函數名字進行比對; 如果比對成功,則記錄從 AddressOfNames 開始的索引号。

步驟5 通過索引号再去檢索 AddressOfNameOrdinals 數組,從同樣索引的位置找到函數的位址索引。

步驟6 通過查詢 AddressOfFunctions 指定函數位址索引位置的值,找到虛拟位址。

步驟7 将虛拟位址加上該動态連結庫在被導和人到位址空間的基位址,即為函數的真實入口位址。

其中通過函數名擷取函數調用位址的編碼見下面代碼清單:

擷取指定字元串的 API 函數的調用位址的函數 _getApi(chapter5\peinfo.asm)

周遊導出表:

周遊導出表的程式設計是以PEInfo.asm 程式為模闆開始的。在函數 .openFile 中加入以下代碼 (加黑部分):

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

周遊導出表的函數 _getExportlnfo (chapter5\peinfo.asm):

代碼略~

以下内容是使用 PEInfo 小工具分析 chapter5\winResult.dll 檔案輸出的與導出表有關的資訊:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

顯示資訊分三部分 :

導出表所處的節

導出表結構 IMAGE_EXPORT_DIRECTORY 的主要字段的值

導出函數的相關資訊(含導出序号、虛拟位址和導出函數名稱)。

導出表的應用:

導出表和常見的應用主要包括對導出表函數的覆寫,以及對動态連結庫内部私有函數的導出等。通過對導出表函數進行覆寫,可以更改代碼流程或代碼功能,為應用程式實施更新檔 。

導出函數覆寫:

導出表程式設計中常見的技術是,不需要修改使用者程式,便能将使用者程式中調用的動态連結庫函數轉向或者實施代碼覆寫,實作使用者程式的調用轉移。這種技術通常用在病毒程式的開發中,因為使用者程式沒有發生改變,是以防毒軟體在對使用者程式的防護過程中,針對這種滲透是無效的。

下面介紹兩種常見的導出函數覆寫技術:

口 修改導出結構中的函數位址

口 覆寫函數位址部分的指令代碼

修改dll導出結構中的函數位址:

以 winResult.dll 為例, 将 AddressOfFunctions 索引1 和2 的位址(分别對應函數 AnimateOpen 和 FadeInOpen) 交換位置:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

如上所示,無需修改應用程式FirstWindow.exe,僅通過将函數調用RVA 位址0x00001282 和 0x00001022 交換位置,即可實作導出函數的覆寫。直接測試,發現顯示視窗的動畫效果發生了變化。

需要注意的是,在使用導出函數位址覆寫技術的時候,首先要保證所涉及的兩個函數參數入口(即參數的個數、類型、大小)要一緻,否則調用完成後棧不平衡,這會導緻應用程式調用失敗;其次,要求使用者對兩個函數的内部實作要有充分的了解,使得位址轉向後,能夠保證應用程式在功能上可以全面相容并運作良好。

覆寫dll中函數位址部分的指令代碼:

第二種常見的覆寫技術,是将 AddressOfFunctions (函數RVA所在位址)指向的位址空間指令位元組碼實施覆寫。

這種技術又衍生出兩種:

口 暴力覆寫,即将所有的代碼全部替換為新代碼。新代碼可能含有原來代碼的全部功能,也可能不包含原有代碼功能。

口完美覆寫,通過構造指令,實施新代碼與原代碼的共存和無遺漏運作。為完美覆寫涉及代碼的重定位,相對複雜一些。

這裡以暴力覆寫為例,相關檔案在随書檔案的 chapters\b 目錄中,winResult.dll 是被覆寫了函數 FadeInOpen 後的動态連結庫。

打開 winResult.dll 檔案的位元組碼,将FadeInOpen 定義部分(檔案中起始偏移 0x0682) 修改成成如下指令序列:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

第一個紅框儲存原始棧基位址,第二個紅框是維持棧平衡的傳回指令。所有位元組碼對應的反彙編指令為:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

因為函數 FadeInOpen 的代碼被全部覆寫,是以運作 FirstWindow 隻會彈出提示對話框,内容顯示“user32.dll”。顯示的字元串是借用了 winResult.asm 的資料段中定義的函數SetLayeredWindowAttributes 所在動态連結庫的名稱。

從反彙編指令可以看出,調用函數 user32.MessageBoxA 時使用了寫死,即将該函數在虛拟位址空間配置設定的 VA 直接寫人了代碼段,不通過導入表直接跳轉到函數代碼處執行,上面反彙編代碼加黑部分即為位址位元組碼(在這裡 OD 錯誤地将它識别成了指令序列)。使用寫死最大的好處是引入動态連結庫的函數時不需要修改導入表。因為 FirstWindow 引入的動态連結庫就這一個,不存在基位址被占用的問題,是以,與重定位有關的資訊在此例中不需要進

行修改。

導出私有函數:

在某些場合下,DLL 中的私有函數還是很有用的。也許是出于保密考慮,或者其他原因,DLL 的開發者将一些比較重要的函數設定為内部私有函數,并不在導出表中聲明。當程式被二次開發時,開發者卻需要這些函數,這時候就需要開發者自己将這些被定義為私有的函數添加到導出表中。

在本章的執行個體中,程式 winResult.dll 一共導出了4 個公有函數; 源代碼中的 TopXY 函數被聲明為私有函數,并未導出,是以在使用PEInfo 分析時看不到該函數。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

導出私有函數所要的步驟:

首先,将最原始的導出表整體搬遷到一個空閑空間中(原來部分可删可不删)。

先附上前面導出表資料結構執行個體分析和導出表定位中确定的導出表RVA和資料大小:

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

這裡選擇從檔案偏移 0x0940 處搬到 0x0a50 處。以下是添加私有函數到導出表後的兩處位址對應的位元組碼:

(注意将覆寫dll中函數位址部分的指令代碼目錄中暴力覆寫的FadeInOpen 定義部分改回原樣,不然程式運作不了)

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

現在從幾個方面來分析添加了私有函數的導出表與原有導出表的差別:

1) 長度從 8Fh 變成了 9Fh。增加的部分包含:

口 函數名:'TopXY\0'共 6 個位元組。

口 函數的 RVA: 0x0000100c,共 4 個位元組。

口 函數名稱所在位址: 0x000032e9,共 4 個位元組。

2) 函數個數由原來的4個變成 5 個(見位元組碼的第一個藍色大框部分)。

3) 修正其他因搬遷和增加而變動的位址。比如記憶體30偏移對A0。

4) 資料目錄項修改。因為導出表位置和資料大小發生了變化,是以 PE 檔案頭部的資料目錄項中需要進行如下修正:

口位置由原來的 0x2140 變成 0x3050。

口大小由原來的 8Fh 變成 9Fh。

WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表
WINDOWS+PE權威指南讀書筆記(9)PE中的導出表

如上所示,導出函數已經由最初的4個變成了5個 ; 在導出函數的描述部分也顯示了新函數的導出序号、函數所在的RVA,以及新導出函數的名稱TopXY,這意味着将私有函數轉換為導出函數是成功了。

繼續閱讀