1. 程式人生 > >PE文件格式詳解(七)

PE文件格式詳解(七)

msgbox nbsp clas com 類型 結構 size date 是的

PE文件格式詳解(七)

Ox00 前言

前面好幾篇在講輸入表,今天要講的是輸出表和地址的是地址重定位。有了前面的基礎,其實對於怎麽找輸出表地址重定位的表已經非常熟悉了。

0x01 輸出表結構

當創建一個DLL文件時,實際上創建了一組能讓EXE或者其他DLL調用的一組函數,PE裝載器根據DLL文件中輸出信息修正正在執行文件的IAT。當一個DLL函數能被EXE或者DLL文件使用時,它被稱為輸出了。其中輸出信息被保存在輸出表中,DLL文件通過輸出表向系統提供輸出函數名,序號和入口信息。

對於EXE文件一般不存在輸出表,而大部分DLL文件都有輸出表。輸出表是由一個叫做IMAGE_EXPORT_DIRECTORY

(簡稱IED)組成。IED存放著輸出函數名,輸出序數等信息,他的結構如下:

typedef struct _IMAGE_EXPORT_DIRECTORY {

DWORD Characteristics; // 未使用,總為0

DWORD TimeDateStamp; // 文件創建時間戳

WORD MajorVersion; // 未使用,總為0

WORD MinorVersion; // 未使用,總為0

DWORD Name; // 指向一個代表此 DLL名字的 ASCII字符串的 RVA

DWORD Base; // 函數的起始序號

DWORD NumberOfFunctions; // 導出函數的總數

DWORD NumberOfNames; // 以名稱方式導出的函數的總數

DWORD AddressOfFunctions; // 指向輸出函數地址的RVA

DWORD AddressOfNames; // 指向輸出函數名字的RVA

DWORD AddressOfNameOrdinals; // 指向輸出函數序號的RVA

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

下面介紹幾個重要字段:

Name:指向的是的ASCII字符串的RVA

NumberOfFunctions:輸出表EAT表中的數據條目數。

NumberOfName:輸出表ENT表中的數據條目數,ENT也是一個RVA地址,不過每項指向的是函數名字的ascii碼。

AddressOfFunctions:是指向EATRVAEATRVA一個數組,每一項指向了被輸出的函數的地址。

AddressOfNames是指向ENTRVA,ENT也是一個數組,每項指向被輸出函數的名字。

下圖是他們之間的關系圖:

技術分享圖片

0x02實例分析輸出表結構

1)hexworkshop打開DLLDemo.DLL文件,找到數據目錄表的第一項,它在文件頭偏移78h處。如下圖:

技術分享圖片

值為RVA=4000h,轉化為FileOffset=800h。此處即為輸出表結構所在地址。

2)跳往800h,依次讀出幾個重要字段的值如下圖:

技術分享圖片

由上圖可知各個字段的RVA值,Name=0000 4032NumberOfFunctions=0000 0010 NumberOfNames0000 0010

AddressOfFunctions=0000 4028 AddressOfNames=0000 402c 。兩個Address字段全都轉化為FileOffset值。

Name=832hAddressOfFunctions=828hAddressOfNames=82ch。跳往832h如下圖:

技術分享圖片

可知輸出的DLL名字叫做DLLDemo.DLL,跳往828h即可找到輸出函數的EAT表,跳往82ch即可找到輸出函數的ENT表。

0x03 輸出實現過程總結

PE裝載器調用GetProcAddress來查找DLLDemo.DLL裏的api函數,系統通過定位DLLDemo.DLLIMAGE_EXPORT_DIRECTORY(出書目錄表)結構開始工作,從這個結構中他獲得輸出函數名稱表(ENTb表)的起始地址,進而知道這個數組只有一個元素,他對名字進行二進制查找直到發現字符串“MegBox”。PE裝載器發現MsgBox是數組的第一個元素,加載器然後從輸出序數表讀取相應的第一個值,這個值是MsgBox的輸出序數。使用輸出序數作為進入EAT的索引,他得到MsgBox的值是1008h1008h加上DllDemo.DLL的裝入地址得到MsgBox的實際地址。

0x04 基址重定位概念

當連接器生成一個PE文件時,他假設這個文件執行時會被裝入默認的基址處,並且把codedata的相關地址寫入PE文件中。如果裝入是按照默認的值作為基址裝入,則不需要基址重定位,但是如果可執行文件被裝在到內存中的另一個地址,鏈接器所登記的地址就是錯的這時就需要用重定位表來調整。在PE文件中,它往往單獨分為一塊,用“.reloc”來表示。PE文件重定位過程。

0x05 詳細解讀基址重定位

基址重定位表放在一個位於.relo的區域中,但是找到它需要通過數據目錄表的第五項成員Base reloction Table,這項所指向重定位的基本結構IMAGE_BASE_RELOCATION

IMAGE_BASE_RELOCATION的基本定義如下:

struct IMAGE_BASE_RELOCATION {

DWORD VirtualAddress;//重定位數據開始RVA

DWORD SizeOfBlock;//重定位塊的長度

WORD TypeOffset;//重定位項位數組

}

IMAGE_BASE_RELOCATION ENDS

下面分別解釋一下這幾個字段:

VirtualAddress是這一組重定位數據的開始的RVA地址。其實就是原來的地址加上這個值就完成了重定位。

SizeOfBlock當前重定位結構的大小,因為VirtualAddressSizeOfBlock都是固定的四個字節,所以SizeOfBlock的值減去8就得到了重定位塊的大小。

TypeOffset:這個字段很有意思,一個兩個字節16位,高四位代表重定義類型,低12位裝入的是我們需要重定位的地址,即這個地址加上前面的字段VirtualAddress的值完成重定位。註意Typeoffset是一個數組他的值有SizeOfBlock-8決定。

下圖位重定位示意圖:

技術分享圖片

0x06 實例講解地址重定位

1)hexWrokShop打開PE文件DllDemo.DLL。通過數據目錄表的第五項找到重定位結構IMAGE_BASE_RELOCATION,即在PE文件頭偏移地址為A0h處,跳往a00h處,下圖標黑地方即為結構IMAGE_BASE_RELOCATION數據。

技術分享圖片

我們註意到第一個字段VritualAddress=0000 1000hSizeOfBlock=0000 0010h,由此可得數組TypeOffset共八個個字節。每個元素兩個字節則共四組數據。整理得出下表:

項目

重定位數據1

重定位數據2

重定位數據3

重定位數據4

原始數據

0F 30

23 30

00 00

00 00

TypeOffset

30 0F

30 23

00 00

00 00

TypeOffset高四位

3

3

TypeOffset低四位

00F

023

12位加上VritualAddress

100FRVA值)

1023RVA值)

轉換成FileOffset

20F

223

重定位後的地址如下圖:

技術分享圖片技術分享圖片

PE文件格式詳解(七)