① Optional header Optional header
从字面上看,这个文件头是可选的,但实际上它是PE文件中必不可少的。它在winnt.h中被定义称为:
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;
其中:
Magic:声明PE文件的状态,如果是普通的PE文件,值为0x10B;如果是只读的,值为0x107。
MajorLinkerVersion、MinorLinkerVersion:链接器的版本号,但实际上这两个值并不可靠,某些链接器不会设置这两个值。
SizeOfCode:所有代码节的大小。
SizeOfInitializedData、SizeOfUninitializedData:所有已初始化数据节、未初始化数据节的大小。
AddressOfEntryPoint:这是一个RVA。当PE文件被装载到内存中以后,第一条可执行指令的地址。对于设备驱动,这是初始化函数的地址;对于DLL,这个入口点是可选的,如果DLL没有入口点,则这个值必须为零。
BaseOfCode、BaseOfData:代码的RVA,已初始化数据的RVA。
ImageBase:PE文件的优先装载地址。比如,这个值是0x10000000,则装载器会将PE文件优先装载到虚拟内存地址0x10000000中。通常.EXE文件的这个值是0x00400000;.DLL文件的是0x10000000。
SectionAlignment:PE文件装载到内存后,节的对齐粒度,必须大于等于FileAlignment。WIN32下,一般是0x1000,WIN64下,一般是0x2000。
FileAlignment:PE文件中,节的对齐粒度。一般情况下是0x200的倍数。
MajorOperatingSystemVersion、MinorOperatingSystemVersion:期望的操作系统版本。
MajorImageVersion、MinorImageVersion:期望的PE文件版本,某些链接器不设定这个值。
MajorSubsystemVersion、MinorSubsystemVersion:期望的子系统版本。
Win32VersionValue:这个是保留的,必须是0。
SizeOfImage:PE文件装载到内存后,整个镜像的大小,必须是SectionAlignment的整数倍。
SizeOfHeaders:MS-DOS头、MS DOS 2.0 Stub Program、Magic Number、PE Header和Optional header大小之和,必须是FileAlignment的整数倍。
CheckSum:对于普通的PE文件,这个值是0。
Subsystem:声明PE文件在什么样的系统上运行,可以是下表中的值:(对于WINDOWS开发,通常选择第三项或者第四项)
Constant |
Value |
Description |
IMAGE_SUBSYSTEM_UNKNOWN |
0 |
未知子系统。 |
IMAGE_SUBSYSTEM_NATIVE |
1 |
设备驱动以及WINDOWS内部程序。 |
IMAGE_SUBSYSTEM_WINDOWS_GUI |
2 |
WINDOWS GUI 程序。 |
IMAGE_SUBSYSTEM_WINDOWS_CUI |
3 |
WINDOWS控制台程序。 |
IMAGE_SUBSYSTEM_POSIX_CUI |
7 |
Posix字符子系统程序。 |
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI |
9 |
Windows CE。 |
IMAGE_SUBSYSTEM_EFI_APPLICATION |
10 |
可扩展固件程序。 |
IMAGE_SUBSYSTEM_EFI_BOOT_ SERVICE_DRIVER |
11 |
启动服务的EFI驱动。 |
IMAGE_SUBSYSTEM_EFI_RUNTIME_ DRIVER |
12 |
运行时的EFI驱动。 |
IMAGE_SUBSYSTEM_EFI_ROM |
13 |
EFI只读镜像。 |
IMAGE_SUBSYSTEM_XBOX |
14 |
XBOX。 |
DllCharacteristics:声明DLL文件的性质,可以是下表中的值:(如果没有特别需要,这个值是零)
常量 |
值 |
描述 |
|
0x0001 |
保留的,必须为零。 |
|
0x0002 |
保留的,必须为零。 |
|
0x0004 |
保留的,必须为零。 |
|
0x0008 |
保留的,必须为零。 |
IMAGE_DLL_CHARACTERISTICS_ DYNAMIC_BASE |
0x0040 |
DLL可以在运行时被重置。 |
IMAGE_DLL_CHARACTERISTICS_ FORCE_INTEGRITY |
0x0080 |
强制进行代码完整性检查。 |
IMAGE_DLL_CHARACTERISTICS_ NX_COMPAT |
0x0100 |
映像是NX兼容的。 |
IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION |
0x0200 |
不隔离映像文件。 |
IMAGE_DLLCHARACTERISTICS_ NO_SEH |
0x0400 |
不使用结构化异常处理。 |
IMAGE_DLLCHARACTERISTICS_ NO_BIND |
0x0800 |
不绑定映像 |
|
0x1000 |
保留的,必须为零。 |
IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER |
0x2000 |
WDM驱动。 |
IMAGE_DLLCHARACTERISTICS_ TERMINAL_SERVER_AWARE |
0x8000 |
终端服务器 |
SizeOfStackReserve:预留栈的大小,一般默认为0x10000。
SizeOfStackCommit:提交栈的大小,一般默认为0x1000。
SizeOfHeapReserve:预留堆的大小,一般默认为0x10000。
SizeOfHeapCommit:提交堆的大小,一般默认为0x1000。
LoaderFlags:保留的,必须为零。
NumberOfRvaAndSizes:之后的数据目录的数量,强烈建议使用默认的16。
DataDirectory:数据目录,每一个对应一个节,声明该节的RVA和大小。
每一个节对应一个与之相关的节头,节头声明了节的大小、RVA以及的性,在winnt.h中,节头定义如下:
#define IMAGE_SIZEOF_SHORT_NAME 8
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;
其中:
Name:一个8字节长度的变量,给出节的名字。如果名字中的字符少于8字节,则用NULL填充;如果刚好等于8字节,则不需要以NULL结束。一般情况下,代码节名称为“.text”,数据节为“.data”。
Misc:这是一个联合体,在可执行映像中,使用的是VirtualSize,声明对应的节的大小。
VirtualAddress:当PE文件读入内存后,对应节的RVA。
SizeOfRawData:磁盘上,节根据FileAlignment对齐后的大小,必须是FileAlignment的倍数。
PointerToRawData:从文件开头到对应节的偏移量。
PointerToRelocations
PointerToLinenumbers
NumberOfRelocations
NumberOfLinenumbers:以上四个变量在可执行文件中用不到。
Characteristics:声明节的性质:可以是下表中的值,并可以按位或:
标志 |
值 |
描述 |
IMAGE_SCN_CNT_CODE |
0x00000020 |
节包含可执行代码。 |
IMAGE_SCN_CNT_INITIALIZED_DATA |
0x00000040 |
节包含已经初始化的数据。 |
IMAGE_SCN_CNT_UNINITIALIZED_ DATA |
0x00000080 |
节包含未初始化的数据。 |
IMAGE_SCN_LNK_INFO |
0x00000200 |
节包含注释或其他信息。只用于目标文件。 |
IMAGE_SCN_LNK_REMOVE |
0x00000800 |
节不是映像的一部分,只用于目标文件。 |
IMAGE_SCN_LNK_COMDAT |
0x00001000 |
节包含COMDAT数据。只用于目标文件。 |
IMAGE_SCN_GPREL |
0x00008000 |
节包含引用全局指针的数据。 |
IMAGE_SCN_LNK_NRELOC_OVFL |
0x01000000 |
节包含扩展重定位。 |
IMAGE_SCN_MEM_DISCARDABLE |
0x02000000 |
根据需要,节可被废弃。 |
IMAGE_SCN_MEM_NOT_CACHED |
0x04000000 |
节不可被缓存。 |
IMAGE_SCN_MEM_NOT_PAGED |
0x08000000 |
节不可被分页。 |
IMAGE_SCN_MEM_SHARED |
0x10000000 |
节可在内存中被共享。 |
IMAGE_SCN_MEM_EXECUTE |
0x20000000 |
节可以执行。 |
IMAGE_SCN_MEM_READ |
0x40000000 |
节可被读取。 |
IMAGE_SCN_MEM_WRITE |
0x80000000 |
节可被写入。 |