• 羽夏壳世界—— PE 结构(上)


    写在前面

      此系列是本人一个字一个字码出来的,包括代码实现和效果截图。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

    你如果是从中间插过来看的,请仔细阅读 羽夏壳世界——序 ,方便学习本教程。

    概述

      在学习PE结构之前,我们来大体看一下它的整体结构:

      初步学习PE文件,可能看的比较头大。如果你学习过编程语言的话,可以这么说:PE 文件是由一堆结构体和其他数据堆积起来的 。在学习本教程时,建议安装010 Editor可以更方便的学习PE结构,不过该软件是商用的。使用该软件主要是用了它的模板功能,如果是指定类型的文件,它也会主动匹配类型并提示使用相应的模板,如下所示是使用模板之后的效果:

      PE文件有两种状态,一个是在磁盘以文件的形式进行保存,另一个就是在内存中,如下图所示:

      为什么这么说呢?是因为有对齐这个概念,什么是对齐如果不会建议自己进行查阅,这个无论是编程语言还是操作系统基础中非常重要的概念,我就不在这里絮叨了。对于为什么有两种状态,之后的博文将会介绍。
      在PE结构中,有一个十分重要的概念就是RVAFOARVA英文全称为Relative Virtual Address,即相对虚拟地址;FOA英文全称为File Offset Address,即文件偏移地址。就是因为PE有两种状态,所以会有这两种偏移地址。当然还有一个VA,英文全称为Virtual Address,意为绝对的虚拟地址,也就是绝对值,是几就是几。而RVA需要基址才能准确定位到VA。有关RVAFOAVA之间的相互转化,将会到下一篇继续。

    IMAGE_DOS_HEADER

      该结构体的结构如下:

    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系统,上面除了最后一个成员e_lfanew,其他的都是给16位DOS程序看的。不过第一个成员e_magic被作为合法PE文件的校验值,值为IMAGE_DOS_SIGNATURE/0x5A4D/"MZ"
      e_lfanew为文件偏移,指向了IMAGE_NT_HEADERS结构,这个就是我们真正可以跑在现在图形界面的Windows程序必须有的结构,我们来看一个程序与此部分相关的结构示意图如下:

    DosStub

      接在IMAGE_DOS_HEADER结构体后面是被称之为DosStub的,我们能够看到一个字符串This program cannot be run in DOS mode,意为该程序不能够在Dos模式下运行。这里面其实不是垃圾数据,而是有意义的汇编代码,我们用16位反汇编器来看一下:

    seg000:0000 seg000          segment byte public 'CODE' use16
    seg000:0000                 assume cs:seg000
    seg000:0000                 assume es:nothing, ss:seg000, ds:nothing, fs:nothing, gs:nothing
    seg000:0000
    seg000:0000 ; =============== S U B R O U T I N E =======================================
    seg000:0000
    seg000:0000                 public start
    seg000:0000 start           proc near
    seg000:0000                 push    cs
    seg000:0001                 pop     ds
    seg000:0002                 assume ds:seg000
    seg000:0002                 mov     dx, 0Eh
    seg000:0005                 mov     ah, 9
    seg000:0007                 int     21h             ; DOS - PRINT STRING
    seg000:0007                                         ; DS:DX -> string terminated by "$"
    seg000:0009                 mov     ax, 4C01h
    seg000:000C                 int     21h             ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
    seg000:000C start           endp                    ; AL = exit code
    seg000:000C
    seg000:000C ; ---------------------------------------------------------------------------
    seg000:000E aThisProgramCan db 'This program cannot be run in DOS mode.',0Dh,0Dh,0Ah
    seg000:000E                 db '$',0
    seg000:003A                 align 8
    seg000:0040                 db 0A9h, 2Ch, 93h, 71h, 0EDh, 4Dh, 0FDh, 22h, 0EDh, 4Dh
    seg000:0040                 db 0FDh, 22h, 0EDh, 4Dh, 0FDh, 22h, 8Fh, 35h, 0FCh, 23h
    seg000:0040                 db 0EEh, 4Dh, 0FDh, 22h, 8Fh, 35h, 0F8h, 23h, 0F5h, 4Dh
    seg000:0040                 db 0FDh, 22h, 8Fh, 35h, 0F9h, 23h, 0E7h, 4Dh, 0FDh, 22h
    seg000:0040                 db 8Fh, 35h, 0FEh, 23h, 0EEh, 4Dh, 0FDh, 22h, 3Eh, 3Fh
    seg000:0040                 db 0FCh, 23h, 0E9h, 4Dh, 0FDh, 22h, 0EDh, 4Dh, 0FCh, 22h
    seg000:0040                 db 0AAh, 4Dh, 0FDh, 22h, 6Dh, 34h, 0F9h, 23h, 0ECh, 4Dh
    seg000:0040                 db 0FDh, 22h, 6Dh, 34h, 2, 22h, 0ECh, 4Dh, 0FDh, 22h, 6Dh
    seg000:0040                 db 34h, 0FFh, 23h, 0ECh, 4Dh, 0FDh, 22h, 52h, 69h, 63h
    seg000:0040                 db 68h, 0EDh, 4Dh, 0FDh, 22h, 8 dup(0)
    seg000:00A8                 db 10h dup(?)
    seg000:00A8 seg000          ends
    seg000:00A8
    seg000:00A8
    seg000:00A8                 end start
    

      注意一定要使用16位的反汇编器,否则无论是在32位是64位下,所有的代码都是错误的。
      上面的代码如果没有《微机原理》的基础可能有点难懂,这里就补充一下:int 21hDos下的中断,类似表示我要调用API了,不同的AH寄存器的值表示调用的函数是不一样的。如果是9,表示在屏幕上打印字符串,不过这个字符串不是用\0结尾的,而是$,和我们现代操作系统的\0的作用是一样的。如果AH的值为0x4C,则表示将AL作为返回值结束程序,和我们C语言编写main函数中的return 0;是一个道理。
      有关该部分就介绍到这里。

    IMAGE_NT_HEADERS

      该结构体在32位和64位程序是有所不同的,只不过是结构体的成员大小的区别,如下所示:

    typedef struct _IMAGE_NT_HEADERS {
        DWORD Signature;
        IMAGE_FILE_HEADER FileHeader;
        IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
    
    typedef struct _IMAGE_NT_HEADERS64 {
        DWORD Signature;
        IMAGE_FILE_HEADER FileHeader;
        IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
    

      Signature是一个十分重要的成员。PE指纹的第二部分,值为IMAGE_NT_SIGNATURE/0x00004550/"PE"。如果值是错误的,同样被判定为非法PE文件。
      FileHeader是标准PE头,大小为20个字节,可以通过IMAGE_SIZEOF_FILE_HEADER宏获取,具体细节后面将会介绍。
      OptionalHeader是扩展PE头,虽然名字带着可选,但它是必需结构。
      我们再来看看它在二进制文件下的位置:

    IMAGE_FILE_HEADER

      该结构体在32位和64位的程序是一样的,如下所示:

    typedef struct _IMAGE_FILE_HEADER {
        WORD    Machine;
        WORD    NumberOfSections;
        DWORD   TimeDateStamp;
        DWORD   PointerToSymbolTable;
        DWORD   NumberOfSymbols;
        WORD    SizeOfOptionalHeader;
        WORD    Characteristics;
    } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
    

      Machine指示可以运行在什么样的CPU上,它的值如下:

    • 任意CPUIMAGE_FILE_MACHINE_UNKNOWN/0x0000
    • Intel 386以及后续CPU: IMAGE_FILE_MACHINE_I386/0x014C
    • x64:IMAGE_FILE_MACHINE_AMD64/0x8664

    下面是其中的所有成员:

    #define IMAGE_FILE_MACHINE_UNKNOWN           0
    #define IMAGE_FILE_MACHINE_TARGET_HOST       0x0001  // Useful for indicating we want to interact with the host and not a WoW guest.
    #define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
    #define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
    #define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
    #define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
    #define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
    #define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
    #define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
    #define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
    #define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
    #define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
    #define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
    #define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
    #define IMAGE_FILE_MACHINE_THUMB             0x01c2  // ARM Thumb/Thumb-2 Little-Endian
    #define IMAGE_FILE_MACHINE_ARMNT             0x01c4  // ARM Thumb-2 Little-Endian
    #define IMAGE_FILE_MACHINE_AM33              0x01d3
    #define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
    #define IMAGE_FILE_MACHINE_POWERPCFP         0x01f1
    #define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
    #define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
    #define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
    #define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
    #define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
    #define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64
    #define IMAGE_FILE_MACHINE_TRICORE           0x0520  // Infineon
    #define IMAGE_FILE_MACHINE_CEF               0x0CEF
    #define IMAGE_FILE_MACHINE_EBC               0x0EBC  // EFI Byte Code
    #define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)
    #define IMAGE_FILE_MACHINE_M32R              0x9041  // M32R little-endian
    #define IMAGE_FILE_MACHINE_ARM64             0xAA64  // ARM64 Little-Endian
    #define IMAGE_FILE_MACHINE_CEE               0xC0EE
    

      NumberOfSections指示节的数量,它十分重要。
      TimeDateStamp指示编译器填写的时间戳与文件属性里面创建时间/修改时间无关,计算的是当前时间与1970年0时0点0分差的秒数。
      PointerToSymbolTable/NumberOfSymbols与调试相关,不做关注。
      SizeOfOptionalHeader表示扩展PE头的大小,可以修改合适的数值。在默认情况下,32位PE文件:0xE0,64位PE文件:0xF0
      Characteristics指示了文件属性,它的值有如下:

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

      我们再来看看它在二进制文件下的位置:

    IMAGE_OPTIONAL_HEADER

      这个结构体在32位和64位是有区别的,是某些成员的大小区别,它们的结构如下:

    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;    //目录表的个数(决定DataDirectory数组长度)
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
    
    
    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;
    
    #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
    

      Magic标识程序的位数。32位程序为值为IMAGE_NT_OPTIONAL_HDR32_MAGIC/0x10b,64位程序为值为IMAGE_NT_OPTIONAL_HDR64_MAGIC/0x20b。它是判断程序位数的关键属性。
      AddressOfEntryPoint的值表示程序入口RVA,十分重要。
      ImageBase表示该PE比较倾向的内存镜像加载基址,比较重要。对于DLL这个值通常并不被操作系统采纳,如果开启了随机基址的EXE也是如此。
      SectionAlignment表示内存对齐值,十分重要。
      FileAlignment表示文件对齐值,十分重要。
      SizeOfImage表示内存中整个PE文件的映射的尺寸,可比实际的值大,必须是SectionAlignment的整数倍,十分重要。
      SizeOfHeaders所有头和节表按照文件对齐后的大小,否则加载会出错,这十分重要。
      CheckSum表示校验和,一些系统文件有要求用来判断文件是否被修改。
      Subsystem意为子系统,驱动程序值为1,图形界面值为2,控制台、DLL值为3。下面是其枚举:

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

      DllCharacteristics是文件特性,不是针对DLL文件的,该成员是比较重要的,基址重定位就是从这个成员进行设置的。下面是其枚举:

    //      IMAGE_LIBRARY_PROCESS_INIT            0x0001     // Reserved.
    //      IMAGE_LIBRARY_PROCESS_TERM            0x0002     // Reserved.
    //      IMAGE_LIBRARY_THREAD_INIT             0x0004     // Reserved.
    //      IMAGE_LIBRARY_THREAD_TERM             0x0008     // Reserved.
    #define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA    0x0020  // Image can handle a high entropy 64-bit virtual address space.
    #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040     // DLL can move.
    #define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    0x0080     // Code Integrity Image
    #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT    0x0100     // Image is NX compatible
    #define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200     // Image understands isolation and doesn't want it
    #define IMAGE_DLLCHARACTERISTICS_NO_SEH       0x0400     // Image does not use SEH.  No SE handler may reside in this image
    #define IMAGE_DLLCHARACTERISTICS_NO_BIND      0x0800     // Do not bind this image.
    #define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000     // Image should execute in an AppContainer
    #define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   0x2000     // Driver uses WDM model
    #define IMAGE_DLLCHARACTERISTICS_GUARD_CF     0x4000     // Image supports Control Flow Guard.
    #define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 
    

      DataDirectory是存储表位置大小的数组。不同的索引代表不同的表的数据,下面是其枚举:

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

      我们再来看看它在二进制文件下的位置:

    IMAGE_SECTION_HEADER

      IMAGE_SECTION_HEADER是节区头,是重要的结构体,在32位和64位下的程序没有区别。大小可通过IMAGE_SIZEOF_SECTION_HEADER获取,在内存的展开大小 = Max(Misc,SizeOfRawData)。如果此节为已初始化的变量,则 Misc > SizeOfRawData;若节为未初始化的变量则 Misc < SizeOfRawData。如下是结构体成员:

    #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表示节区名称,ASCII字符串,可自定义,只截取8个。
      Misc表示该节在没有对齐前的真实尺寸,该值可以不准确。
      VirtualAddress是在内存中的偏移地址,加上lmageBase才是在内存中的真正地址,十分重要。
      PointerToRawData是节区在文件中的偏移,十分重要。
      PointerToRelocations/PointerToRelocations/NumberOfRelocations/PointerToLinenumbers与调试相关,不关注。
      Characteristics是节的属性。下面是其枚举:

    //      IMAGE_SCN_TYPE_REG                   0x00000000  // Reserved.
    //      IMAGE_SCN_TYPE_DSECT                 0x00000001  // Reserved.
    //      IMAGE_SCN_TYPE_NOLOAD                0x00000002  // Reserved.
    //      IMAGE_SCN_TYPE_GROUP                 0x00000004  // Reserved.
    #define IMAGE_SCN_TYPE_NO_PAD                0x00000008  // Reserved.
    //      IMAGE_SCN_TYPE_COPY                  0x00000010  // Reserved.
    #define IMAGE_SCN_CNT_CODE                   0x00000020  // Section contains code.
    #define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  // Section contains initialized data.
    #define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  // Section contains uninitialized data.
    #define IMAGE_SCN_LNK_OTHER                  0x00000100  // Reserved.
    #define IMAGE_SCN_LNK_INFO                   0x00000200  // Section contains comments or some other type of information.
    //      IMAGE_SCN_TYPE_OVER                  0x00000400  // Reserved.
    #define IMAGE_SCN_LNK_REMOVE                 0x00000800  // Section contents will not become part of image.
    #define IMAGE_SCN_LNK_COMDAT                 0x00001000  // Section contents comdat.
    //                                           0x00002000  // Reserved.
    //      IMAGE_SCN_MEM_PROTECTED - Obsolete   0x00004000
    #define IMAGE_SCN_NO_DEFER_SPEC_EXC          0x00004000  // Reset speculative exceptions handling bits in the TLB entries for this section.
    #define IMAGE_SCN_GPREL                      0x00008000  // Section content can be accessed relative to GP
    #define IMAGE_SCN_MEM_FARDATA                0x00008000
    //      IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
    #define IMAGE_SCN_MEM_PURGEABLE              0x00020000
    #define IMAGE_SCN_MEM_16BIT                  0x00020000
    #define IMAGE_SCN_MEM_LOCKED                 0x00040000
    #define IMAGE_SCN_MEM_PRELOAD                0x00080000
    #define IMAGE_SCN_ALIGN_1BYTES               0x00100000  //
    #define IMAGE_SCN_ALIGN_2BYTES               0x00200000  //
    #define IMAGE_SCN_ALIGN_4BYTES               0x00300000  //
    #define IMAGE_SCN_ALIGN_8BYTES               0x00400000  //
    #define IMAGE_SCN_ALIGN_16BYTES              0x00500000  // Default alignment if no others are specified.
    #define IMAGE_SCN_ALIGN_32BYTES              0x00600000  //
    #define IMAGE_SCN_ALIGN_64BYTES              0x00700000  //
    #define IMAGE_SCN_ALIGN_128BYTES             0x00800000  //
    #define IMAGE_SCN_ALIGN_256BYTES             0x00900000  //
    #define IMAGE_SCN_ALIGN_512BYTES             0x00A00000  //
    #define IMAGE_SCN_ALIGN_1024BYTES            0x00B00000  //
    #define IMAGE_SCN_ALIGN_2048BYTES            0x00C00000  //
    #define IMAGE_SCN_ALIGN_4096BYTES            0x00D00000  //
    #define IMAGE_SCN_ALIGN_8192BYTES            0x00E00000  //
    // Unused                                    0x00F00000
    #define IMAGE_SCN_ALIGN_MASK                 0x00F00000
    #define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  // Section contains extended relocations.
    #define IMAGE_SCN_MEM_DISCARDABLE            0x02000000  // Section can be discarded.
    #define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  // Section is not cachable.
    #define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  // Section is not pageable.
    #define IMAGE_SCN_MEM_SHARED                 0x10000000  // Section is shareable.
    #define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
    #define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
    #define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.
    

      我们再来看看它在二进制文件下的位置:

    IMAGE_DATA_DIRECTORY

      IMAGE_DATA_DIRECTORY是十分重要的结构体,具体重要性的体现已经在IMAGE_OPTIONAL_HEADER介绍过了,如下是其结构:

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;
        DWORD   Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
    

      VirtualAddress是指表在内存的RVA,十分重要。
      Size是指IMAGE_DATA_DIRECTORY结构体和其使用的所有数据之和,该值不会影响程序的运行。

      我们再来看看它在二进制文件下的位置:

    小结

      本篇我们只介绍了基本结构体,对于后面比较复杂的几个表和地址转化,考虑到比较复杂,挪到下一篇进行。
      看完本篇文章一定要把在16进制下看明白结构,最好用自己熟悉的编程语言写一个解析上面所述结构体的解析器,以巩固自己的学习成果。

    下一篇

      羽夏壳世界—— PE 结构(下)

  • 相关阅读:
    Thinkphp下载oss文件至本地压缩包
    csblog: 一站式秃头孵化基地
    esp c3下载程序一直重启
    总结vue 需要掌握的知识点
    Python编程 元组中不允许的操作
    5.11 Firmware Commit command
    Docker DeskTop的安装(Windows版本)
    单元测试效率优化:为什么要对程序进行测试?测试有什么好处?
    二叉查找树的插入
    11.(地图数据篇)OSM数据如何下载使用
  • 原文地址:https://www.cnblogs.com/wingsummer/p/16125690.html