本文是我整理逆向工程学习周报的第一篇,系统梳理了 Windows PE 文件结构、常见文件格式魔数、Win32 GUI 程序基本原理、常用 Windows API 以及 .NET 程序逆向基础。内容适合作为逆向入门的知识点参考,并附有实际分析中的注意要点。
一、PE 文件结构详解
Windows 平台下的可执行文件(如 exe、dll、sys、ocx、com 等)均采用 PE(Portable Executable)文件结构。PE 结构主要由四大部分组成:
- DOS 头:包含 MZ 标识和
e_lfanew字段,用于定位 PE 头。 - PE 头:包含 PE 标识、文件头(
IMAGE_FILE_HEADER)、可选头(IMAGE_OPTIONAL_HEADER)等。 - 节表(Section Table):描述各节区的位置、大小和属性。
- 节区数据:如
.text(代码)、.data(数据)、.rdata(只读数据)等实际内容。
1. DOS 头(DOS Header)
DOS 头是 PE 文件的开头部分,兼容古老的 MS-DOS 系统。关键字段:
| 字段 | 偏移 | 大小 | 说明 |
|---|---|---|---|
e_magic |
0x00 | 2 字节 | 魔数 MZ,十六进制 4D 5A(小端存储) |
e_lfanew |
0x3C | 4 字节 | 指向 PE 头的文件偏移量 |
注意:e_lfanew 的值是相对于文件开头的偏移,不是 RVA。在分析时需结合节表转换为文件偏移或虚拟地址。
2. DOS 存根(DOS Stub)
DOS 存根是一段 16 位汇编代码,当程序在 DOS 环境下运行时,输出 "This program cannot be run in DOS mode" 并退出。该区域在正常情况下没有实际用途,但可以被利用来植入恶意代码或自定义数据。
3. PE 头(PE Header)
在 e_lfanew 指向的位置,首先是 PE 标识:
- PE 标识:
50 45 00 00,即 ASCII 字符串PE\0\0,用于确认这是一个 PE 文件。
紧接着是 IMAGE_FILE_HEADER(文件头),包含 7 个参数,使用 WORD(2 字节)和 DWORD(4 字节)类型:
| 字段 | 大小 | 说明 |
|---|---|---|
Machine |
WORD | 目标 CPU 类型,如 0x14C(x86)、0x8664(x64) |
NumberOfSections |
WORD | 节区数量 |
TimeDateStamp |
DWORD | 编译时间戳 |
PointerToSymbolTable |
DWORD | 符号表偏移(通常为 0) |
NumberOfSymbols |
DWORD | 符号数量(通常为 0) |
SizeOfOptionalHeader |
WORD | 可选头大小 |
Characteristics |
WORD | 文件属性标志,如可执行、DLL 等 |
随后是 IMAGE_OPTIONAL_HEADER(可选头),尽管名字叫可选,但对于 PE 文件来说实际上是必需的。关键字段包括:
Magic:0x10B(32 位 PE)、0x20B(64 位 PE+)AddressOfEntryPoint:程序入口点 RVAImageBase:首选加载基址SectionAlignment/FileAlignment:节区对齐值Subsystem:子系统类型(如 GUI、CUI)DataDirectory:数据目录表,包含导入表、导出表、重定位表等重要结构的 RVA 和大小
4. 节表(Section Table)
节表是一个 IMAGE_SECTION_HEADER 结构数组,每个结构描述一个节区。关键字段:
Name:节区名称(最多 8 字节)VirtualSize:节区在内存中的大小VirtualAddress:节区在内存中的 RVASizeOfRawData:节区在文件中的大小PointerToRawData:节区在文件中的偏移Characteristics:节区属性(如可读、可写、可执行)
在逆向时,经常需要将 RVA 转换为文件偏移(或反之),公式为:
文件偏移 = RVA - VirtualAddress + PointerToRawData
5. 数据目录表(Data Directory)
数据目录表位于可选头末尾,是一个 IMAGE_DATA_DIRECTORY 结构数组,包含 16 个条目,常见的有:
| 索引 | 名称 | 用途 |
|---|---|---|
| 0 | Export Table | 导出函数信息 |
| 1 | Import Table | 导入函数信息 |
| 2 | Resource Table | 资源信息 |
| 5 | Base Relocation Table | 重定位信息 |
| 9 | TLS Table | 线程局部存储信息 |
| 12 | Import Address Table | 导入地址表(IAT) |

导入函数表记录了这个程序运行时要加载哪些DLL,以及要调用DLL里的哪些函数。比如你的程序用了 MessageBox,链接器会把 user32.dll 和 MessageBox 写进导入表。程序启动时,Windows加载器会加载这些DLL,并把函数地址填到导入地址表(IAT)中,程序才能正确调用。
导出函数表是外部访问该函数的“入口”
二、ELF 文件与常见魔数
1. ELF 文件
Linux 平台的可执行文件格式为 ELF(Executable and Linkable Format)。其魔数为 7f 45 4c 46,对应的 ASCII 字符串为 ^?ELF。ELF 文件结构包括 ELF 头、程序头表、节头表等,与 PE 结构类似但有所不同。
2. 文件魔数(Magic Number)
许多文件类型的起始几个字节是固定的,用于快速识别文件类型。常见魔数如下:
| 文件类型 | 魔数 |
|---|---|
| ELF | 7f 45 4c 46 |
| tar | 第 257 字节起为 ustar |
| PE | 0x00004550(即 PE\0\0) |
| Java .class | 0xCAFEBABE |
| PNG | 89 50 4E 47 0D 0A 1A 0A |
| JPEG | FF D8 FF |
| ZIP | 50 4B 03 04 |
| RAR | 52 61 72 21 |
| GZIP | 1F 8B |
在逆向中,通过魔数可以快速识别文件真实类型,即使文件扩展名被篡改。
三、Windows GUI 程序基础
1. WinMain 入口
Windows GUI 程序的入口函数为 WinMain(控制台程序为 main)。原型如下:
1 | int WINAPI WinMain( |
2. 创建窗口的典型流程
定义窗口类:填充 WNDCLASS 或 WNDCLASSEX 结构体,指定窗口过程函数、背景色、图标、光标、类名等。
注册窗口类:调用 RegisterClass 将窗口类注册到系统。
创建窗口:调用 CreateWindow 或 CreateWindowEx,根据窗口类创建实际窗口。
显示窗口:调用 ShowWindow 显示窗口,并调用 UpdateWindow 触发重绘。
消息循环:通过 GetMessage / TranslateMessage / DispatchMessage 不断从消息队列获取消息并分派给窗口过程函数。
3. WNDCLASS 结构体关键字段
1 | typedef struct _WNDCLASS { |
其中 lpfnWndProc 是核心,它负责处理所有发送到该窗口的消息(如鼠标点击、键盘输入、绘制等)。