① 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 | 节可被写入。 |