Windows 的 PE 文件结构简介

Windows 的 PE 文件头结构包括三大部分:DOS 文件头、NT 文件头以及 Section 表(节表),在 DOS 文件头后面有一小段 DOS 程序,被称为 DOS stub 程序。

DOS stub 程序是运行在 DOS 下面的 16 位程序,目的是指出:当 windows 程序在 dos 下运行时,将显示信息:This program cannot be run in DOS mode.... 然后终止执行。

1.MS-DOS 文件头

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;

IMAGE_DOS_HEADER 结构共 64 bytes,从映象的 0x00000000 - 0x0000003F。

其中最重要的是:e_magic 和 e_lfanew 域(上面红色部分标注),e_magic 是 MS-DOS 文件头的签名,它的值是:0x5A4D,这个签名在 WinNT.h 中定义为:

#define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ

它的值代表字母 MZ,表示 MS-DOS 文件头。 e_lfanew 是一个 offset 偏移量,指出 IMAGE_NT_HEADER 在映象中的位置。

2.IMAGE_NT_HEADER 结构

  IMAGE_NT_HEADER 是 PE 文件的核心部分, IMAGE_NT_HEADER 在 WinNT.h 中定义为两个版本,分别是: IMAGE_NT_HEADERS64 和  IMAGE_NT_HEADERS32,它们的定义如下:

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

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

当使用在 win64 下时,IMAGE_NT_HEADER 使用的是 64 位版本 IMAGE_NT_HEADERS64,当使用在 win32 下时,IMAGE_NT_HEADER 使用的是 IMAGE_NT_HEADERS32

#ifdef _WIN64
typedef IMAGE_NT_HEADERS64                  IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64                 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32                  IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32                 PIMAGE_NT_HEADERS;
#endif

在 IMAGE_NT_HEADERS 结构的定义里得出,它包含了一个签名和两个结构体,这个签名是 0x00004550 表示 PE 文件:

#define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

2.1 IMAGE_FILE_HEADER 结构

在 IMAGE_NT_HEADER 的中 IMAGE_FILE_HEADER 定义如下:

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;

这个结构共 20 bytes,下面是这些域的描述:

size

描述

Machine

WORD

IMAGE_FILE_MACHINE_xxx

表示目标平台 processor 类型,例:IMAGE_FILE_MACHINE_I386

NumberOfSection

WORD

---

表示映象中有多少个 section

TimeDataStamp

DWORD

从1970年1月1日0:00 以来的总秒数

表示文件创建的时间

PointerToSymbolTable

DWORD

COFF 符号表偏移量

在 PE 中很少见,总是为 0

NumberOfSymbols

DWORD

COFF 符号表的个数

如果存在的话,表示符号表的个数

SizeOfOptionHeader

WORD

IMAGE_OPTIONAL_HEADER 结构大小

该域表示 IMAGE_NT_HEADER 中的 IMAGE_OPTIONAL_HEADER 结构的大小

Characteristics

WORD

IMAGE_FILE_xxx

表示文件属性,例如:IMAGE_FILE_DLL 属性


在 WinNT.h 文件里定义了一系列的 Machine 值,这里举列一些,详细的参见 WinNT.h 文件:IMAGE_FILE_HEADER 结构中比较重要的域是:Machine 和 SizeOfOptionalHeaderMachine 可以用来判断目标平台,比如:值为 0x8664 是代表 AMD64(即:x64 平台)它也适合 Intel64 平台。SizeOfOptionalHeader 指出 IMAGE_OPTIONAL_HEADER 结构的大小。

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)

也为 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 externel 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  // Agressively 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.


虽然在 Machine 里指出目标平台,但是对于判断映象是 32 位还是 64 位的 PE 文件,Microsoft 官方认可以的方法是:通过 IMAGE_OPTIONAL_HEADER 结构里的 Magic 域。

2.2 IMAGE_OPTIONAL_HEADER 结构

与 IMAGE_NT_HEADER 一样,IMAGE_OPTIONAL_HEADER 也有 32 位版本和 64 位版本,因此,相应版本的 IMAGE_NT_HEADER 对应相应版本的 IMAGE_OPTIONAL_HEADER。虽然这个结构被称为 IMAGE_OPTIONAL_HEADER(可选),但是它却是必须存在于 IMAGE_NT_HEADER 结构中。

下面是 64 位版本的 IMAGE_OPTIONAL_HEADER 结构定义, 32 位版本的参见 WinNT.h 中的定义

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;

IMAGE_OPTIONAL_HEADER 结构的定义稍长一些,下面是自来 Matt Pietrek 所写的文章,名为《An In-Depth Look into the Win32 Portable Executable File Format》中对IMAGE_OPTIONAL_HEADER 结构的描述,地址在:http://msdn.microsoft.com/en-us/magazine/bb985997.aspx 其中的 Figure 5 IMAGE_OPTIONAL_HEADER 一节里对 IMAGE_OPTIONAL_HEADER 结构有详细的描述。这里就不再描述了。 :)

关键的一点:

  在 IMAGE_OPTIONAL_HEADER 里,第 1 个域 magic 用来识别文件头是 32 位还是 64 位

这个 magic 的值在 WinNT.h 的定义如下:

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b

当 magic = 0x10b 时,映象是 32 位,magic = 0x20b 时,映象是 64 位。

2.3 IMAGE_DATA_DIRECTORY 表格

IMAGE_DATA_DIRECTORY 结构在 WinNT.h 中定义如下:

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

这个结构十分重要,它用来描述 windows 执行映象中所使用的各种表格的位置和大小。VirtualAddress 域是一个 RVA(Relative Virtual Address)值,更明白一点就是:它是一个偏移量(基于 PE 文件头),Size 域表示这个表格有多大。

这个数组有 16 个元素,也就是表示,在执行映象中最多可以使用 16 个表格。在 WinNT.h 里定义为:

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

由于这个 IMAGE_DATA_DIRECTORY 表格用来描述在映象中所使用到的表格(最多 16 个表格)

实际上这 16 个表格是固定的,对于这些表格 Microsoft 都作了统一的规定,在 WinNT.h 里都作了定义:

// Directory Entries

#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

从定义上得出,第 0 项是 export table(导出表),第 1 项是 import table(导入表)等等,在 Microsoft 的 MSDN 网站里有一个知识点介绍: http://msdn.microsoft.com/en-us/library/ms680305(VS.85).aspx

下面,我将这些表格归纳如下:

表项
表格
0
export table
1
import table
2
resource table
3
exception table
4
certificate table
5
base relocation table
6
debug
7
architecute
8
global pointer
9
TLS table
10
load configuration table
11
bound import
12
import address table
13
delay import descriptor
14
CLR runtime header
15
reserved, must bo zero

2.3.1 import table

import table 在 WinNT.h 中定义为一个 IMAGE_IMPORT_DESCRIPTOR 结构,如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;

IMAGE_IMPORT_DESCRIPTOR 结构 5 个域,共 20 bytes,OriginalFirstThunk 域指向一个 IMAGE_THUNK_DATA 结构,IMAGE_THUNK_DATA 实际上只有一个域AddressOfDataAddressOfData 指向一个 IMAGE_IMPORT_BY_NANE 结构,它们在 WinNT.h 中的定义为:

//
// Import Format
//

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

#include "pshpack8.h"                       // Use align 8 for the 64-bit IAT.

typedef struct _IMAGE_THUNK_DATA64 {
    union {
        ULONGLONG ForwarderString;  // PBYTE 
        ULONGLONG Function;         // PDWORD
        ULONGLONG Ordinal;
        ULONGLONG AddressOfData;    // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

IMAGE_IMPORT_BY_NAME 是最终的 import table 结构,是原始的 Thunk 表格,这个 Thunk 表格是一个包含所有导入 function 的列表,这个 Thunk table 包括了 Hint, function name 和 DLL name。 Hint 代表每个函数的标识,是一个 16 位的数值,Hint 下面接着是 import 的函数名,最后是所导入的 DLL name。

IMAGE_IMPORT_DESCRIPTOR 里的 FirstThunk 指向出 Import Address Table (IAT)表格,这个 IAT 大小为 16 bytes ,前 4 bytes 是一个 RVA 指向上面所说的 Thunk table。

          import table

     +----------------------+                       IMAGE_THUNK_DATA
 (0) | OriginalFirstThunk   | ----------------> +--------------------+
     +----------------------+                   |   AddressOfData    | ------\         
     | TimeDataStamp        |                   +--------------------+       |
     +----------------------+            +----> |   AddressOfData    | ---+  |  
     | ForwarderChain       |            |      +--------------------+    |  |
     +----------------------+            |                                |  |
     | Name                 |            |                                |  | 
     +----------------------+            |                                |  | 
     | FirstThunk           | ----\      |                                |  | 
     +----------------------+     |      |                                |  | 
 (1) | OriginalFirstThunk   | ----+------+                                |  |       Thunk Table (IMAGE_IMPORT_BY_NAME)
     +----------------------+     |                                       |  |
     | TimeDataStramp       |     |                                       |  +--->  +-------+-----------------+--------------+
     +----------------------+     |                                     +-|------>  | Hint  | function name   | DLL name     |
     | ForwarderChain       |     |                                     | +------>  +-------+-----------------+--------------+
     +--------------------- +     |                                     |  +----->  | Hint  | function name   | DLL name     |
     | Name                 |     |      IAT (Import Address Table)     |  |        +-------+-----------------+--------------+
     +----------------------+     |    +----------------------------+   |  |        | Hint  | function name   | DLL name     |
     | FirstThunk           |--+  +--> |     Thunk table            | --+  |        +-------+-----------------+--------------+
     +----------------------+  |       +----------------------------+      |        | Hint  | function name   | DLL name     |
                               +-----> |     Thunk table            | -----+        +-------+-----------------+--------------+
                                       +----------------------------+



● import table(IMAGE_IMPORT_DESCRIPTOR)

上面是一张关系图表,上面所示,在映象和内存中,存在 4 张表格:

● Thunk table pointer-table(IMAGE_THUNK_DATA)
● IAT(Import Address Table)
● 导入函数的 Thunk Table(IMAGE_IMPORT_BY_NAME)。

section table(节表)结构

IMAGE_NT_HEADER 结构后面紧接着就是 section table(节表)结构

这个节表结构在 WinNT.h 中定义为

//
// Section header format.
//

#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;

#define IMAGE_SIZEOF_SECTION_HEADER          40

映象中包括有多少个节表结构,由 IMAGE_NT_HEADER 结构中的 IMAGE_FILE_HEADER 结构中的 NumberOfSections 域指出。

在 IMAGE_SECTION_HEADER 结构的第 1 个域 Name,用来标识 section table 的名字。它的长度固定为 8 bytes(前面定义的宏),这将意味着,不存在超过 8 bytes 的节表名。接下来使用 VirtualSize 来用表示 section talbe 大小。VirtualAddress 表示 section table 的 RVA。

size

描述

Name

8 bytes

Section 表名字

VirtualSize

DWORD

Section 表的大小

VirtualAddress

DWORD

Section 表的 RVA,即:section 表的位置

SizeOfRawData

DWORD

section 表占用映像的大小,这个 size 是以 0x200 为单位的

PointerToRawData

DWORD

section 表在映像中的物理位置,即是:file 位置,而非 virtual 位置

PointerToRelocation

DOWRD

PointerToLinenumber

DWORD

NumberOfRelocation

WORD

NumberOfLineumbers

WORD

Characteristics

DWORD

section 的属性 flags,可用于 '|' 多个属性值

所有的 Characteristics 都在 WinNT.h 中有定义,下面是一些常用的 flags:

#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_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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值