PE檔案詳解二
0x00 前言
上一篇講到了PE檔案頭的中IMAGE_FILE_HEADER結構的第二個結構,今天從IMAGE_FILE_HEADER中第三個結構sizeOfOptionalHeader講起。這個欄位的結構名也叫做IMAGE_OPTIONAL_HEDAER講起。
0x01 IMAGE_OPTIONAL_HEADER概述
其實這個結構是IMAGE_FILE_HEADER結構的補充。這兩個結構合起來才能對整個PE檔案頭進行描述。這個結構異常複雜,但真正我們用得到的其實不多,下面來看看它的各個欄位情況,如下圖(左邊的16位字元表示相對於檔案頭的偏移量):
ypedef struct _IMAGE_OPTIONAL_HEADER
{
//
// Standard fields.
//
+18h WORD Magic; // 標誌字, ROM 映像(0107h),普通可執行檔案(010Bh)
+1Ah BYTE MajorLinkerVersion; // 連結程式的主版本號
+1Bh BYTE MinorLinkerVersion; // 連結程式的次版本號
+1Ch DWORD SizeOfCode; // 所有含程式碼的節的總大小
+20h DWORD SizeOfInitializedData; // 所有含已初始化資料的節的總大小
+24h DWORD SizeOfUninitializedData; // 所有含未初始化資料的節的大小
+28h DWORD AddressOfEntryPoint; // 程式執行入口RVA
+2Ch DWORD BaseOfCode; // 程式碼的區塊的起始RVA
+30h DWORD BaseOfData; // 資料的區塊的起始RVA
//
// NT additional fields. 以下是屬於NT結構增加的領域。
//
+34h DWORD ImageBase; // 程式的首選裝載地址
+38h DWORD SectionAlignment; // 記憶體中的區塊的對齊大小
+3Ch DWORD FileAlignment; // 檔案中的區塊的對齊大小
+40h WORD MajorOperatingSystemVersion; // 要求作業系統最低版本號的主版本號
+42h WORD MinorOperatingSystemVersion; // 要求作業系統最低版本號的副版本號
+44h WORD MajorImageVersion; // 可運行於作業系統的主版本號
+46h WORD MinorImageVersion; // 可運行於作業系統的次版本號
+48h WORD MajorSubsystemVersion; // 要求最低子系統版本的主版本號
+4Ah WORD MinorSubsystemVersion; // 要求最低子系統版本的次版本號
+4Ch DWORD Win32VersionValue; // 莫須有欄位,不被病毒利用的話一般為0
+50h DWORD SizeOfImage; // 映像裝入記憶體後的總尺寸
+54h DWORD SizeOfHeaders; // 所有頭 + 區塊表的尺寸大小
+58h DWORD CheckSum; // 映像的校檢和
+5Ch WORD Subsystem; // 可執行檔案期望的子系統
+5Eh WORD DllCharacteristics; // DllMain()函式何時被呼叫,預設為 0
+60h DWORD SizeOfStackReserve; // 初始化時的棧大小
+64h DWORD SizeOfStackCommit; // 初始化時實際提交的棧大小
+68h DWORD SizeOfHeapReserve; // 初始化時保留的堆大小
+6Ch DWORD SizeOfHeapCommit; // 初始化時實際提交的堆大小
+70h DWORD LoaderFlags; // 與除錯有關,預設為 0
+74h DWORD NumberOfRvaAndSizes; // 下邊資料目錄的項數,這個欄位自Windows NT 釋出以來 // 一直是16
+78h DWORD DataDirctory[16];
// 資料目錄表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
這裡總共31個欄位但是常用的其實就是我用紅色字型標明的。
前面我們已經知道了PE檔案頭在40h的位置,則上面的偏移量推斷IMAGE_OPTIONAL_HEADER欄位的首個欄位在40h+18h=58h的地方,我們還是用hexwrokshop開啟那個PE檔案。Ctrl+G開啟轉移視窗,輸入58則找到了第一個欄位位置,如下圖:
對於這31個欄位我們今天最為關心的是最後一個欄位DataDirctory[16]我們一眼就能看出這是一個數組,其中的每個元素都是由一個叫做IMAGE_DATA_DIRECTORY的結構組成。這個叫做IMAGE_DATA_DIRACTORY的結構如下:
IMAGE_DATA_DIRACTORY STRUC
VritualAddress DWORD //資料塊的起始RVA
Size DWORD //資料塊的長度
IMAGE_DATA_DIRACTORY RENS
下面是DataDirctory[16]即資料目錄表的各個成員
索 引 |
索引值在Windows.inc中的預定義值 |
對應的資料塊 |
偏移量 |
0 |
IMAGE_DIRECTORY_ENTRY_EXPORT |
匯出表 |
78h |
1 |
IMAGE_DIRECTORY_ENTRY_IMPORT |
匯入表 |
80h |
2 |
IMAGE_DIRECTORY_ENTRY_RESOURCE |
資源 |
88h |
3 |
IMAGE_DIRECTORY_ENTRY_EXCEPTION |
異常(具體資料不詳) |
90h |
4 |
IMAGE_DIRECTORY_ENTRY_SECURITY |
安全(具體資料不詳) |
98h |
5 |
IMAGE_DIRECTORY_ENTRY_BASERELOC |
重定位表 |
A0h |
6 |
IMAGE_DIRECTORY_ENTRY_DEBUG |
除錯資訊 |
A8h |
7 |
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE |
版權資訊 |
B0h |
8 |
IMAGE_DIRECTORY_ENTRY_GLOBALPTR |
具體資料不詳 |
B8h |
9 |
IMAGE_DIRECTORY_ENTRY_TLS |
Thread Local Storage |
C0h |
10 |
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG |
具體資料不詳 |
C8h |
11 |
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT |
具體資料不詳 |
D0h |
12 |
IMAGE_DIRECTORY_ENTRY_IAT |
匯入函式地址表 |
D8h |
13 |
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT |
具體資料不詳 |
E0h |
14 |
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR |
具體資料不詳 |
E8h |
15 |
未使用 |
|
保留 |
這張表的16個成員中第一個成員IMAGE_DIRECTORY_ENTRY_EXPORT(匯出表)和第二個成員IMAGE_DIRECTORY_ENTRY_EXPORT(匯入表)非常重要。下面我們用另一個PE檔案來檢視資訊。由於前面的PE.exe沒有輸出表,所以換一個Dumped.DLL這個有輸出表的來檢視結構。步驟如下:
1.用Hexwrokshop開啟檔案,首先找到PE檔案頭位置,一般都是在載入起始位+3ch處,如下圖所示。
圖中被選中的黑色處100h,故可知PE檔案頭在100h處,用快捷鍵跳ctrl+G跳轉到該處 ,上圖示黑部分即PE檔案頭位置。
2.找到了PE檔案頭的位置,接下來我們來找DataDirctory[16]各個成員位置。第一個成員輸出表位於PE檔案頭+78h位置即100h+78h=178h處,如下圖:
由於每個結構都佔8個位元組,所以可以知道輸出表的其實位置在4000h處,大小為45h
輸入表的位置位100h+80h=180h處,如下圖:
由上圖可知輸入表的起始位置在3000h處,大小為52h。
3.其實除了這麼查詢,還有一種更為簡單的方式。
我們要用到另一個工具LordPE。
步驟如下:
1)開啟lordPE,點選PE編輯器即可檢視PE檔案頭的許多資訊,如下圖:
2)再點選目錄按鈕即可檢視資料目錄表的相關資訊。如下圖:
由上圖我們直接就能看到輸出表RVA為4000h大小為45h,輸入表的RVA為3000h,大小為52h。這和我們計算的查詢的結果一致。