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;
其中节属性如下表:
在节表的后面就是节的具体数据所在了..常用的节分析,将在后面的文章中给出