Windows PE 文件结构详解:从入口到执行的旅程
在 Windows 平台上,几乎所有的可执行文件(如 .exe
、.dll
)都遵循一种统一的格式:PE(Portable Executable)文件格式。它是微软为 Windows 操作系统设计的一种文件结构,基于 COFF(Common Object File Format)扩展而来,广泛用于程序加载、调试、反汇编、病毒分析等领域。
本文将深入解析 PE 文件的结构组成,结合实际例子,帮助你理解一个 Windows 可执行文件是如何从磁盘加载到内存并开始执行的。
一、PE 文件的整体结构概览
PE 文件结构可以分为以下几个主要部分:
- DOS Header(MS-DOS 头)
- DOS Stub(DOS 程序段)
- PE Header(PE 头)
- Signature(PE 签名)
- File Header(文件头)
- Optional Header(可选头)
- Section Table(节表)
- Section Bodies(节内容)
如下图所示:
+------------------+
| DOS Header |
+------------------+
| DOS Stub |
+------------------+
| PE Header |
| - Signature |
| - File Header |
| - Optional Hdr |
+------------------+
| Section Table |
+------------------+
| Section Bodies |
+------------------+
二、各部分结构详解
1. DOS Header(IMAGE_DOS_HEADER)
PE 文件的开头是一个 DOS 头,主要用于兼容旧版 DOS 程序。它的第一个字段是 e_magic
,值为 0x5A4D
(即 ASCII 的 "MZ"),表示这是一个 DOS 可执行文件。
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
...
LONG e_lfanew; // Offset to PE Header
} IMAGE_DOS_HEADER;
其中最关键的是 e_lfanew
,它指向 PE Header 的偏移位置。
2. DOS Stub
这是一个小型的 DOS 程序段,通常显示类似 “This program cannot be run in DOS mode.” 的提示信息。它在现代 Windows 中没有实际用途,但仍保留以兼容性为目的。
3. PE Header(IMAGE_NT_HEADERS)
PE Header 是整个文件的核心部分,包含三个子结构:
a. Signature
固定为 0x00004550
,即 "PE\0\0",标志着这是一个 PE 文件。
b. File Header(IMAGE_FILE_HEADER)
描述文件的基本信息,如机器类型、节数量、时间戳等。
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
...
} IMAGE_FILE_HEADER;
c. Optional Header(IMAGE_OPTIONAL_HEADER)
虽然叫 “Optional”,但对于可执行文件来说是必须的。它包含程序入口点、内存布局、DLL 特性等信息。
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
DWORD AddressOfEntryPoint;
DWORD ImageBase;
...
} IMAGE_OPTIONAL_HEADER;
其中 AddressOfEntryPoint
是程序的入口地址,ImageBase
是程序加载到内存的默认地址。
三、节表与节内容(Section Table & Bodies)
PE 文件的代码、数据、资源等内容都被划分为多个节(Section),每个节在节表中有一个描述项。
常见的节包括:
.text
:代码段.data
:初始化数据.rdata
:只读数据(如字符串、导入表).rsrc
:资源段(如图标、对话框).reloc
:重定位信息
每个节由 IMAGE_SECTION_HEADER
描述:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[8];
DWORD VirtualSize;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
...
} IMAGE_SECTION_HEADER;
四、实际案例分析:用 C 读取 PE 文件结构
下面是一个简单的 C 程序片段,用于读取并打印一个 PE 文件的入口点地址:
#include <stdio.h>
#include <windows.h>int main() {
FILE *fp = fopen("sample.exe", "rb");
IMAGE_DOS_HEADER dosHeader;
fread(&dosHeader, sizeof(dosHeader), 1, fp);
fseek(fp, dosHeader.e_lfanew, SEEK_SET);DWORD peSignature;
fread(&peSignature, sizeof(peSignature), 1, fp);IMAGE_FILE_HEADER fileHeader;
fread(&fileHeader, sizeof(fileHeader), 1, fp);IMAGE_OPTIONAL_HEADER optionalHeader;
fread(&optionalHeader, sizeof(optionalHeader), 1, fp);printf("Entry Point: 0x%X\n", optionalHeader.AddressOfEntryPoint);
fclose(fp);
return 0;
}
这段代码展示了如何从磁盘读取 PE 文件结构,并提取入口点信息。
五、PE 文件在加载时的流程
当 Windows 加载一个 PE 文件时,主要执行以下步骤:
- 读取 DOS Header,定位 PE Header
- 解析 PE Header,获取节表和入口点
- 将各节映射到内存中
- 执行入口点代码
Windows 的加载器会根据 ImageBase
和 AddressOfEntryPoint
将程序加载到合适的内存位置,并开始执行。
六、PE 文件在安全领域的应用
PE 文件结构在信息安全领域有广泛应用:
- 恶意软件分析:通过解析 PE 文件结构识别病毒行为。
- 逆向工程:使用工具(如 IDA Pro、Ghidra)分析节内容和入口点。
- 代码注入与钩子技术:修改
.text
或.data
节实现功能劫持。 - 签名校验与完整性检测:验证 PE 文件是否被篡改。
PE 文件结构是 Windows 平台程序的基础,理解它不仅有助于开发,还能在安全分析、逆向工程等领域发挥重要作用。希望本文能帮助你建立对 PE 文件的清晰认知。
如果你对 PE 文件的加载过程、节内容解析或安全应用感兴趣,欢迎留言交流或关注后续文章。