转自:http://www.mouseos.com/assembly/06.html
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 中定义为:
|
#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 的偏移量是 0x000000C0,IMAGE_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 节被加载到以 ImageBase(virtual 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 table、relocation 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 实际上只有一个域 AddressOfData,AddressOfData 指向一个 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 属性。