PE文件结构解析

t.exe 共 3072 bytes,下面是 t.exe 映象 PE 文件头的整体结构图:

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

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

这段 DOS stub 程序是这样的:

00000040  0E                push cs
00000041  1F                pop ds
00000042  BA0E00            mov dx,0xe
00000045  B409              mov ah,0x9
00000047  CD21              int 0x21
00000049  B8014C            mov ax,0x4c01
0000004C  CD21              int 0x21

信息字符串在位置 0x0e 上,即在:0x00000040 + 0x0e = 0x0000004e,这正好是字符信息“This program cannot be run in DOS mode....$”的地址。

6.1 MS-DOS 文件头

t.exe 映象文件头最开始部分是 MS-DOS 文件头部分,这个文件头结构定义在 WinNT.h 文件里,在我的 windows 7 系统 visual studio 2010 下,这个 winnt.h 在目录:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include 下,在 WinNT.h 文件里定义了 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;

这个 IMAGE_DOS_HEADER 结构共 64 bytes,从映象的 0x00000000 - 0x0000003F,参见上图标注的“DOS 文件头”部分,将这部分按照 IMAGE_DOS_HEADER 结构分解为:

00000000 4D 5A    // e_magic
00000002 90 00    // e_cblp
00000004 03 00    // e_cp
00000006 00 00    // e_cric
00000008 04 00    // e_cparhdr
0000000A 00 00    // e_minalloc
0000000C FF FF    // e_maxalloc
0000000E 00 00   // e_ss
00000010 B8 00    // e_sp
00000012 00 00    // e_csum
00000014 00 00    // e_ip
00000016 00 00    // e_cs
00000018 40 00    // e_lfarlc
0000001A 00 00    // e_ovno
0000001C 00 00 00 00   // e_res[4]
00000020 00 00 00 00   
00000024 00 00    // e_oemid
00000026 00 00    // e_oeminfo
00000028 00 00 00 00  // e_res2[10] 
0000002C 00 00 00 00 
00000030 00 00 00 00 
00000034 00 00 00 00 
00000038 00 00 00 00
0000003C C0 00 00 00  // e_lfanew

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

扫描二维码关注公众号,回复: 5837259 查看本文章

#define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ

它的值代表字母 MZ,表示 MS-DOS 文件头。 e_lfanew 是一个 offset 偏移量,指出 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

在本例 t.exe 映象里 e_lfanew  0x000000C0,它说明 IMAGE_NT_HEADERS 结构位于映象的 0x000000C0 处,此时尚不能断定这个 PE 是 32 位还是 64 位结构。

6.2 IMAGE_NT_HEADER 结构

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

#define IMAGE_NT_SIGNATURE                  0x00004550  // PE00

6.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  属性

IMAGE_FILE_HEADER 结构中比较重要的域是:Machine  SizeOfOptionalHeader, Machine 可以用来判断目标平台,比如:值为 0x8664 是代表 AMD64(即:x64 平台)它也适合 Intel64 平台。SizeOfOptionalHeader 指出 IMAGE_OPTIONAL_HEADER 结构的大小。

 WinNT.h 文件里定义了一系列的 Machine 值,这里举列一些,详细的参见 WinNT.h 文件:

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

下面看看 t.exe 里的 IMAGE_FILE_HEADER 具体的值,如下图所示:

IMAGE_FILE_HEADER 结构

t.exe 映象的目标平台是 x64 平台,它指出了接下来的 IMAGE_OPTIONAL_HEADER 结构大小为:0x00F0 bytes

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

6.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 位。

6.3 观察 t.exe 映象的实际 IMAGE_NT_HEADER 结构

在我们实例 t.exe 映象中,IMAGE_NT_HEADER 的偏移量是 0x000000C0IMAGE_NT_HEADER 结构在 32 位下是 244 bytes,在 64 位下是 264 bytes

下面我们来看一看 t.exe 映象中的 IMAGE_NT_HEADER 结构,从 0x0000000C0 - 0x000001C7(共 264 bytes)

IMAGE_NT_HEADER 结构的签名 Signature 是 0x00004550 表示 PE 文件头。

6.3.1 t.exe 的 IMAGE_FILE_HEADER 结构

蓝色部分 IMAGE_FILE_HEADER 结构,IMAGE_FILE_HEADER 结构紧接着 PE 签名之后,共 20 bytes,其中的 Machine 是 0x8664,由上述定义所得,它代表的机器类型是 AMD64处理器

000000C4  64 86    // Machine = AMD64
000000C6  04 00    // NumberOfSection = 4
000000C8  91 86 CC 4B   // TimeDataStamp = 1271694993 秒
000000CC  00 00 00 00   // PointerToSymbolTable = 0
000000D0  00 00 00 00   // NumberOfSymbols = 0
000000D4  F0 00    // SizeOfOptionHeader = 0xf0(240 bytes)
000000D6  22 00    // Characteristics = 0x0022

它的文件属性是 0x0022,也就是:Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE,说明它是一个可执行的映象,可以在 >2G 地址上,并且指明了接下来的 IMAGE_OPTIONAL_HEADER 结构是 0xf0 bytes(240 个字节)。

6.3.2 t.exe 的 IMAGE_OPTIONAL_HEADER 结构

IMAGE_FILE_HEADER 结构接下来就是 IMAGE_OPTIONAL_HEADER 结构,前面的 SizeOfOptionHeader 域已经指出 IMAGE_OPTIONAL_HEADER 将会是 0xf0 个字节。

000000D8  0B 02                // Magic = 0x020b
000000DA  0A                   // MajorLinkerVersion = 0x0A (10 版)
000000DB  00                   // MinorLinkerVersion = 0x00 
000000DC  00 02 00 00          // SizeOfCode = 0x0200
000000E0  00 06 00 00          // SizeOfInitializedData = 0x0600
000000E4  00 00 00 00          // SizeOfUninitializedData = 0
000000E8  00 10 00 00          // AddressOfEntryPoint = 0x00001000
000000EC  00 10 00 00          // BaseOfCode = 0x00001000
000000F0  00 00 00 40 01 00 00 00     // ImageBase = 0x00000001_40000000
000000F8  00 10 00 00                 // SectionAlignment = 0x00001000
000000FC  00 02 00 00                 // FileAligment = 0x00000200
00000100  05 00                       // MajorOperationSystemVersion = 0x0005
00000102  02 00                       // MinorOperationSystemVersion = 0x0002
00000104  00 00                       // MajorImageVersion = 0
00000106  00 00                       // MinorImageVersion = 0
00000108  05 00                       // MajorSubsystemVersion = 5
0000010A  02 00                       // MinorSubsystemVersion = 2
0000010C  00 00 00 00                 // Win32VersionValue = 0
00000110  00 50 00 00                 // SizeOfImage = 0x00005000
00000114  00 04 00 00                 // SizeOfHeaders = 0x00000400
00000118  00 00 00 00                 // CheckSum = 0
0000011C  02 00                       // subsystem = 2
0000011E  40 81                       // DllCharacteristics = 0x8140
00000120  00 00 10 00 00 00 00 00     // SizeOfStackReserve = 0x00000000_00100000
00000128  00 10 00 00 00 00 00 00     // SizeOfStackCommit = 0x00000000_00001000
00000130  00 00 10 00 00 00 00 00     // SizeOfHeapReserve = 0x00000000_00100000
00000138  00 10 00 00 00 00 00 00     // SizeOfHeapCommit = 0x00000000_00001000
00000140  00 00 00 00                 // LoaderFlags
00000144  10 00 00 00                 // NumberOfRvaAndSizes
00000148  00 00 00 00 00 00 00 00     // IMAGE_DATA_DIRECTORY
00000150  10 20 00 00 28 00 00 00 00 00 00 00 00 00 00 00
00000160  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000170  00 40 00 00 0C 00 00 00 00 00 00 00 00 00 00 00
00000180  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000190  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000001A0  00 00 00 00 00 00 00 00 00 20 00 00 10 00 00 00
000001B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000001C0  00 00 00 00 00 00 00 00

t.exe  IMAGE_OPTIONAL_HEADER 结构从 0xd8 ~ 0x1c7 共 0xF0 bytes,这个值在 IMAGE_FILE_HEADER 结构的 SizeOfOptionalHeader 域里已经给出。

在上面的可以看出 t.exe 映象是 64 位是 PE+ 文件结构,它的 ImageBase 是 0x00000001_40000000,t.exe 映象的入口点在 ImageBase + AddressOfEntryPoint = 0x00000001_40001000,这个信息可以从上面分析的 .text 节中所描述的信息中验证,IMAGE_OPTIONAL_HEADER 结构的 AddressOfEntryPoint 域的值是 RVA(Relative Virtual Address)值。

上面的 SectionAlinment 域值为 0x1000 是表示映象被加载到 virtual address 以是 0x1000(4K byte)为单位的倍数,也就是加载在 virtual address 的 4K 边界上,例如:t.exe 映象的 .text 节被加载到以 ImageBasevirtual address 为 0x00000001_40000000)为基址的第 1 个 4K 边界上(即:0x00000001_40001000 处),.rdata 节加载到第 2 个 4K 边界上(即:0x00000001_40002000 处)。

FileAlinment 域的值为 0x200 表示执行映象从 0x200 为边界开始加载到 virtual address 上。例如,t.exe 映象中 code 位于文件映象的 0x400 处(0x200 边界上),因此,t.exe 文件映象 code 从 0x400 处开始加载到 virtual address。

因此:.text 节在文件映象中位于 0x400 开始,在 virtual address 空间中是位于 0x00000001_40001000 开始,它说明映象的 0x400 处被加载到 virtual address 000000001_40001000 位置上

6.3.3 t.exe 的 IMAGE_DATA_DIRECTORY 表格

上面所示:从 0x00000148 到 0x000001C7 是被称之为 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

那么,下面我们来看一看 t.exe 映象中使用了哪些表?

00000148  00 00 00 00                   // IMAGE_DATA_DIRECTORY[0]
0000014A  00 00 00 00
00000150  10 20 00 00                   // IMAGE_DATA_DIRECTORY[1] 
00000154  28 00 00 00 

00000158  00 00 00 00                  // IMAGE_DATA_DIRECTORY[2]
0000015A  00 00 00 00
00000160  00 00 00 00                   // IMAGE_DATA_DIRECTORY[3] 
00000164  00 00 00 00 
00000168  00 00 00 00                   // IMAGE_DATA_DIRECTORY[4]
0000016A  00 00 00 00
00000170  00 40 00 00                   // IMAGE_DATA_DIRECTORY[5] 
00000174  0C 00 00 00 

00000178  00 00 00 00                   // IMAGE_DATA_DIRECTORY[6]
0000017A  00 00 00 00
00000180  00 00 00 00                   // IMAGE_DATA_DIRECTORY[7] 
00000184  00 00 00 00 
00000188  00 00 00 00                   // IMAGE_DATA_DIRECTORY[8]
0000018A  00 00 00 00
00000190  00 00 00 00                   // IMAGE_DATA_DIRECTORY[9] 
00000194  00 00 00 00 
00000198  00 00 00 00                   // IMAGE_DATA_DIRECTORY[10]
0000019A  00 00 00 00
000001A0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[11] 
000001A4  00 00 00 00 
000001A8  00 20 00 00                   // IMAGE_DATA_DIRECTORY[12]
000001AA  10 00 00 00

000001B0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[13] 
000001B4  00 00 00 00 
000001B8  00 00 00 00                   // IMAGE_DATA_DIRECTORY[14]
000001BA  00 00 00 00
000001C0  00 00 00 00                   // IMAGE_DATA_DIRECTORY[15] 
000001C4  00 00 00 00                               

我们的示例程序 t.exe 仅仅使用了 3 个 driectory 表格:import tablerelocation table 以及 import address table

import table 的 RVA 是 0x00002010,size 是 0x28,relocation table 的 RVA 是 0x4000,size 是 0x0c, import address table 的 RVA 是 0x2000,size 是 0x10

t.exe  ImageBase 是 0x00000001_40000000,那么 import table 则在 0x00000001_40002010,relocation table 则在 0x00000001_40004000,import address table 则在 0x00000001_40002000。

6.3.3.1 t.exe 所使用的 import table

上面所述,t.exe  import talbe 在 0x00000001_40002010(virtual address),这个 import table 位于 .rdata 节中,从上面的 .rdata 节介绍中看出,.rdata 节位于文件映象中的 0x600 ~ 0x7ff 中共 512 bytes。

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            | -----+        +-------+-----------------+--------------+
                                       +----------------------------+

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

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

下面看看实际中 t.exe 映象中的这几个表格是什么:

RAW DATA #2
  0000000140002000: 48 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00  H ..............
  0000000140002010: 38 20 00 00 00 00 00 00 00 00 00 00 56 20 00 00  8 ..........V ..
  0000000140002020: 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00  . ..............
  0000000140002030: 00 00 00 00 00 00 00 00 48 20 00 00 00 00 00 00  ........H ......
  0000000140002040: 00 00 00 00 00 00 00 00 12 02 4D 65 73 73 61 67  ..........Messag
  0000000140002050: 65 42 6F 78 41 00 55 53 45 52 33 32 2E 64 6C 6C  eBoxA.USER32.dll
  0000000140002060: 00 00                                            ..

  Section contains the following imports:

    USER32.dll
             140002000 Import Address Table
             140002038 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                         212 MessageBoxA

上面已经显示出了全部信息,在 IMAGE_DATA_DIRECTORY 结构里已给出了 Import table 的 RVA 是 0x00002010,即:t.exe 的 import table 在 virtual address 0x00000001_40002010 里,那么现在我们来看一看 import table 里的数据:

0000000140002010: 38 20 00 00           // OriginalFirstThunk(Thunk table pointer
0000000140002014: 00 00 00 00           // TimeDataStamp
0000000140002018: 00 00 00 00           // ForwarderChain
000000014000201A: 56 20 00 00           // Name(import DLL name
0000000140002020: 00 20 00 00           // FirstThunk(Import address table

因此 Thunk table pointer 放在 00000000140002038 里,导入的 DLL 名字放在 0000000140002056 里,Import address table 放在 0x0000000140002000 里。

6.4 t.exe 的节表

IMAGE_NT_HEADER 结构后面紧接着就是 section table(节表)结构, 从 0x1c8 ~ 0x267 共 160 bytes。

这个节表结构在 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 域指出。在前面所述的 t.exe 映象里 NumberOfSections 值是 4 那么表示将有 4 个 sections 存在于映象中。从前的面 dumpbin 工具输出可以得出。

 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.

下面以 .text 节为例,看看 t.exe  .text 是什么。

.text 节的 Name 是 ".text",VirtualSize 是 0x42 bytes,.text 节在 RVA 为 0x00001000 的位置上,section 的分配单位为 0x1000(4K bytes),第 1 个 section 一般都分配在 0x1000 位置上,SizeOfRawData 为 0x200,这是映像文件分配单位。PointerToRawData 为 0x400,说明 .text 节在映像文件的 0x400 处。Characteristics 是 0x60000020,说明 .text 是 executable/readable/code 属性。

猜你喜欢

转载自blog.csdn.net/liuqinglong_along/article/details/52128798