抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Windows PE

  • 可执行文件(Executable file)指的是可以由操作系统进行加载执行的文件
  • 可执行文件的格式:
    • Windows 平台
      PE(Portable Executable)文件结构
    • Linux 平台
      ELF(Executable and Linking Format)文件结构

PE 文件结构

PE 文件的整体结构

PE 文件主要结构

PE 文件结构体宽度

结构体 宽度(字节)
IMAGE_DOS_HEADER 64
IMAGE_FILE_HEADER 20
INAGE_OPTIONAL_HEADER32 244
IMAGE_SECTION_HEADER 40

PE文件的两种状态

PE文件的两种状态

  • PE文件在运行前(静态,存储在磁盘上)和运行时(动态,运行在内存中)的格式是有差异的,这种差异对于我们理解PE文件是如何执行的来说很重要。
  • 我们在之前的文件分析过程中实际上所看到的是静态的内容,其大小是要根据FileAlignment的值进行文件对齐的,但是在运行时则整体按照扩展PE头的成员SectionAlignment的值进行内存对齐,默认情况下该值为0x1000:

DOS头结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;

  • e_magic 为DOS可执行文件标识符,占用2字节,值为0x5A4D
  • e_lfanew 保存了PE头的起始位置

标准PE头

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

_IMAGE_NT_HEADERS

  • Signature: PE标识, 值为0x00004550
  • FileHeader: 文件头
  • OptionalHeader: 扩展头

文件头(标准PE头)

1
2
3
4
5
6
7
8
9
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;

_IMAGE_FILE_HEADER

  1. Machine: 该字段为WORD类型,占用2字节,该字段标识可执行文件的目标CPU类型
  2. NumberOfSections: 该字段为WORD类型,占用2字节,该字段标识PE文件的节区个数
  3. TimeDateStamp: 该字段表示编译器填写的时间戳与文件属性中(创建时间、修改时间无关),这个值是自1970年1月1日以来用格林威治时间计算的秒数
  4. PointerToSymbolTable: 该字段很少使用,调试相关
  5. NumberOfSymbols: 该字段很少使用,调试相关
  6. SizeOfOptionalHeader: 该字段为WORD类型,占用2字节;该字段指定IMAGE_OPTIONAL_HEADER结构大小(32位PE文件:0xEO 64位PE文件:0xF0)
  7. Characteristics: 该字段为WORD,占用2字节;该字段指定文件属性

Machine 字段取值范围

宏定义 意义
IMAGE_FILE_MACHINE_I386 0x014C Intel
IMAGE_FILE_MACHINE_ALPHA 0x0184 DEC Alpha
IMAGE_FILE_MACHINE_IA64 0x200 Intel(64-bit)
IMAGE_FILE_MACHINE_AXP64 0x0284 DEC Alpha(64-bit)
IMAGE_FILE_MACHINE_AMD64 0x8664 AMD64 (K8)

Characteristics 字段取值范围

数据位 宏定义 为1时的含义
0 IMAGE_FILE_RELOCS_STRIPPED 0x0001 文件中不存在重定位信息
1 IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 文件是可执行的
2 IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 不存在行信息
3 IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 不存在符号信息
4 IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 调整工作集
5 IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 应用程序可处理大于2GB的地址
6 此标志位保留
7 IMAGE_FILE_BYTES_REVERSED_LO 0x0080 小尾方式
8 IMAGE_FILE_32BIT_MACHINE 0x0100 只在32平台运行
9 IMAGE_FILE_DEBUG_STRIPPED 0x0200 不包含调试信息
10 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 不能从可移动盘运行
11 IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 不能从网络运行
12 IMAGE_FILE_SYSTEM 0x1000 系统文件(驱动程序),不能直接运行
13 IMAGE_FILE_DLL 0x2000 DLL文件
14 IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 文件不能在多处理器计算机上运行
15 IMAGE_FILE_BYTES_REVERSED_HI 0x8000 大尾方式
Characteristics 计算方式 C++
1
2
3
4
例如:0102
0000 0001 0000 0010

下标是1和8位为1;表示文件是一个可执行文件,只在32平台运行

可选头(扩展头)

扩展头(32位) C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; // 32位PE程序:10B 64位PE程序:20B 重要
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; // PE文件自身的版本号
WORD MinorImageVersion; // PE文件自身的版本号
WORD MajorSubsystemVersion; // 运行所需子系统版本号
WORD MinorSubsystemVersion; // 运行所需子系统版本号
DWORD Win32VersionValue; // 子系统版本值,必须位0
DWORD SizeOfImage; // 内存中整个PE文件的映射的尺寸,可比实际的值大,必须是SectionAlignment整数倍 重要
DWORD SizeOfHeaders; // 所有头 + 节表按照文件对齐后的大小,否则加载会出错 重要
DWORD CheckSum; // 校验和,一些系统文件有要求用来判断文件是否被修改 重要
WORD Subsystem; // 子系统 驱动程序(1)图形界面(2)控制台、DLL(3)
WORD DllCharacteristics; // 文件特性 不是针对DLL文件的
DWORD SizeOfStackReserve; // 初始化时保留的栈大小
DWORD SizeOfStackCommit; // 初始化实际提交的大小
DWORD SizeOfHeapReserve; // 初始化保留的堆大小
DWORD SizeOfHeapCommit; // 初始化实际提交的大小
DWORD LoaderFlags; // 调试相关
DWORD NumberOfRvaAndSizes; // 目录项目数
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

  • 程序真正入口:ImageBase 内存镜像基址 + AddressOfEntryPoint 程序入口

_IMAGE_SECTION_HEADER(节表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;

空白区域添加代码

  • 构造跳转地址公式
    要跳转的地址 - E8指令当前的地址 - 5

新增节

新增节的步骤

  1. 判断是否有足够的空间,可以添加一个节表
  2. 在节表中新增一个成员
  3. 修改PE头中节的数量
  4. 修改SizeOfImage的大小
  5. 在原有数据的最后,新增一个节的数据(内存对齐的整数倍)
  6. 修正新增节表的属性

评论