【2021.01.11】PE文件的两种状态

主要结构体

注意

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

  1. IMAGE_DOS_HEADER (DOS MZ头) 的最后一个成员 e_lfaew,它指向了PE头从哪里开始。
  2. 从IMAGE_DOS_HEADER 最后一个成员开始,直到PE头之前的就是 DOS STUB(DOS块)。
  3. 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;
  1. 结构体看着就这点东西为什么这么大?因为该结构体的最后一个成员是一个有着16个元素的结构体数组。
  2. 扩展PE头是可扩展的,可以往里面加想加的东西。
  3. 在标准PE头中有一个成员 SizeOfOptionalHeader,它是用来标识扩展PE头的大小的。
  4. 如果觉得扩展PE头不够用想自己往里面加东西,那么就修改标准PE头重的成员 SizeOfOptionalHeader,改变它的值即可。
  5. 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头+节表,按照文件对齐以后的值。

  1. 如果DOS头+PE头+节表是302个字节,那么 SizeOfHeaders 中存储的一定不会是302。
  2. 扩展PE头(IMAGE_OPTIONAL_HEADER32)中的另一个成员 FileAlignment,指的是文件对齐。
  3. 如果 FileAlignment 的值是0x200,那么 SizeOfHeaders 的值一定是0x400,也就是说 SizeOfHeaders 的值一定是 FileAlignment 的整数倍。
  4. 还是上面的例子,如果DOS头+PE头+节表是302个字节,FileAlignment(文件对齐)的值是0x200,那么 SizeOfHeaders 的值一定是0x400。
  5. 如果0x400放不下,但是不超过0x600,那么 SizeOfHeaders 就是0x600,如果0x600放不下,但是不超过0x800,那么 SizeOfHeaders 就是0x800。

例如:

如果DOS头+PE头+节表的大小是0x306,那么 SizeOfHeaders 的值是0x400。

如果DOS头+PE头+节表的大小不够0x400那剩余的字节是可以用0x00填充的(可能会被编译器添加常量字符串)。

总而言之,剩下的空白可以拿来用,想填什么就填什么(再强调一遍,可能会被编译器添加常量字符串,或许是其他信息)。

节的大小也一定会是文件对齐(FileAlignment)的整数倍,也就是说如果有空白,也都会使用0x00来填充。

PE文件的两种状态

PE文件在硬盘和在内存中拉伸后的区别:

  1. 文件起始地址不同。
  2. 节起始地址不同,由内存对齐(SectionAlignment)成员属性决定PE文件运行时在内存中的节起始地址。
  3. 成员内存对齐的大小和文件对齐的大小可能一样,也可能不一样。
  4. 差异在于每个头和节,或每个节和节的空白区不一样。

猜你喜欢

转载自blog.csdn.net/qq_18120361/article/details/112488757