• PE文件结构详解(二)可执行文件头


    PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构。

    了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格式,重要的信息都包含在头部,顺着头部的信息,可以引导系统解析整个文件。所以,我们先来认识一下PE文件的头部格式。还记得上篇里的那个图吗?


    DOS头和NT头就是PE文件中两个重要的文件头。

    一、DOS头

    DOS头的作用是兼容MS-DOS 操作系统中的可执行文件,对于32位PE文件来说,DOS所起的作用就是显示一行文字,提示用户:我需要在32位windows上才可以运行。我认为这是 个善意的玩笑,因为他并不像显示的那样不能运行,其实已经运行了,只是在DOS上没有干用户希望看到的工作而已,好吧,我承认这不是重点。但是,至少我们 看一下这个头是如何定义的:

    1. typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header  
    2.     WORD   e_magic;                     // Magic number  
    3.     WORD   e_cblp;                      // Bytes on last page of file  
    4.     WORD   e_cp;                        // Pages in file  
    5.     WORD   e_crlc;                      // Relocations  
    6.     WORD   e_cparhdr;                   // Size of header in paragraphs  
    7.     WORD   e_minalloc;                  // Minimum extra paragraphs needed  
    8.     WORD   e_maxalloc;                  // Maximum extra paragraphs needed  
    9.     WORD   e_ss;                        // Initial (relative) SS value  
    10.     WORD   e_sp;                        // Initial SP value  
    11.     WORD   e_csum;                      // Checksum  
    12.     WORD   e_ip;                        // Initial IP value  
    13.     WORD   e_cs;                        // Initial (relative) CS value  
    14.     WORD   e_lfarlc;                    // File address of relocation table  
    15.     WORD   e_ovno;                      // Overlay number  
    16.     WORD   e_res[4];                    // Reserved words  
    17.     WORD   e_oemid;                     // OEM identifier (for e_oeminfo)  
    18.     WORD   e_oeminfo;                   // OEM information; e_oemid specific  
    19.     WORD   e_res2[10];                  // Reserved words  
    20.     LONG   e_lfanew;                    // File address of new exe header  
    21.   } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;  

    我们只需要关注两个域:

    e_magic:一个WORD类型,值是一个常数0x4D5A,用文本编辑器查看该值位‘MZ’,可执行文件必须都是'MZ'开头。

    e_lfanew:为32位可执行文件扩展的域,用来表示DOS头之后的NT头相对文件起始地址的偏移。

    二、NT头

    顺着DOS头中的e_lfanew,我们很容易可以找到NT头,这个才是32位PE文件中最有用的头,定义如下:

    1. typedef struct _IMAGE_NT_HEADERS {  
    2.     DWORD Signature;  
    3.     IMAGE_FILE_HEADER FileHeader;  
    4.     IMAGE_OPTIONAL_HEADER32 OptionalHeader;  
    5. } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;  

    下图是一张真实的PE文件头结构以及其各个域的取值:

    Signature:类似于DOS头中的e_magic,其高16位是0,低16是0x4550,用字符表示是'PE‘。

    IMAGE_FILE_HEADER是PE文件头,c语言的定义是这样的:

    1. typedef struct _IMAGE_FILE_HEADER {  
    2.     WORD    Machine;  
    3.     WORD    NumberOfSections;  
    4.     DWORD   TimeDateStamp;  
    5.     DWORD   PointerToSymbolTable;  
    6.     DWORD   NumberOfSymbols;  
    7.     WORD    SizeOfOptionalHeader;  
    8.     WORD    Characteristics;  
    9. } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;  

    每个域的具体含义如下:

    Machine:该文件的运行平台,是x86、x64还是I64等等,可以是下面值里的某一个。

    1. #define IMAGE_FILE_MACHINE_UNKNOWN           0  
    2. #define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.  
    3. #define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian  
    4. #define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian  
    5. #define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian  
    6. #define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2  
    7. #define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP  
    8. #define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian  
    9. #define IMAGE_FILE_MACHINE_SH3DSP            0x01a3  
    10. #define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian  
    11. #define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian  
    12. #define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5  
    13. #define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian  
    14. #define IMAGE_FILE_MACHINE_THUMB             0x01c2  
    15. #define IMAGE_FILE_MACHINE_AM33              0x01d3  
    16. #define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian  
    17. #define IMAGE_FILE_MACHINE_POWERPCFP         0x01f1  
    18. #define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64  
    19. #define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS  
    20. #define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64  
    21. #define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS  
    22. #define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS  
    23. #define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64  
    24. #define IMAGE_FILE_MACHINE_TRICORE           0x0520  // Infineon  
    25. #define IMAGE_FILE_MACHINE_CEF               0x0CEF  
    26. #define IMAGE_FILE_MACHINE_EBC               0x0EBC  // EFI Byte Code  
    27. #define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)  
    28. #define IMAGE_FILE_MACHINE_M32R              0x9041  // M32R little-endian  
    29. #define IMAGE_FILE_MACHINE_CEE               0xC0EE  

    NumberOfSections:该PE文件中有多少个节,也就是节表中的项数。

    TimeDateStamp:PE文件的创建时间,一般有连接器填写。

    PointerToSymbolTable:COFF文件符号表在文件中的偏移。

    NumberOfSymbols:符号表的数量。

    SizeOfOptionalHeader:紧随其后的可选头的大小。

    Characteristics:可执行文件的属性,可以是下面这些值按位相或。

    1. #define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.  
    2. #define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).  
    3. #define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.  
    4. #define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.  
    5. #define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set  
    6. #define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses  
    7. #define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.  
    8. #define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.  
    9. #define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file  
    10. #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.  
    11. #define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.  
    12. #define IMAGE_FILE_SYSTEM                    0x1000  // System File.  
    13. #define IMAGE_FILE_DLL                       0x2000  // File is a DLL.  
    14. #define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine  
    15. #define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.  

    可以看出,PE文件头定义了PE文件的一些基本信息和属性,这些属性会在PE加载器加载时用到,如果加载器发现PE文件头中定义的一些属性不满足当前的运行环境,将会终止加载该PE。

    另一个重要的头就是PE可选头,别看他名字叫可选头,其实一点都不能少,不过,它在不同的平台下是不一样的,例如32位下是IMAGE_OPTIONAL_HEADER32,而在64位下是IMAGE_OPTIONAL_HEADER64。为了简单起见,我们只看32位。

    1. typedef struct _IMAGE_OPTIONAL_HEADER {  
    2.     WORD    Magic;  
    3.     BYTE    MajorLinkerVersion;  
    4.     BYTE    MinorLinkerVersion;  
    5.     DWORD   SizeOfCode;  
    6.     DWORD   SizeOfInitializedData;  
    7.     DWORD   SizeOfUninitializedData;  
    8.     DWORD   AddressOfEntryPoint;  
    9.     DWORD   BaseOfCode;  
    10.     DWORD   BaseOfData;  
    11.     DWORD   ImageBase;  
    12.     DWORD   SectionAlignment;  
    13.     DWORD   FileAlignment;  
    14.     WORD    MajorOperatingSystemVersion;  
    15.     WORD    MinorOperatingSystemVersion;  
    16.     WORD    MajorImageVersion;  
    17.     WORD    MinorImageVersion;  
    18.     WORD    MajorSubsystemVersion;  
    19.     WORD    MinorSubsystemVersion;  
    20.     DWORD   Win32VersionValue;  
    21.     DWORD   SizeOfImage;  
    22.     DWORD   SizeOfHeaders;  
    23.     DWORD   CheckSum;  
    24.     WORD    Subsystem;  
    25.     WORD    DllCharacteristics;  
    26.     DWORD   SizeOfStackReserve;  
    27.     DWORD   SizeOfStackCommit;  
    28.     DWORD   SizeOfHeapReserve;  
    29.     DWORD   SizeOfHeapCommit;  
    30.     DWORD   LoaderFlags;  
    31.     DWORD   NumberOfRvaAndSizes;  
    32.     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];  
    33. } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;  


    Magic:表示可选头的类型。

    1. #define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b  // 32位PE可选头  
    2. #define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b  // 64位PE可选头  
    3. #define IMAGE_ROM_OPTIONAL_HDR_MAGIC       0x107    

    MajorLinkerVersion和MinorLinkerVersion:链接器的版本号。

    SizeOfCode:代码段的长度,如果有多个代码段,则是代码段长度的总和。

    SizeOfInitializedData:初始化的数据长度。

    SizeOfUninitializedData:未初始化的数据长度。

    AddressOfEntryPoint:程序入口的RVA,对于exe这个地址可以 理解为WinMain的RVA。对于DLL,这个地址可以理解为DllMain的RVA,如果是驱动程序,可以理解为DriverEntry的RVA。当 然,实际上入口点并非是WinMain,DllMain和DriverEntry,在这些函数之前还有一系列初始化要完成,当然,这些不是本文的重点。

    BaseOfCode:代码段起始地址的RVA。

    BaseOfData:数据段起始地址的RVA。

    ImageBase:映象(加载到内存中的PE文件)的基地址,这个基地址是建议,对于DLL来说,如果无法加载到这个地址,系统会自动为其选择地址。

    SectionAlignment:节对齐,PE中的节被加载到内存时会按照这个域指定的值来对齐,比如这个值是0x1000,那么每个节的起始地址的低12位都为0。

    FileAlignment:节在文件中按此值对齐,SectionAlignment必须大于或等于FileAlignment。

    MajorOperatingSystemVersion、MinorOperatingSystemVersion:所需操作系统的版本号,随着操作系统版本越来越多,这个好像不是那么重要了。

    MajorImageVersion、MinorImageVersion:映象的版本号,这个是开发者自己指定的,由连接器填写。

    MajorSubsystemVersion、MinorSubsystemVersion:所需子系统版本号。

    Win32VersionValue:保留,必须为0。

    SizeOfImage:映象的大小,PE文件加载到内存中空间是连续的,这个值指定占用虚拟空间的大小。

    SizeOfHeaders:所有文件头(包括节表)的大小,这个值是以FileAlignment对齐的。

    CheckSum:映象文件的校验和。

    Subsystem:运行该PE文件所需的子系统,可以是下面定义中的某一个:

    1. #define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.  
    2. #define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.  
    3. #define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.  
    4. #define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.  
    5. #define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.  
    6. #define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.  
    7. #define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.  
    8. #define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.  
    9. #define IMAGE_SUBSYSTEM_EFI_APPLICATION      10  //  
    10. #define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  11   //  
    11. #define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   12  //  
    12. #define IMAGE_SUBSYSTEM_EFI_ROM              13  
    13. #define IMAGE_SUBSYSTEM_XBOX                 14  
    14. #define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16  

    DllCharacteristics:DLL的文件属性,只对DLL文件有效,可以是下面定义中某些的组合:

    1. #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040     // DLL can move.  
    2. #define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    0x0080     // Code Integrity Image  
    3. #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT    0x0100     // Image is NX compatible  
    4. #define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200     // Image understands isolation and doesn't want it  
    5. #define IMAGE_DLLCHARACTERISTICS_NO_SEH       0x0400     // Image does not use SEH.  No SE handler may reside in this image  
    6. #define IMAGE_DLLCHARACTERISTICS_NO_BIND      0x0800     // Do not bind this image.  
    7. //                                            0x1000     // Reserved.  
    8. #define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   0x2000     // Driver uses WDM model  
    9. //                                            0x4000     // Reserved.  
    10. #define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     0x8000  


    SizeOfStackReserve:运行时为每个线程栈保留内存的大小。

    SizeOfStackCommit:运行时每个线程栈初始占用内存大小。

    SizeOfHeapReserve:运行时为进程堆保留内存大小。

    SizeOfHeapCommit:运行时进程堆初始占用内存大小。

    LoaderFlags:保留,必须为0。

    NumberOfRvaAndSizes:数据目录的项数,即下面这个数组的项数。

    DataDirectory:数据目录,这是一个数组,数组的项定义如下:

    1. typedef struct _IMAGE_DATA_DIRECTORY {  
    2.     DWORD   VirtualAddress;  
    3.     DWORD   Size;  
    4. } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  

    VirtualAddress:是一个RVA。

    Size:是一个大小。

    这两个数有什么用呢?一个是地址,一个是大小,可以看出这个数据目录项定义的是一个区域。那他定义的是什么东西的区域呢?前面说了,DataDirectory是个数组,数组中的每一项对应一个特定的数据结构,包括导入表,导出表等等,根据不同的索引取出来的是不同的结构,头文件里定义各个项表示哪个结构,如下面的代码所示:

    1. #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory  
    2. #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory  
    3. #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory  
    4. #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory  
    5. #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory  
    6. #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table  
    7. #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory  
    8. //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)  
    9. #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data  
    10. #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP  
    11. #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory  
    12. #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory  
    13. #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers  
    14. #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table  
    15. #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors  
    16. #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor  

    看到这么多的定义,大家估计要头疼了,好不容易要把PE文件头学习完了,又“从天而降”一大波的结构。不用紧张,有了前面的知识,后面的部分就迎刃而解了。下一篇开始将沿着这个数据目录分解其余部分,继续关注哦~

  • 相关阅读:
    2.25
    2.24
    2.22
    influxdb 配置
    mongodb 数据备份
    influxDb数据备份
    Linux 及 git 指令集合
    git 项目 保存至gitee中
    TypeScripte 资料
    SecureCrt 的配色方案
  • 原文地址:https://www.cnblogs.com/milantgh/p/3947425.html
Copyright © 2020-2023  润新知