一、简介
本篇章主要PE文件组成部分中使用的结构体;根据结构体的成员变量去了解各个字节的含义。(ps:我们依旧以”cmd.exe
“为例展开解析;)
二、DOS Header
1、结构体:IMAGE_DOS_HEADER
IMAGE_DOS_HEADER
结构体的背景是为了兼容DOS
文件,即拓展当时被人们广泛使用DOS
文件的DOS EXE
头。
下图是使用UE使用Hex Editor
打开的cmd.exe
文件中DOS Header
的内容。以及大小为0x40
个字节的结构体信息。
typedef struct _IMAGE_DOS_HEADER { // DOS的.EXE头部USHORT e_magic; // 魔术数字(0x5A4D "MZ")****************USHORT e_cblp; // 文件最后页的字节数USHORT e_cp; // 文件页数USHORT e_crlc; // 重定义元素个数USHORT e_cparhdr; // 头部尺寸,以区块落为单位USHORT e_minalloc; // 所需的最小附加区块USHORT e_maxalloc; // 所需的最大附加区块USHORT e_ss; // 初始的SS值(相对偏移量)USHORT e_sp; // 初始的SP值USHORT e_csum; // 校验和USHORT e_ip; // 初始的IP值USHORT e_cs; // 初始的CS值(相对偏移量)USHORT e_lfarlc; // 重分配表文件地址USHORT e_ovno; // 覆盖号USHORT e_res[4]; // 保留字USHORT e_oemid; // OEM标识符(相对e_oeminfo)USHORT e_oeminfo; // OEM信息USHORT e_res2[10]; // 保留字LONG e_lfanew; // 新NT头的地址,************
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
1.1 e_magic:
e_magic
表示DOS
签名,所有未被加密的PE
文件开始部分都有DOS
签名(“MZ
”:PE文
件设计者Mark Zbikowski
),
1.2 e_lfanew:
e_lfanew
表示Name Table Heard
的偏移地址;占用四个字节内存;
三、DOS Stub
DOS
存根(Stub
)位于DOS Header
下方。可有可无,且大小不固定;由代码和数据混合而成。下图是使用UE
使用Hex Editor
打开的cmd.exe
文件中DOS Stub
的内容。
以下位对应汇编指令内容。PE
文件带有MS-DOS
兼容模式,可以在Dos
环境下运行,输出“This program cannot be run in DOS mode
”后终止。(在DOS
环境中运行16位DOS代码
,在Winsows环境中运行32位Windows代码
)。
四、NameTable Header
1、结构体:IMAGE_NT_HEADERS32
IMAGE_NT_HEADERS32
头主要由3
个成员组成,下图是使用UE
使用Hex
Editor
打开的cmd.exe
文件中NT Header
的内容。以及大小为0xF8
个字节的结构体信息。
typedef struct _IMAGE_NT_HEADERS {DWORD Signature; //签名IMAGE_FILE_HEADER FileHeader; //文件头结构体IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选头结构体
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
1.1、Signature
占用四个字节,其值位
0x50450000 (“PE..”).
1.2、结构体:IMAGE_FILE_HEADER
IMAGE_FILE_HEADER
结构体、文件内容、较为重要的结构体部分成员如下所示:
typedef struct _IMAGE_FILE_HEADER {USHORT Machine; //运行平台USHORT NumberOfSections; //文件的区块数ULONG TimeDateStamp; //文件创建日期和时间ULONG PointerToSymbolTable; //指向符号表(用于调试)ULONG NumberOfSymbols; //符号表中符号的个数(用于调试)USHORT SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32结构的大小USHORT Characteristics; //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
1.2.1 Machine
Machine
是CUP
芯片对应的专属ID
;以下是定义在winnt.h
文件中的Machine
码; 当前cmd.exe
对应的Machine = 0x8664
;
#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
...
Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
1.2.2 NumberOfSections
NumberOfSections
表示当前文件中存在的节区数量,当前值必须大于零且与实际节区数一样,否则程序运行错误。
1.2.3 NumberOfOptionalHeader
NumberOfOptionalHeader
表示IMAGE_OPTIONAL_HEADER32
结构体的大小。
因为PE32+
格式使用的是IMAGE_OPTIONAL_HEADER64
结构体,所以PE文件需要区分结构体大小;
1.2.4 Characteristics
Characteristics
表示文件属性。比如文件是否可运行,是否是exe
文件等; 一下是winnt.h
文件中的Characteristics
值表示的属性;当前文件的characteristics = 0x0022
; 即兼容两种属性IMAGE_FILE_EXECUTABLE_IMAGE
和IMAGE_FILE_LARGE_ADDRESS_AWARE
; (注意:PE文件也存在不可执行的属性,类似".obj
"文件)
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references).
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#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_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
1.3 节构体:IMAGE_OPTIONAL_HEADER32
IMAGE_OPTIONAL_HEADER32
是PE头中占用空间最大的结构体。
typedef struct _IMAGE_OPTIONAL_HEADER {// Standard fields.WORD Magic; //标志字BYTE MajorLinkerVersion; //链接器主版本号BYTE MinorLinkerVersion; //链接器次版本号DWORD SizeOfCode; //所有含有代码的区块的大小DWORD SizeOfInitializedData; //所有初始化数据区块的大小DWORD SizeOfUninitializedData; //所有未初始化数据区块的大小DWORD AddressOfEntryPoint; //程序执行人口 RVADWORD BaseOfCode; //代码区块起始RVADWORD BaseOfData; //数据区块起始RVA// NT additional fields.DWORD ImageBase; //程序默认载人基地址DWORD SectionAlignment; //内存中区块的对齐值DWORD FileAlignment; //文件中区块的对齐值WORD MajorOperatingSystemVersion; //操作系统主版本号WORD MinorOperatingSystemVersion; //操作系统次版本号WORD MajorImageVersion; //用户自定义主版本号WORD MinorImageVersion; //用户自定义次版本号WORD MajorSubsystemVersion; //所需子系统主版本号WORD MinorSubsystemVersion; //所需子系统次版本号DWORD Win32VersionValue; //保留,通常被设置为0DWORD SizeOfImage; //映像载入内存后的总大小DWORD SizeOfHeaders; //MS-DOS头部、PE文件头、区块表总大小DWORD CheckSum; //映像校验和WORD Subsystem; //文件子系统WORD DllCharacteristics; //显示 DLL特性的旗标DWORD SizeOfStackReserve; //初始化时栈的大小DWORD SizeOfStackCommit; //初始化时实际提交栈的大小DWORD SizeOfHeapReserve; //初始化时保留堆的大小DWORD SizeOfHeapCommit; //初始化时实际保留堆的大小DWORD LoaderFlags; //与调试相关,默认值为0DWORD NumberOfRvaAndSizes; //数据目录表的项数IMAGE_DATA_DIRECTORY DataDirectory[16]; //数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
1.3.1 Magic
当
Magic = 0x010B
时,表示使用结构体IMAGE_OPTIONAL_HEADER32
当Magic = 0x020B
时,表示使用结构体IMAGE_OPTIONAL_HEADER64
1.3.2 AddressOfEntryPoint
AddressOfEntryPoint
表示PE程序的入口点,是最先执行程序代码的地方,所以它的值表示程序执行代码的起始地址;其中地址值使用RVA(Relative
Virtual Address)值表示;
1.3.3 ImageBase
ImageBase
表示基址;是PE文件被加载到内存时优先装入的地址;可通过编译器设置;
其中DLL
文件的ImageBase
默认值是0x00100000
;
其中exe
文件的ImageBase
默认值是0x00400000
;
执行PE
文件是,PE
装载其先创建进程(进行内存范围0~0xFFFFFFFF
),再将文件载入内存,最后把EIP寄存器的值设置位`ImageBase
- AddressOfEntryPoint`;
1.3.4 SectionAlignment
SectionAlignment
表示节区在磁盘文件中的最小单位;不对齐则用null填充;
1.3.5 FileAlignment
FileAlignment
表示节区在内存中的最小单位;不对齐则用null填充;
1.3.6 SizeOfImage
SizeOfImage
表示加载PE文件到内存时,PE Image在虚拟内存中所占空间的大小;
1.3.7 SizeOfHeader
SizeOfHeader
表示整个PE头的占用空间的大小,当前文件中SizeOfHeader = 0x400
;
第一节区的起始位置在PE头位置之后;
1.3.8 Subsystem
Subsystem
表示文件类型,也可称为子系统;如下图表示值对应含义。
1.3.8 NumberOfRvaAndSizes
NumberOfRvaAndSizes表示成员变量DataDirectory的个数(数组大小不一定是16个);即数据目录表的实际数量;
1.3.9 DataDirectory
DataDirectory
表示IMAGE_DATA_DIRECTORY
的结构体类型;数组表示每张表对应的虚拟地址和大小;结构体数组指定下标含义如下所示:
typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress;DWORD Size;
}IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
DataDirectory[0] = "EXPORT Directory"; //函数导出表DataDirectory[1] = "IMPORT Directory"; //函数导入表DataDirectory[2] = "RESOURCE Directory";DataDirectory[3] = "EXCEPTION Directory";DataDirectory[4] = "SECURITY Di rectory";DataDirectory[5] = "BASERELOC Directory";DataDirectory[6] = "DEBUG Directory";DataDirectory[7] = "COPYRIGHT Directory";DataDirectory[8] = "GLOBALPTR Directory";DataDirectory[9] = "TLS Directory"; //线程局部存储器;用于反调试DataDirectory[10] = "LOAD CONFIG Directory";DataDirectory[11] = "BOUND IMPORT Directory";DataDirectory[12] = "IAT Directory"; //导入地址表,用于设置钩子DataDirectory[13] = "DELAY IMPORT Directory";DataDirectory[14] = "COM DESCRIPTOR Directory";DataDirectory[15] = "Reserved Directory";
五、Section Header
1、节构体:IMAGE_SECTION_HEADER
PE
文件中包含如code
(代码)、data
(数据)、resource
(资源)等节区。节区头定义了PE
文件中各个节区的属性。并同故宫为每个截取设置不同特性,访问权限等,可以提高程序的安全性。
节区头是由IMAGE_SECTION_HEADER
结构体组成的数组,每个结构体对应一个节区。
typedef struct _IMAGE_SECTION_HEADER {BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//8字节的块名区块大小union {DWORD PhysicalAddress;DWORD VirtualSize; //内存中节区所占大小} Misc;DWORD VirtualAddress; //区块的RVA地址DWORD SizeOfRawData; //在文件中对齐后的大小DWORD PointerToRawData; //在文件中的偏移,即节区起始位置DWORD PointerToRelocations; //在 OBJ文件中使用,重定位的偏移DWORD PointerToLinenumbers; //行号表的偏移(供调试用)WORD NumberOfRelocations; //在OBJ文件中使用,重定位项数目WORD NumberOfLinenumbers; //行号表中行号的数目DWORD Characteristics; //节区的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
1.1 Name
Name
表示节区名称,但是不一定用NULL结束;
1.2 VirtualAddress
VirtualAddress
表示虚拟地址,是当前节区在内存的起始地址;
1.3 SizeOfRawData
SizeOfRawData
表示文件偏移,是当前节区在磁盘文件中的偏移
1.4 Characteristics
Characteristics
在头文件“winnt.h
”的定义如下:
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.contents comdat.
#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL 0x00008000 // Section content can be accessed relative to GP
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others are specified.
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
六、小结
文本章详细讲解了PE文件头的信息。包括Dos Header
,Dos Stub
,NT
Header
,Section Header
等四个主要部分,并列举了其中包含的结构体信息。通过注释,举例的方式快速帮助我们了解PE文件最初的样子。时而观之,温故知新呀。