PE文件的全稱是Portable Executable,意爲可移植的可執行的文件,常見的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微軟Windows操作系統上的程序文件(可能是間接被執行,如DLL)
下圖是PE文件的基本結構
MZ文件頭
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
我們只需要關注兩個域:
e_magic :一個WORD類型,值是一個常數0x4D5A,用文本編輯器查看該值位‘MZ’,可執行文件必須都是’MZ’開頭。
e_lfanew:爲32位可執行文件擴展的域,用來表示DOS頭之後的NT頭相對文件起始地址的偏移。
下面是一個PE文件的DOS頭
DOS插樁小程序
這個程序實際上是在DOS環境下顯示 This program can not be run in DOS mode 或 This program must be run under Win32 之類信息的小程序
NT映像頭
緊跟着DOS小程序後面的是PE文件的NT映像頭(IMAGE_NT_HEADERS),它存放PE整個文件信息分佈的重要字段,結構定義如下
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //簽名
IMAGE_FILE_HEADER FileHeader; //映像文件頭
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可選映像頭
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
可見它由三部分組成:
(1) : Signature:類似於DOS頭中的e_magic,其高16位是0,低16是0x4550,用字符表示是’PE‘。
(2) : IMAGE_FILE_HEADER是PE文件頭,c語言的定義是這樣的:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
每個域的具體含義如下:
附1:
#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_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0
// IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200
// Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266
// MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284
// ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366
// MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
// MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520
// Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC
// EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664
// AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041
// M32R little-endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
附2:
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
// Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
// File is executable (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
// Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
// Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010
// Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
// App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
// Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100
// 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
// Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400
// If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800
// If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000
// System File.
#define IMAGE_FILE_DLL 0x2000
// File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000
// File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
// Bytes of machine word are reversed.
可以看出,PE文件頭定義了PE文件的一些基本信息和屬性,這些屬性會在PE加載器加載時用到,如果加載器發現PE文件頭中定義的一些屬性不滿足當前的運行環境,將會終止加載該PE。
(3)可選映像頭:
另一個重要的頭就是PE可選頭,別看他名字叫可選頭,其實一點都不能少,不過,它在不同的平臺下是不一樣的,例如32位下是IMAGE_OPTIONAL_HEADER32,而在64位下是IMAGE_OPTIONAL_HEADER64。爲了簡單起見,我們只看32位。
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
定義如下:
附3:
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b // 32位PE可選頭
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b // 64位PE可選頭
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107
附4:
#define IMAGE_SUBSYSTEM_UNKNOWN 0
// Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1
// Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
// Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
// Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5
// image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7
// image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8
// image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9
// Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
#define IMAGE_SUBSYSTEM_EFI_ROM 13
#define IMAGE_SUBSYSTEM_XBOX 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
附5
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
// DLL can move.
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080
// Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
// Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
// Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
// Image does not use SEH. No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
// Do not bind this image.
// 0x1000
// Reserved.
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
// Driver uses WDM model
// 0x4000
// Reserved.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
附6:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
VirtualAddress:是一個RVA。
Size:是一個大小。
這兩個數有什麼用呢?一個是地址,一個是大小,可以看出這個數據目錄項定義的是一個區域。那他定義的是什麼東西的區域呢?前面說了,DataDirectory是個數組,數組中的每一項對應一個特定的數據結構,包括導入表,導出表等等,根據不同的索引取出來的是不同的結構,頭文件裏定義各個項表示哪個結構,如下面的代碼所示:
#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
下圖是一個PE文件的數據目錄
節表
緊接着NT映像頭的是節表,節表是一個數組,每個結構包含一個節的具體信息(28H字節),節的個數由映像頭文件IMAGE_FILE_HEADER中的NumberOfSections的值決定,節表的定義如下:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
其中節屬性如下表:
在節表的後面就是節的具體數據所在了..常用的節分析,將在後面的文章中給出