一. PE文件結構圖
二. DOS 頭部
其中最后一個字段DWORD e_lfanew;的值為PE文件頭的相對偏移地址(RVA);
三.PE文件頭
結構體的定義:IMAGE_NT_HEADERS:左邊地址為相對PE文件頭地址的偏移量。
DWORD Signature:PE文件頭標識,一般其值為:0x00004550;對應字符為PE..;
1.映像文件頭(_IMAGE_FILE_HEADER FileHeader):
左邊地址為相對映像文件頭的地址文件頭地址的偏移量。
IMAGE_FILE_HEADER的大小為20字節.
第一個成員為WORD類型的Machine,這個通常為0x014C(intel 386系列CPU);
第二個成員為WORD類型的NumberOfSections,即文件中節的數量,這個對應的文件具體的節區數量。
第三個成員為DWORD類型的TimeDateStamp,是一個時間戳,對應於從1970年1月1日開始的秒數。
第四個和第五個成員分別為DWORD類型的PointerToSymbolTable和NumberOfSymbols,和調試相關,這里忽略。
第六個成員為WORD類型的SizeOfOptionalHeader,即后面可選映像頭部分的大小,即為IMAGE_OPTIONAL_HEADER的大小,一般情況下32位的為0x00E0,64位的為0x00F0
第七個成員為WORD類型的Characteristics,反映出這個文件的類型以及運行平台的特征。
2.可選映像頭(IMAGE_OPTIONAL_HEADER OptionalHeader)
比較重要的字段:
(1)WORD Magic:標記字,其說明了文件的映像類型:
ROM映像:0x0107
普通可執行的映像:0x010B(一般)
PE(32+)映像:0x020B
(2) 。DWORD AddressOfEntryPoint,這個字段是程序執行的入口RVA地址。
對於DLL文件,這個入口點是在進程初始化和關閉時以及線程創建和毀滅時被調用。大多數的的可執行文件中這個地址並不指向MAIN,WINMAIN或DLLMAIN,而是指向運行時庫代碼並由其來調用上述函數在DLL中這個域可以被置為0
BaseOfCode:代碼段的起始RVA。通常為1000H
BaseOfData: 數據段的起始RVA。在64位的可執行文件中其是不存在的,了解就行
(3) ImageBase為建議的裝載地址。對於可執行文件來說一般是
0x00400000。
SectionAlignment為內存中節的對齊大小,即每個區段裝入得大小必須是該值得整數倍,一般為0x00001000H(4KB).FileAlignment為PE文件中節的對齊大小
(4) Subsystem: 指定程序的子系統類型.
微軟定義如下:
Value |
Meaning |
|
|
IMAGE_SUBSYSTEM_UNKNOWN 0 |
Unknown subsystem. |
|
|
IMAGE_SUBSYSTEM_NATIVE 1 |
No subsystem required (device drivers and native system processes). |
|
|
IMAGE_SUBSYSTEM_WINDOWS_GUI 2 |
Windows graphical user interface (GUI) subsystem. |
|
|
IMAGE_SUBSYSTEM_WINDOWS_CUI 3 |
Windows character-mode user interface (CUI) subsystem. |
|
|
IMAGE_SUBSYSTEM_OS2_CUI 5 |
OS/2 CUI subsystem. |
|
|
IMAGE_SUBSYSTEM_POSIX_CUI 7 |
POSIX CUI subsystem. |
|
|
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 |
Windows CE system. |
|
|
IMAGE_SUBSYSTEM_EFI_APPLICATION 10 |
Extensible Firmware Interface (EFI) application. |
|
|
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 |
EFI driver with boot services. |
|
|
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 |
EFI driver with run-time services. |
|
|
IMAGE_SUBSYSTEM_EFI_ROM 13 |
EFI ROM image. |
|
|
IMAGE_SUBSYSTEM_XBOX 14 |
Xbox system. |
|
|
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 |
Boot application. |
|
|
☆數據目錄表DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
其指向了輸出表,輸入表,資源塊等數據。其由數個相同的IMAGE_DATA_DIRECTORY結構組成。數據目錄成員(16個成員)定義如下:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
基本結構如右圖:
對應的偏移地址(RVA)相對PE文件頭地址的偏移量及數據塊的大小:
Offset (PE/PE32+) |
Description |
96/112(78h) |
Export table address and size(輸出表的地址與大小) |
104/120(80h) |
Import table address and size(輸入表的地址與大小) |
112/128(88h) |
Resource table address and size |
120/136(90h) |
Exception table address and size |
128/144(98h) |
Certificate table address and size |
136/152(A0h) |
Base relocation table address and size |
144/160(A8h) |
Debugging information starting address and size |
152/168(B0h) |
Architecture-specific data address and size |
160/176(B8h) |
Global pointer register relative virtual address |
168/184(C0h) |
Thread local storage (TLS) table address and size |
176/192(C8h) |
Load configuration table address and size |
184/200(D0h) |
Bound import table address and size |
192/208(D8h) |
Import address table address and size |
200/216(E0h) |
Delay import descriptor address and size |
208/224(E8h) |
The CLR header address and size |
216/232 |
Reserved(保留) |
●輸出表(IMAGE_DIRECTORY_ENTRY_EXPORT)
輸出表的內容即結構體IMAGE_DIRECTORY_ENTRY_EXPORT所指向的地址的內容
1.當PE文件被執行的時候,windows裝載器將文件裝入內存並將輸入表中登記的DLL文件一並裝入,再根據DLL文件中的函數輸出信息對被執行文件的IAT表進行修正。在這些包含輸出函數的DLL文件中,輸出信息被保存在輸出表中,通過輸出表,DLL文件向系統提供輸出函數的名稱,序號和入口地址等信息。以便windows裝載器通過這些信息來完成動態鏈接的過程。一般來說,exe文件不存在輸出表,DLL文件大部分都包含輸出表,不過這不是絕對的。
2.Base:導出函數序號的起始值,將AddressOfFunctions 字段指向的入口地址表的索引號加上這個起始值就是對應函數的導出 序號。假如Base 字段的值為x,那么入口地址表指定的第1個導出函數的序號就是x;第2個導出函數的序號就是x+1。總之,一個導出函數的導出序號等 於Base 字段的值加上其在入口地址表中的位置索引值。
3.AddressOfNames 和 AddressOfNameOrdinals:均為RVA 值。前者指向函數名字符串地址表。這個地址表是一個雙字數組,數組中的每一項指向一個函數名稱字符串的RVA。數組的項數等於NumberOfNames 字段的值,所有有名稱的導出函數的名稱字符串都定義在這個表中;后者指向另一個word 類型的數組(注意不是雙字數組)。數組項目與文件名地址表中的項目一一對應,項目值代表函數入口地址表的索引,這樣函 數名稱與函數入口地址關聯起來。(舉個例子說,加入函數名稱字符串地址表的第n 項指向一個字符串“MyFunction”,那么可以去查找 AddressOfNameOrdinals 指向的數組的第n 項,假如第n 項中存放的值是x,則表示AddressOfFunctions 字段描述的地址表中的第x 項函數入口地址對應的名稱就是“MyFunction”)
- ●輸入表(IMAGE_DIRECTORY_ENTRY_IMPORT )☆
IID(_IMAGE_IMPORT_DESCRIPTOR)結構如下圖所示:
每個被PE文件隱式地連接進來的DLL文件都有一個IID。在這個結構中沒有字段指出該結構數組的項數,但它的最后一個單元式NULL,可以由此計算出該數組的項數
如下圖所示:
IMAGE_THUNK_DATA為DWORD大小
當 IMAGE_THUNK_DATA 值的最高位為 1時,表示函數以序號方式輸入,這時候低 31位被看作一個函數序號。當
IMAGE_THUNK_DATA 值的最高位為 0時,表示函數以字符串類型的函數名方式輸入,這時雙字的值是一個 RVA,指向一個 IMAGE_IMPORT_BY_NAME 結構。
上圖中最后加的14位NULL標記為最后一項。
IMAGE_IMPORT_BY_NAME結構如右圖。
結構中的 Hint 字段也表示函數的序號,不過這個字段是可選的,有些編譯器總是將它設置為 0,Name 字段定義了導入函數的名稱字符串,這是一個以 0 為結尾的字符串。
為什么由兩個並行的指針數組同時指向 IMAGE_IMPORT_BY_NAME 結構呢?第一個數組(由 OriginalFirstThunk 所指向)是單獨的一項,而且不能被改寫,我們前邊稱為 INT。第二個數組(由 FirstThunk 所指向)事實上是由 PE 裝載器重寫的。
PE 裝載器首先搜索 OriginalFirstThunk ,找到之后加載程序迭代搜索數組中的每個指針,找到每個 IMAGE_IMPORT_BY_NAME 結構所指向的輸入函數的地址,然后加載器用函數真正入口地址來替代由 FirstThunk 數組中的一個入口,因此我們稱為輸入地址表(IAT)。
OriginalFirstThunk指向INT表,存儲API函數名稱。FirstThunk執行IAT表,存儲API函數地址,即是PE文件在影射到內存空間后,所用到的地址表,此時OringinalFirstThunK 就無用啦。