PE文件定義
PE 文件(”Portable executable”, 可移植的可執行文件)文件格式,是微軟Windows NT, 中Win32、Win32s中的可執行的二進制的文件格式。 包括:.exe, .dll, .sys, .com, .ocs. PE文件最重要的兩個因素:
1.磁盤上的可執行文件和它被映射到windows內存之后的格式非常相像。
2.對於Win32 來講, 模塊中多使用的所有代碼、數據、資源、導入表、和其他需要的模塊數據結構都在一個連續的內存塊中。因此,只需要知道PE Loader把可執行文件映射到了內存的什么地方(基址),通過作為映像的一部分指針,就可以找到這個模塊的所有不同的塊。
PE文件總覽:
1. DOS Header: (size:64byte)
_IMAGE_DOS_HEADER結構體:
1 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header 2 WORD e_magic; // Magic number 3 WORD e_cblp; // Bytes on last page of file 4 WORD e_cp; // Pages in file 5 WORD e_crlc; // Relocations 6 WORD e_cparhdr; // Size of header in paragraphs 7 WORD e_minalloc; // Minimum extra paragraphs needed 8 WORD e_maxalloc; // Maximum extra paragraphs needed 9 WORD e_ss; // Initial (relative) SS value 10 WORD e_sp; // Initial SP value 11 WORD e_csum; // Checksum 12 WORD e_ip; // Initial IP value 13 WORD e_cs; // Initial (relative) CS value 14 WORD e_lfarlc; // File address of relocation table 15 WORD e_ovno; // Overlay number 16 WORD e_res[4]; // Reserved words 17 WORD e_oemid; // OEM identifier (for e_oeminfo) 18 WORD e_oeminfo; // OEM information; e_oemid specific 19 WORD e_res2[10]; // Reserved words 20 LONG e_lfanew; // File address of new exe header 21 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
結構體中有兩個重要的數據成員。第一個為e_magic,這個必須為MZ,即0x5A4D。另一個重要的數據成員是最后一個成員e_lfanew,這個成員的值為IMAGE_NT_HEADERS的偏移。其中,*e_lfanew這個字段的值: PE Header 在磁盤文件中相對於文件開始的偏移地址.
實例截圖:
2. PE Header: (size: 248bytes)
IMAGE_NT_HEADERS 緊接在DOS Stub之后,位置有e_lfanew所指
1 typedef struct _IMAGE_NT_HEADERS { 2 DWORD Signature; //4 bytes PE文件頭標志:(e_lfanew)->‘PE’ 3 IMAGE_FILE_HEADER FileHeader; //20 bytes PE文件物理分布的信息 4 IMAGE_OPTIONAL_HEADER32 OptionalHeader;//224bytes PE文件邏輯分布的信息 5 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
PE Header 總覽
IMAGE_NT_HEADERS結構體成員解析:
2.1.Signature: (4 bytes)
2.2.IMAGE_FILE_HEADER(20 bytes)
1 typedef struct _IMAGE_FILE_HEADER { 2 WORD Machine; //運行平台 3 WORD NumberOfSections; //文件區塊數目 4 DWORD TimeDateStamp; //文件創建日期和時間 5 DWORD PointerToSymbolTable; //指向符號表(主要用於調試) 6 DWORD NumberOfSymbols; //符號表中符號個數 7 WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32 結構大小 8 WORD Characteristics; //文件屬性 9 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
IMAGE_FILE_HEADER結構體成員解析:
1). Machine 代表了CPU的類型 //運行平台

#define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. #define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian #define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian #define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian #define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2 #define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP #define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian #define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 #define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian #define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian #define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5 #define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian …………………. #define IMAGE_FILE_MACHINE_CEE 0xC0EE
2) NumberOfSections: 代表區塊的數目,區塊表緊跟在IMAGE_NT_HEADERS后面, 區塊表大概是一個鏈表結構,鏈表長度由NumberOfSection的數值決定.
3) TimeDataStamp: 表明文件的創建時間
4) SizeOfOptionalHeader: 是IMAGE_NT_HEADERS的另一個子結構IMAGE_OPTIONAL_HEADER的大小
5) Characteristics: 代表文件的屬性EXE文件一般是0100h DLL文件一般是210Eh,多種屬性可以用或運算同時擁有

#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // 重定位信息被移除 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // 文件可執行 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // 行號被移除 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符號被移除 …….. #define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32位機器 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的調試信息被移除 …………………. #define IMAGE_FILE_SYSTEM 0x1000 // 系統文件 #define IMAGE_FILE_DLL 0x2000 // 文件是一個dll #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // 文件只能運行在單處理器上
實例截圖:
2.3. IMAGE_OPTIONAL_HEADER(224 bytes)
緊接IMAGE_FILE_HEADER之后,IMAGE_OPTIONAL_HEADER的大小由IMAGE_FILE_HEADER中倒數第二個成員(SizeOfOptionalHeader)指定. IMAGE_OPTIONAL_HEADER結構體如下:
1 typedef struct _IMAGE_OPTIONAL_HEADER { 2 WORD Magic; //映像文件的狀態 3 BYTE MajorLinkerVersion; //連接器的主版本號 4 BYTE MinorLinkerVersion; //連接器的次版本號 5 DWORD SizeOfCode; //代碼段的大小,如果有多個代碼段則為總和 6 DWORD SizeOfInitializedData; //初始化數據段大小.如果多個則為總和 7 DWORD SizeOfUninitializedData;//未初始化數據段大小,.如果多個則為總和.bbs 8 DWORD AddressOfEntryPoint; //PE文件入口地址的RAV:OEP = ImageBase + RAV 9 DWORD BaseOfCode; //代碼塊起始地址的RVA 10 DWORD BaseOfData;//數據塊的起始地址的RVA 11 // 12 // NT additional fields. 13 // 14 DWORD ImageBase; //可執行文件的基址ImageBase 15 DWORD SectionAlignment; //每一個塊必須保證始於這個值的整數倍 16 DWORD FileAlignment; //對齊映射文件部分原始數據 2 or 512 or 64: 默認為512 17 WORD MajorOperatingSystemVersion;//要求的操作系統的主版本號 18 WORD MinorOperatingSystemVersion;//要求的操作系統的次版本號 19 WORD MajorImageVersion;//映像的主版本號 20 WORD MinorImageVersion;//映像的次版本號 21 WORD MajorSubsystemVersion;//子系統的主版本號 22 WORD MinorSubsystemVersion;//子系統的次版本號 23 DWORD Win32VersionValue;//保留值.必須為0 24 DWORD SizeOfImage;//映像文件的大小 25 DWORD SizeOfHeaders; 26 DWORD CheckSum;//映像文件的校驗和 27 WORD Subsystem;//運行此映像的字系統 28 WORD DllCharacteristics;//映像文件的DLL特征 29 DWORD SizeOfStackReserve;//堆棧保留字節. 0x100000 30 DWORD SizeOfStackCommit;//線程開始提交的初始堆棧大小 31 DWORD SizeOfHeapReserve;//為初始進程保留的虛擬內存總數 32 DWORD SizeOfHeapCommit;//進程開始提交初始虛擬內存的大小 33 DWORD LoaderFlags; 34 DWORD NumberOfRvaAndSizes; //0x10 35 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 36 //指向第一個IMAGE_DATA_DIRECTORY 37 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
_IMAGE_OPTIONAL_HEADER結構體實例截圖以及成員解析:
1) Magic:32位可執行文件來:0x010B
64位可執行文件來:0x020B
0x107
2) SizeOfCode: 代碼段的大小,如果有多個代碼段則為總和
RAW:經過文件對齊處理后大小(PE文件中的大小)
3) SizeOfInitializedData: 初始化數據段大小.如果多個則為總和
4) ImageBase: 建議的裝載地址
PE建議裝載地址:
實際裝載地址:
5) AddressOfEntryPoint: 程序執行的入口RVA地址
OEP = ImageBase + (AddressOfEntryPoint)RVA
6) SectionAlignment:為內存中節的對齊大小,一般為0×00001000
7) FileAlignment:為PE文件中節的對齊大小
8) SizeofImage:映像文件的大小
9) DataDirectory為數據目錄表數組,比較重要:共有16個表項
Size = sizeof(_IMAGE_DATA_DIRECTORY) * 16
sizeof(_IMAGE_DATA_DIRECTORY) = 8 bytes
_IMAGE_DATA_DIRECTORY結構體以及成員定義:
1 typedef struct _IMAGE_DATA_DIRECTORY { 2 DWORD VirtualAddress; 3 DWORD Size; 4 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 5 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 6 // Directory Entries 7 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory 8 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory 9 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory 10 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory 11 #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory 12 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table 13 #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory 14 // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) 15 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data 16 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP 17 #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory 18 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory 19 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers 20 #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table 21 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors 22 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
Import Table RVA & Size截圖:
3. Section Header:
Section Header 數量:_IMAGE_FILE_HEADER結構體中NumberOfSections成員。
Section Header 定位:緊跟在IMAGE_NT_HEADERS后面
結構體大小:40 bytes
_IMAGE_SECTION_HEADER 結構體以及重要變量的定義:
1 #define IMAGE_SIZEOF_SHORT_NAME 8 2 typedef struct _IMAGE_SECTION_HEADER { 3 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; 4 union { 5 DWORD PhysicalAddress; 6 DWORD VirtualSize; 7 } Misc; 8 DWORD VirtualAddress; //內存中偏移地址 9 DWORD SizeOfRawData; //PE文件中對其之后的大小 10 DWORD PointerToRawData;//為PE塊區在PE文件中偏移 11 DWORD PointerToRelocations; 12 DWORD PointerToLinenumbers; 13 WORD NumberOfRelocations; 14 WORD NumberOfLinenumbers; 15 DWORD Characteristics; //塊區的屬性:可讀、可寫.. 16 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 17 #define IMAGE_SIZEOF_SECTION_HEADER 40
重要數據成員:
1) Name[IMAGE_SIZEOF_SHORT_NAME]:
8字節大小的NAME, 如果節區名稱小於8個字節,則多余的用0填充,否則全部填充節名,末尾不保證有1個0,同樣會被名字填充
2) PointerToRawData:
為節區在PE文件中的偏移
3) Characteristics: 為節區的屬性,如可讀、可寫、可執行等

1 #define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code. 2 #define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations. 3 #define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded. 4 #define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable. 5 #define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable. 6 #define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable. 7 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable. 8 #define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
Section Header 實例截圖:
4. 導入表
_IMAGE_IMPORT_DESCRIPTOR 數據結構:(20 bytes)
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; DWORD ForwarderChain; // -1 if no forwarders DWORD Name; //dll 名稱 DWORD FirstThunk; //指向引入函數真實地址單元處的RVA IAT } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
其中OriginalFirstThunk和FirstThunk非常類似,指向兩個本質上相同的數組IMAGE_THUNK_DATA。
1) 定位查找IMAGE_IMPORT_DESCRIPTO結構
A 獲取引入表的RVA.也就是
data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress 所指的值
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
查看 IMAGE_DIRECTORY_ENTRY_IMPORT的值
&data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0x0002D51C
&data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualSize= 0xA0
B. 查找導入表所在的區段:
if(RVA>=SECTION.VirtualAddress && RVA<SECTION.Misc.VirtualSize)
{
//處於該節
}
else
{
//不在該節中
}
C. 找到引入表所在的節后就可以用該節的VirtualAddress和PointerToRawData兩個域確定引入表在文件中的偏移量:RVA –△k
VirtualAddress = 0x00026000
PointerToRawData = 0x00025000
△k = VirtualAddress – PointerToRawData = 0x1000H
Address = 0x0002D51C - △k = 0x0002C51C
然后定位到文件偏移處:
陰影部分是IID的內容, IID的大小為20h, 陰影部分存在鏈各個IID,最后一個為0000000, 說明此PE文件只有8個IID, 對應8個dll。
0xA0 = 160 bytes = 7 * 20bytes +20bytes(空白)
截圖
第四個變量的地址RVA:0002DB14, 需要轉換成對應的文件偏移
(0x0002DB14 –△k) = 0x0002cb14,定位到文件偏移為0x0002cb14的地方
查看內容,里面記錄的是IMAGE_IMPORT_DESCRIPTOR的第四個成員變量多對應的dll的名字,mfc90u.dll,到此,我們已經找到了這個輸入的dll
2) 獲取dll調用的所有函數
IMAGE_IMPORT_DESCRIPTOR中的第一個參數和最后一個參數,original_first_thunk 和first_thunk分別指向了INT(輸入名稱表)IAT(輸入地址表)這兩個表里面分別記錄了指向調用函數名的地址,和此函數在dll中的序號(序號用來快速索引dll中的函數)
0x0002D85C和0x000262A0是INT 和IAT數組的首地址,下面我們跳到該地址(由於△K=0x1000,故RVA=文件偏移0x0002C85C和0x000252A0)
_IMAGE_THUNK_DATA32數據結構:
typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; //數據 DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
a.當一個函數以序號導入.MAGE_THUNK_DATA結構中的AddressOfData最高位被設成1.用16進制表示為(0x80000000),例入一個IMAGE_THUNK_DATA的AddressOfData的值為0x 800010E4在mfc90u.dll數組中.就表示IMAGE_THUNK_DATA將引入mfc90u.dll
中的第10E4號函數
INT數組:
IAT數組:
b.如果一個函數以名稱導入.IMAGE_THUNK_DATA結構中的Ordinal域就包含一個RVA.這個RVA指向一個IMAGE_IMPORT_BY_NAME 結構.該結構保存了一個引入函數的相關信息:例如MSVCR90.dll
typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; //序列號 BYTE Name[1];//函數名稱 } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
有第四個變量Name定位dll名字
定位INT和IAT:
INT:
IAT
數組里面的值為指向IMAGE_IMPORT_BY_NAME的地址,IMAGE_IMPORT_BY_NAME里面存放的是所調用函數的名字的地址,下來我們選取一個數組里面的值,跳轉到相應的IMAGE_IMPORT_BY_NAME
可以看到,上面的那個圖片中顯示了所調用的函數的名字,名字前面的兩個字節代表的是函數在dll中的序號,方便以后快速索引到
3. 需要注意的地方
INT 和IAT數組在一開始的時候,里面存放的地址都是一樣的,他們都是指向所調用函數的名字的字符串。而在加載到內存的時候,IAT的值會發生變換,它的值存放的是dll中函數實際被調用的地址,在加載到內存后,就只需要保存IAT就可以了,利用它來調用函數
今天先到這里.有時間的話在繼續...