1 byte = 8 bit
WORD: 2 byte (16 bit)
DWORD: 4 byte (32 bit)
VA (Virtual Address): 进程虚拟内存的绝对地址
RVA (Relative Virtual Address): 进程虚拟内存位置相对于某个基准位置 (ImageBase) 的相对地址
RVA + ImageBase = VA
在下面介绍的PE头中,所有字段均没有用到VA,涉及地址的地方全都用RVA。对RVA的使用体现了Portable的理念
DOS头 (40h bytes, i.e. 6410 bytes, "MZ" - 40h)
struct _IMAGE_DOS_HEADER{
0X00 WORD e_magic; //※Magic DOS signature MZ(4Dh 5Ah):MZ标记:用于标记是否是可执行文件
0X02 WORD e_cblp; //Bytes on last page of file
0X04 WORD e_cp; //Pages in file
0X06 WORD e_crlc; //Relocations
0X08 WORD e_cparhdr; //Size of header in paragraphs
0X0A WORD e_minalloc; //Minimun extra paragraphs needs
0X0C WORD e_maxalloc; //Maximun extra paragraphs needs
0X0E WORD e_ss; //intial(relative)SS value
0X10 WORD e_sp; //intial SP value
0X12 WORD e_csum; //Checksum
0X14 WORD e_ip; //intial IP value
0X16 WORD e_cs; //intial(relative)CS value
0X18 WORD e_lfarlc; //File Address of relocation table
0X1A WORD e_ovno; //Overlay number
0x1C WORD e_res[4]; //Reserved words
0x24 WORD e_oemid; //OEM identifier(for e_oeminfo)
0x26 WORD e_oeminfo; //OEM information;e_oemid specific
0x28 WORD e_res2[10]; //Reserved words
0x3C DWORD e_lfanew; //※Offset to start of PE header:定位PE文件,PE头相对于文件的偏移量
};
-
WORD e_magic
: DOS签名(signature, 4D5A,即"MZ") -
WORD e_lfanew
: 指示NT头的偏移
DOS存根 (40h - "PE\0\0")
NT头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; // PE Signature: 50450000 ("PE"00)
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADER32, *PIMAGE_NT_HEADER32;
NT头:文件头 (File Header) (2010 bytes)
struct _IMAGE_FILE_HEADER{
0x00 WORD Machine; //※程序执行的CPU平台:0X0:任何平台,0X14C:intel i386及后续处理器
0x02 WORD NumberOfSections; //※PE文件中区块数量
0x04 DWORD TimeDateStamp; //时间戳:连接器产生此文件的时间距1969/12/31-16:00P:00的总秒数
0x08 DWORD PointerToSymbolTable; //COFF符号表格的偏移位置。此字段只对COFF除错信息有用
0x0c DWORD NumberOfSymbols; //COFF符号表格中的符号个数。该值和上一个值在release版本的程序里为0
0x10 WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER结构的大小(字节数):32位默认E0H,64位默认F0H(可修改)
0x12 WORD Characteristics; //※描述文件属性,eg:
//单属性(只有1bit为1):#define IMAGE_FILE_DLL 0x2000 //File is a DLL.
//组合属性(多个bit为1,单属性或运算):0X010F 可执行文件
};
-
WORD Machine
:标识CPU类型的Machine码,Intel x86为14C -
WORD NumberOfSections
:节区 (Section) 数量2 -
WORD SizeOfOptionalHeader
:指示IMAGE_OPTIONAL_HEADER32
的大小 -
WORD Characteristics
:标识文件的属性和运行信息,记住,0002h为可执行文件,2000h为DLL文件
NT头:可选头 (Optional Header) (32位默认E0h bytes,64位默认F0h bytes)
struct _IMAGE_OPTIONAL_HEADER{
0x00 WORD Magic; //※幻数(魔数),0x0107:ROM image,0x010B:32位PE,0X020B:64位PE
0x02 BYTE MajorLinkerVersion; //连接器主版本号
0x03 BYTE MinorLinkerVersion; //连接器副版本号
0x04 DWORD SizeOfCode; //所有代码段的总和大小,注意:必须是FileAlignment的整数倍,存在但没用
0x08 DWORD SizeOfInitializedData; //已经初始化数据的大小,注意:必须是FileAlignment的整数倍,存在但没用
0x0c DWORD SizeOfUninitializedData; //未经初始化数据的大小,注意:必须是FileAlignment的整数倍,存在但没用
0x10 DWORD AddressOfEntryPoint; //※程序入口地址OEP,这是一个RVA(Relative Virtual Address),通常会落在.textsection,此字段对于DLLs/EXEs都适用。
0x14 DWORD BaseOfCode; //代码段起始地址(代码基址),(代码的开始和程序无必然联系)
0x18 DWORD BaseOfData; //数据段起始地址(数据基址)
0x1c DWORD ImageBase; //※内存镜像基址(默认装入起始地址),默认为4000H
0x20 DWORD SectionAlignment; //※内存对齐:一旦映像到内存中,每一个section保证从一个「此值之倍数」的虚拟地址开始
0x24 DWORD FileAlignment; //※文件对齐:最初是200H,现在是1000H
0x28 WORD MajorOperatingSystemVersion; //所需操作系统主版本号
0x2a WORD MinorOperatingSystemVersion; //所需操作系统副版本号
0x2c WORD MajorImageVersion; //自定义主版本号,使用连接器的参数设置,eg:LINK /VERSION:2.0 myobj.obj
0x2e WORD MinorImageVersion; //自定义副版本号,使用连接器的参数设置
0x30 WORD MajorSubsystemVersion; //所需子系统主版本号,典型数值4.0(Windows 4.0/即Windows 95)
0x32 WORD MinorSubsystemVersion; //所需子系统副版本号
0x34 DWORD Win32VersionValue; //总是0
0x38 DWORD SizeOfImage; //※PE文件在内存中映像总大小,sizeof(ImageBuffer),SectionAlignment的倍数
0x3c DWORD SizeOfHeaders; //※DOS头(64B)+PE标记(4B)+标准PE头(20B)+可选PE头+节表的总大小,按照文件对齐(FileAlignment的倍数)
0x40 DWORD CheckSum; //PE文件CRC校验和,判断文件是否被修改
0x44 WORD Subsystem; //用户界面使用的子系统类型
0x46 WORD DllCharacteristics; //总是0
0x48 DWORD SizeOfStackReserve; //默认线程初始化栈的保留大小
0x4c DWORD SizeOfStackCommit; //初始化时实际提交的线程栈大小
0x50 DWORD SizeOfHeapReserve; //默认保留给初始化的process heap的虚拟内存大小
0x54 DWORD SizeOfHeapCommit; //初始化时实际提交的process heap大小
0x58 DWORD LoaderFlags; //总是0
0x5c DWORD NumberOfRvaAndSizes; //目录项数目:总为0X00000010H(16)
0x60 _IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
};
-
WORD Magic
:为IMAGE_OPTIONAL_HEADER32
时,Magic
为10B;为IMAGE_OPTIONAL_HEADER64
时,为20B -
DWORD AddressOfEntryPoint
:持有Entry Point的RVA值,指示程序最先执行的代码起始地址 DWORD ImageBase
-
DWORD SectionAlignment, FileAlignment
:FileAlignment
指定了节区在磁盘文件中的最小单位,而SectionAlignment
指定了节区在内存中的最小单位 -
DWORD SizeOfImage
:指定PE文件加载到内存后,PE Image在虚拟内存中所占空间大小(一般来讲,文件的大小和加载到内存中的大小是不同的) -
DWORD SizeOfHeaders
:指出整个PE头的大小 -
WORD Subsystem
:用于区分驱动文件和普通的可执行文件(1表示系统驱动,2表示GUI文件,在窗口打开,3表示CUI文件,在控制台执行) -
DWORD NumberOfRvaAndSizes
:指定DataDirectory数组的个数
节区头
-
DWORD VirtualSize
:内存中节区所占大小 -
DWORD VirtualAddress
:内存中节区起始地址 (RVA) -
DWORD SizeOfRawData
:磁盘文件中节区所占大小 -
DWORD PointerToRawData
:磁盘文件中节区起始位置 -
DWORD Characteristics
:节区属性
Image:映像。PE文件加载到内存时,文件不会原封不动地加载,而要根据节区头中定义的节区起始地址、节区大小等加载。因此,磁盘文件中的PE与内存中的PE具有不同形态。将装载到内存中的形态成为“映像”加以区别,使用这一术语能够很好地区分二者。
加载PE文件时,每个节区都要完成RVA到RAW的映射: 根据
RAW - Pointer To Raw Data = RVA - Virtual Address
得:
RAW = RVA - Virtual Address + Pointer To Raw Data
Reference