主要结构体
注意
PE文件静态(不运行)和动态(运行)是有差异的。
结构体大小
结构体 | 大小(十进制) |
---|---|
IMAGE_DOS_HEADER | 64字节 |
DOS STUB | 注意这个不是结构体,只是为了方便写在这里 不确定(链接器插入的数据,可以修改、删除,不影响程序运行) |
IMAGE_FILE_HEADER | 20字节 |
IMAGE_OPTIONAL_HEADER32 | 224字节 |
IMAGE_SECTION_HEADER | 每个结构体的成员是40个字节 |
IMAGE_DOS_HEADER
文件的前64个字节,就是 IMAGE_DOS_HEADER (DOS MZ头)。
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
DOS STUB
- IMAGE_DOS_HEADER (DOS MZ头) 的最后一个成员 e_lfaew,它指向了PE头从哪里开始。
- 从IMAGE_DOS_HEADER 最后一个成员开始,直到PE头之前的就是 DOS STUB(DOS块)。
- DOS STUB(DOS块) 中的内容是链接器增加的,可以修改可以删除,不会影响程序运行。
PE头
整个PE头的结构:PE文件头标志 + IMAGE_FILE_HEADER + IMAGE_OPTIONAL_HEADER32。
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //标志
IMAGE_FILE_HEADER FileHeader; //标准PE头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //扩展PE头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
PE头标志
PE头标志的大小是4个字节。
IMAGE_FILE_HEADER (标准PE头)
标准PE头的大小是20个字节,也就是从PE头标志往后数20个字节。
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader; //扩展PE头的大小,想改变扩展PE头的大小修改它即可
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
IMAGE_OPTIONAL_HEADER32 (扩展PE头)
扩展PE头的大小是224个字节(这里是32位的程序,64位的程序扩展PE头大小和32位的不一样)。
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;
- 结构体看着就这点东西为什么这么大?因为该结构体的最后一个成员是一个有着16个元素的结构体数组。
- 扩展PE头是可扩展的,可以往里面加想加的东西。
- 在标准PE头中有一个成员 SizeOfOptionalHeader,它是用来标识扩展PE头的大小的。
- 如果觉得扩展PE头不够用想自己往里面加东西,那么就修改标准PE头重的成员 SizeOfOptionalHeader,改变它的值即可。
- 32位程序的标准PE头的 SizeOfOptionalHeader 成员在默认情况下的大小是0xE0(224),如果是64位程序,默认大小是0xF0(240)。
n x IMAGE_SECTION_HEADER (节表)
每个结构体的成员的大小是40个字节。
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;
扩展PE头(IMAGE_OPTIONAL_HEADER32)中的成员 SizeOfHeaders 是头的大小,这个头是DOS头+PE头+节表,按照文件对齐以后的值。
- 如果DOS头+PE头+节表是302个字节,那么 SizeOfHeaders 中存储的一定不会是302。
- 扩展PE头(IMAGE_OPTIONAL_HEADER32)中的另一个成员 FileAlignment,指的是文件对齐。
- 如果 FileAlignment 的值是0x200,那么 SizeOfHeaders 的值一定是0x400,也就是说 SizeOfHeaders 的值一定是 FileAlignment 的整数倍。
- 还是上面的例子,如果DOS头+PE头+节表是302个字节,FileAlignment(文件对齐)的值是0x200,那么 SizeOfHeaders 的值一定是0x400。
- 如果0x400放不下,但是不超过0x600,那么 SizeOfHeaders 就是0x600,如果0x600放不下,但是不超过0x800,那么 SizeOfHeaders 就是0x800。
例如:
如果DOS头+PE头+节表的大小是0x306,那么 SizeOfHeaders 的值是0x400。
如果DOS头+PE头+节表的大小不够0x400那剩余的字节是可以用0x00填充的(可能会被编译器添加常量字符串)。
总而言之,剩下的空白可以拿来用,想填什么就填什么(再强调一遍,可能会被编译器添加常量字符串,或许是其他信息)。
节
节的大小也一定会是文件对齐(FileAlignment)的整数倍,也就是说如果有空白,也都会使用0x00来填充。
PE文件的两种状态
PE文件在硬盘和在内存中拉伸后的区别:
- 文件起始地址不同。
- 节起始地址不同,由内存对齐(SectionAlignment)成员属性决定PE文件运行时在内存中的节起始地址。
- 成员内存对齐的大小和文件对齐的大小可能一样,也可能不一样。
- 差异在于每个头和节,或每个节和节的空白区不一样。