0x00 前言
前面了解了PE文件的輸入和輸出,今天來看看另一個重要的結構——資源。資源結構是很典型的樹形結構,層層查找,最終找到資源位置。
0x01 資源結構介紹
Windows程序的各種界面成為資源,包括加速鍵,位圖,光標,對話框,圖標,菜單,串標,工具欄,版本信息等等,在所有的PE文件中資源結構是最為復雜的。下圖為資源的樹形結構圖:
通常來講,資源的目錄為三層結構。最上面的為根目錄,它存儲了資源的類型同時存儲指向下一級的指針,二級目錄包含資源id(至於啥事資源ID后面會講到)和指向第三級的指針,三級目錄存儲資源代碼和指向真正資源的指針。這里最要理解的是這三級目錄用的都是同一結構。這個結構包含兩個子結構——資源目錄結構(IMAGE_RESOURCE_DIRECTORY)和資源目錄入口地址結構(IMAGE_RESOURCE_DIRECTOTY_RNTRY)。理解了這兩個結構各自的作用就能明白整個資源結構。下面分別從這兩個結構講起。
0x02 資源目錄結構(IMAGE_RESOURCE_DIRECTORY)
資源目錄結構是由數據目錄表的第三個子項(Resource Table)所指向的(想想數據目標表有多重要把,基本上后面所講都要從它開始)。該結構包含16個字節,共6個字段,下面的該結構的定義:
// 【資源表位於數據目錄表的第三項,共動態分配字節, 其中結構體中的成員指出的RVA偏移量都是對於此結構體的地址作為基地址】
typedef struct _IMAGE_RESOURCE_DIRECTORY
{
DWORD Characteristics; //理論上為資源的屬性,不過事實上總是0
DWORD TimeDateStamp; //資源的產生時刻
WORD MajorVersion; //理論上為資源的版本,不過事實上總是0
WORD MinorVersion
WORD NumberOfNamedEntries; //以名稱(字符串)命名的入口數量
WORD NumberOfIdEntries; //以ID(整型數字)命名的入口數量
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
這六個字段真正有用的是最后兩個字段:
WORD NumberOfNamedEntries:標明入口數量,所謂入口數量,資源目錄入口地址結構(IMAGE_RESOURCE_DIRECTOTY_RNTRY)結構的數量,這個結構都是緊緊跟着資源目錄結構(IMAGE_RESOURCE_DIRECTORY),不過它標記數量的方式是用字符串。
WORD NumberOfIdEntries:這個字段本質上和前一個字段一模一樣,不過它是用id來標明數量。注意:最后統計資源入口地址結構數量的時候是這兩個的相加的和。
0x03 資源目錄入口地址結構(IMAGE_RESOURCE_DIRECTORY_ENTRY)
資源目錄入口地址結構雖然很簡單,但是卻非常重要。它不僅包含了指向下一級目錄的地址,好包含了指向真正資源的數據入口地址。
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY
{
DWORD Name; //目錄項的名稱字符串指針或者ID
DWORD OffsetToData; //資源數據偏移地址或者子目錄偏移地址
}_IMAGE_RESOURCE_DIRECTORY_ENTRY, *P_IMAGE_RESOURCE_DIRECTORY_ENTRY;
這兩個字段都是極為重要的,下面將詳細講解這兩個字段的含義:
Name:大小為雙字,該字段定義了目錄項的名稱或者ID。當用於第一級目錄時,它表示的是資源的類型,用於第二級目錄時表示資源的名稱,用於第三級目錄時,用於表示資源的代碼頁號編號。當最高位是0的時候,表示字段的值作為ID來使用(前面說了該字段可以表示資源的類型,資源的類型就是用ID來區分的,不同資源類型ID不一樣)。當最高位是1時,地位字段表示一個指針,這個指針就是指向了資源的名稱的UNICODE編碼(前面也講到該字段可以表示目錄的名稱)。下表統計了系統已經定義的資源類型。表一
類型ID值 |
資源類型 |
類型ID值 |
資源類型 |
01h |
光標(Cursor) |
08h |
字體(Font) |
02h |
位圖(Bitmap) |
09h |
加速鍵(Accelerator) |
03h |
圖標(Icon) |
0ah |
未格式資源(Unformatted) |
04h |
菜單(Menu) |
Obh |
消息表(MessageTable) |
05h |
對話框(Dialog) |
0ch |
光標組(Group Cursor) |
06h |
字符串(Stiring) |
0eh |
圖標組(Group Icon) |
07h |
字體目錄(Font Directory) |
10h |
版本信息(Version Information) |
OffsetToData:雙字結構,是一個指針。當最高為為1時,低位指向的是下一級目錄的起始地址,當最高為是0時,指向的是真正的數據資源的目錄入口結構(IMAGE_RESOURCR_DIRCTORY_DATA)。
重要說明:當字段Name和Offset作為指針時,該指針是從資源區塊開始的地方算起的偏移量,不是RVA,即根目錄的起始位置偏移量(就是從第一級資源目錄結構(IMAGE_RESOURCE_DIRECTORY)的起始地址算起)
0x03 實例講解資源的結構
工具:hexworkshop,lordPE,目標PE文件:pediy.exe。
下面我將一級級逐級追擊資源:
1)根目錄
首先找到數據目錄表的第三項,它指向了第一級目錄即根目錄地址。它在PE文件頭偏移88h處。地址為:0ch+88h=148h。直接跳轉至148h處,如下圖:
標明RVA=4000h,這里本來如果要跳轉至根目錄是要進行地址轉化,但是這里是特殊情況,由於這個PE文件磁盤分頁和內存分頁的值相等,故RVA=Offset,緣由可看下圖:
我們現在直接跳轉至4000h處,該處即為根目錄起始地址,如下圖:
現在我們按照順序以西讀出這個第一個結構IMAGE_RESOURCE_DIRECTORY的六個字段的值。記在下表中:表二
Charateristics |
TimeStamp |
MajorVersion |
MinnorVersion |
NumberOfEntris |
NumberOfIdEntries |
|
0000 0000 |
0000 0000 |
0000 |
0000 |
0000 |
0003 |
|
由最后兩個字段可知緊隨IMAGE_RESOURCE_DIRECTORY的共有三個IMAGE_RESOURCE_DIRECTORY_ENTRY結構,如下圖:
依次將數據讀出登記在下表:表三
|
第一個Dir_entry結構 |
第二個Dir_entry結構 |
第三個Dir_entry結構 |
偏移地址 |
4010h |
4018h |
4020h |
字段Name |
0000 0003h |
0000 0004h |
0000 000Eh |
字段OffsetToData |
8000 0028h |
8000 0040h |
8000 0058h |
由於有三個下級目錄,我們就以第二個IMAGE_RESOURCE_DIRECTORY_ENTRY結構來分析下一級資源結構。
2)二級目錄
根目錄的IMAGE_RESOURCE_DIRECTORY_ENTRY結構的字段Name=0000 0004h,最高為0,所以它表示資源類型,低位值為0004h,這個資源的ID號,查詢表一可得這是個菜單(Mune資源)。再來看字段OffsetToData=8000 0040h,明顯最高為是1,所以這個指針指向的是下一級資源目錄。其低31位值是40h,這個就是下級目錄距離根目錄的偏移地址,地址為:
4000h+40h=4040h。我們跳往地址4040h處,得到下圖:
依次讀出IMAGE_RESOURCE_DIRECTORY結構的六個字段統計在下表:
Charateristics |
TimeStamp |
MajorVersion |
MinnorVersion |
NumberOfEntris |
NumberOfIdEntries |
0000 0000 |
0000 0000 |
0000 |
0000 |
0000 |
0000 0001 |
根據最后一個字段可知,緊跟這結構IMAGE_RESOURCE_DIRECTORY的結構IMAGE_RESOURCE_DIRECTORY_ENTRY只有一個,如下圖:
我們依次讀出兩個字段的值:Name=8000 00E8h OffsetToData=8000 0088h。由於字段Name的最高位是1,所以低位字段表示指向結構IMAGE_RESOURCE_DATA_STRING_U結構,這個結構存儲了資源名的unicod值,我們跳往40E8h處,如下圖:
由上圖可知資源名是PEDIY。第二個字段OffsetToData的最高位是1,低位表示指向下級目錄的地址,我們跳往4088h。
3)第三級目錄
跳轉至地址4088h,我們來到了第三級目錄,如下圖:
依次讀出六個字段統計在下表:
Charateristics |
TimeStamp |
MajorVersion |
MinnorVersion |
NumberOfEntris |
NumberOfIdEntries |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0001 |
根據最后一個字段可知緊隨結構IMAGE_RESOURCE_DIRECTORY只有一個IMAGE_RESOURCE_DIRECTORY_ENTRY結構,如下圖:
字段Name=0000 0409h,字段OffsetToData=0000 00C8h。在第三級目錄中Name字段表示代碼頁編號,這里是409表示英語,OffsetToData最高位是0,低31位作為指針指向資源數據入口結構(IMAGE_RESOURCE_DATA_ENTRY)。該結構的定義如下:
IMAGE_RESOURCE_DATA_ENTRY STRUCT
{
OffsetToData DWORD //資源數據的RVA
Size DWORD//資源數據的長度
CodePage DWORD//代碼頁,一般為0
Reserved DWORD//保留字段
};IMAGE_RESOURCE_DATA_ENTRY ENDS
我們直接跳轉至40c8h處,如下圖:
逐個讀出各個字段的值統計在下表中:
OffsetToData |
Size |
CodePage |
Reserved |
0000 4400 |
0000 005a |
0000 0000 |
0000 0000 |
至此,我們總算找到了資源的真正地址即在字段OffsetToData=4400h,大小為字段Size=5ah。
如下圖就是資源: