0x0 基础知识
PE文件头的结构有两种,分别对应32位的程序和64位的程序,它们的差异在于扩展PE头的结构
PE文件头结构 | 说明 |
_IMAGE_NT_HEADERS | 32位程序对应的PE文件头结构 |
_IMAGE_NT_HEADERS64 | 64位程序对应的PE文件头结构 |
_IMAGE_NT_HEADERS | 对应C中的结构体(类型) | 说明 |
“PE”,0,0 | DOWRD | PE标识 |
IMAGE_FILE_HEADER | IMAGE_FILE_HEADER | 标准PE头 |
IMAGE_OPTIONAL_HEADER32 | IMAGE_OPTIONAL_HEADER32 | 扩展PE头 32位 |
_IMAGE_NT_HEADERS64 | 对应C中的结构体(类型) | 说明 |
“PE”,0,0 | DOWRD | PE标识,固定值不可变 |
IMAGE_FILE_HEADER | IMAGE_FILE_HEADER | 标准PE头 |
IMAGE_OPTIONAL_HEADER64 | IMAGE_OPTIONAL_HEADER64 | 扩展PE头 64位 |
顺着 DOS 头中的
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
下图是一张真实的 PE文件头结构 以及其 各个域的取值 :
之前我们简单了解过区块是要对齐的,无论是在内存中存放还是在磁盘中存放但他们一般的对齐值是不同的。PE文件头里边的FileAligment定义了磁盘区块的对齐值。每一个区块从对齐值的倍数的偏移位置开始有放。而区块的实际代码或数据的大小不一定刚好是这么多,所以在多余的地方一般以00来填充,这就区块间的间隙。
例如,在PE文件中,一个典型的对齐值是200h,这样,每个区块都将从200h的倍数的文件偏移位置开始,假设第一个区块在400h处,长度为90h,那么从文件400h到490h为这一区块的内容,而由于文件的对齐值是200h,所以为了使这一区块的长度为FileAlignment的整数倍,490h到600h这一个区间制会被00h填充,这段空间称为区块间隙,下一个区块的开始地址为600h。
PE文件头里边的SectionAligment定义了内存中区块钓对齐值。PE文件被映射到内存中时,区块总是至少从一个页边界开始。
一般在X86系列的CPU中,页是按4KB(1000h)来排列的;在IA-64上,是按8KB(2000h)来排列的。所以在X86系统中,PE文件区块的内存对齐值一般等于1000h, 每个区块按1000h的倍数在内存中存放。
字节序,又称端序或尾序(英语中用单词:Endianness 表示),在计算机领域中,指电脑内存中或在数字通信链路中,占用多个字节的数据的字节排列顺序。
在几乎所有的平台上,多字节对象都被存储为连续的字节序列。例如在 Go 语言中,一个类型为int的变量x地址为0x100,那么其指针&x的值为0x100。且x的四个字节将被存储在内存的0x100, 0x101, 0x102, 0x103位置。
字节的排列方式有两个通用规则:
00 Signature ( 签名 )
类似于 DOS头中的 e_magic,其高16位是0,低16是0x4550,用字符表示是 ‘PE‘ 。
01 PE 头 之 标准PE头
IMAGE_FILE_HEADER 结构体定义
// 20 Bytes
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;
IMAGE_FILE_HEADER 成员详情
成员 | 数据宽度 | 说明 | 值 |
Machine | WORD(2字节) | 程序支持的CPU | 任意:0 Intel 386以及后续:14C x64:8664 |
NumberOfSections | WORD(2字节) | 节的数量 | 不大于96 |
TimeDateStamp | DWORD(4字节) | 编译器填写的时间戳 | 与文件属性里面(创建时间、修改时间)无关 |
PointerToSymbolTable | DWORD(4字节) | 指向符号表 | 调试相关 |
NumberOfSymbols | DWORD(4字节) | 符号表中的符号个数 | 调试相关 |
SizeOfOptionalHeader | WORD(2字节) | 可选PE头结构大小 | 32位PE文件:0xE0 64位PE文件:0xF0 |
Characteristics | WORD(2字节) | 文件属性 | 由数据位拼接而成,详见下方 |
IMAGE_FILE_HEADER 每个域的具体含义
- Machine:该文件的运行平台,是 x86、x64 还是 I64 等等,可以是下面值里的某一个。
#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
- NumberOfSections: 该PE文件中有多少个节,也就是节表中的项数。
- TimeDateStamp: PE文件的创建时间,一般有连接器填写。
- PointerToSymbolTable: COFF文件符号表在文件中的偏移。
- NumberOfSymbols: 符号表的数量。
- SizeOfOptionalHeader: 紧随其后的可选头的大小。
- Characteristics: 可执行文件的属性,可以是下面这些值按位相或。
#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.
Characteristics的数据宽度为WORD(2字节=16 bits)
假设Characteristics的十六进制为0102,分析其文件属性,首先将十六进制转化为二进制:0000 0001 0000 0010 此时可以发现数据位1和8的位置的值为1(数据位由0开始),对照上面可得出:文件属性为 文件是可执行的、只在32位平台上运行
02 PE 头 之 拓展PE头
- 另一个重要的头就是 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;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
PE 可选头 各个字段分析说明
_IMAGE_OPTIONAL_HEADER 成员详情
成员 | 数据宽度 | 说明 |
Magic | WORD(2字节) | 镜像文件的状态,可用于判断程序是32位还是64位 |
MajorLinkerVersion | BYTE(字节) | 链接器的主要版本号 |
MinorLinkerVersion | BYTE(字节) | 链接器的次要版本号 |
SizeOfCode | DWORD(4字节) | 代码段的大小 |
SizeOfInitializedData | DWORD(4字节) | 初始化数据段的大小 |
SizeOfUninitializedData | DWORD(4字节) | 未初始化数据段的大小 |
AddressOfEntryPoint | DWORD(4字节) | 程序入口 |
BaseOfCode | DWORD(4字节) | 代码开始的基址 |
BaseOfData | DWORD(4字节) | 数据开始的基址 |
ImageBase | DWORD(4字节) | 内存镜像基址 |
SectionAlignment | DWORD(4字节) | 内存对齐 |
FileAlignment | WORD(2字节) | 文件对齐 |
MajorOperatingSystemVersion | WORD(2字节) | 标识操作系统版本号 主版本号 |
MinorOperatingSystemVersion | WORD(2字节) | 标识操作系统版本号 次版本号 |
MajorImageVersion | WORD(2字节) | PE文件自身的版本号 |
MinorImageVersion | WORD(2字节) | PE文件自身的版本号 |
MajorSubsystemVersion | WORD(2字节) | 运行所需子系统版本号 |
MinorSubsystemVersion | WORD(2字节) | 运行所需子系统版本号 |
Win32VersionValue | DWORD(4字节) | 子系统版本的值,必须为0 |
SizeOfImage | DWORD(4字节) | Image大小 |
SizeOfHeaders | DWORD(4字节) | 所有头+节表按照文件对齐后的大小 |
CheckSum | DWORD(4字节) | 校验和 |
Subsystem | WORD(2字节) | 子系统 |
DllCharacteristics | WORD(2字节) | 文件特性 不只是针对DLL文件的 |
SizeOfStackReserve | DWORD(4字节) | 初始化时保留的栈大小 |
SizeOfStackCommit | DWORD(4字节) | 初始化时实际提交的大小 |
SizeOfHeapReserve | DWORD(4字节) | 初始化时保留的堆大小 |
SizeOfHeapCommit | DWORD(4字节) | 初始化时实践提交的大小 |
LoaderFlags | DWORD(4字节) | 调试相关 |
NumberOfRvaAndSizes | DWORD(4字节) | 目录项数目 |
DataDirectory[16] | IMAGE_DATA_DIRECTORY[16]=128字节 | 指向数据目录中第一个IMAGE_DATA_DIRECTORY结构的指针(数据目录项) |
_IMAGE_OPTIONAL_HEADER 每个域的具体含义
- Magic:表示可选头的类型。
#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
- MajorLinkerVersion 和 MinorLinkerVersion:链接器的版本号。
- SizeOfCode:代码段的长度,如果有多个代码段,则是代码段长度的总和。
- SizeOfInitializedData:初始化的数据长度。
- SizeOfUninitializedData:未初始化的数据长度。
- AddressOfEntryPoint:程序入口的 RVA,对于exe这个地址可以理解为WinMain的RVA。对于DLL,这个地址可以理解为DllMain的RVA,如果是驱动程序,可以理解为DriverEntry的RVA。当然,实际上入口点并非是WinMain,DllMain和DriverEntry,在这些函数之前还有一系列初始化要完成,当然,这些不是本文的重点。
- BaseOfCode:代码段起始地址的RVA。
- BaseOfData:数据段起始地址的RVA。
- ImageBase:映象(加载到内存中的PE文件)的基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址。
- SectionAlignment:内存对齐,节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0。
- FileAlignment:文件对齐,节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignment。
- MajorOperatingSystemVersion、MinorOperatingSystemVersion:所需操作系统的版本号,随着操作系统版本越来越多,这个好像不是那么重要了。
- MajorImageVersion、MinorImageVersion:映象的版本号,这个是开发者自己指定的,由连接器填写。
- MajorSubsystemVersion、MinorSubsystemVersion:所需子系统版本号。
- Win32VersionValue:保留,必须为0。
- SizeOfImage:映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。
- SizeOfHeaders:所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。
- CheckSum:映象文件的校验和。
- Subsystem:运行该PE文件所需的子系统,可以是下面定义中的某一个:
#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
DllCharacteristics:DLL的文件属性,只对DLL文件有效,可以是下面定义中某些的组合:
#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
- SizeOfStackReserve:运行时为每个线程栈保留内存的大小。
- SizeOfStackCommit:运行时每个线程栈初始占用内存大小。
- SizeOfHeapReserve:运行时为进程堆保留内存大小。
- SizeOfHeapCommit:运行时进程堆初始占用内存大小。
- LoaderFlags:保留,必须为0。
- NumberOfRvaAndSizes:数据目录的项数,即下面这个数组的项数。
- DataDirectory:数据目录,这是一个数组,数组的项定义如下:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // VirtualAddress:是一个RVA。
DWORD Size; // Size:是一个大小。
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#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
暂无评论内容