🎨 颜色标记说明

  • 粉色:特别重要、主线必掌握、优先复盘的内容。
  • 蓝色:核心概念、术语、分类名。
  • 橙色:需要关注的边界、易错点、注意事项。
  • 绿色:解释类内容、帮助理解知识点。

为什么要划分段

程序中的代码、只读常量和可写变量具有不同用途与权限。汇编源文件通过段声明组织内容,链接器再把它们合并到 PE 的相应节区中。

MASM 声明 常见内容 典型属性
.code 机器指令 读、执行
.data 已初始化可写数据 读、写
.data? 未初始化数据 读、写
.const 只读常量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.data
counter dd 1

.data?
buffer db 100h dup(?)

.const
message db "hello", 0

.code
main proc
inc counter
ret
main endp
end main

源代码里的“段”与 PE 文件里的“节”在语境上经常混用,但并非完全等价。最终怎样合并、命名和设置权限由汇编器、编译器与链接器共同决定。

三种数据形式

已初始化数据

1
2
.data
value dd 12345678h

初始值需要保存在文件中,程序加载后映射到内存。

未初始化数据

1
2
.data?
buffer db 1000h dup(?)

只需要记录所需大小,文件中通常不必为每个字节保存实际内容,加载后由系统提供零初始化存储。

只读数据

1
2
.const
format db "%08X", 0

只读属性能降低意外修改常量的风险,但是否真正只读仍要看最终 PE 节区属性和页面保护。

代码段特性

代码段存放 CPU 将要解码执行的机器指令。常见 PE 中,代码节往往名为 .text,并具有可读、可执行属性。

可写且可执行的页面会扩大安全风险。正常程序通常把代码页设为 RX,把数据页设为 RW,遵循“写入”和“执行”尽量分离的原则。

程序入口点

入口点(Entry Point)是模块映像完成必要加载后,系统开始执行模块代码的位置。

在 PE 可选头中,AddressOfEntryPoint 保存的是入口点相对于映像基址的 RVA:

1
入口点虚拟地址 = ImageBase + AddressOfEntryPoint

例如:

1
2
3
ImageBase            = 00400000h
AddressOfEntryPoint = 00001000h
入口点 VA = 00401000h

OEP(Original Entry Point)通常指程序原始入口点。在分析加壳程序时,当前 PE 入口可能先进入壳代码,脱壳后需要定位原始入口。

main、入口点与导出函数

名称 含义
main / WinMain 高级语言层面的用户入口函数
PE Entry Point Loader 完成映射后进入模块的位置
DLL Entry Point DLL 加载、卸载、线程事件时进入的位置
Export Function DLL 主动提供给外部调用的函数

它们可能相互调用,但不能混为一谈。

从文件到执行

程序启动可以先按下面的主线理解:

1
2
3
4
5
6
7
8
9
创建进程
-> 建立虚拟地址空间
-> 映射 EXE 与依赖 DLL
-> 处理重定位
-> 修复导入函数地址
-> 完成运行库和必要初始化
-> 转入 PE 入口点
-> 进入 main 或业务逻辑
-> 返回并结束进程

实际 Windows 加载流程还包含 TLS、异常信息、权限与其他结构,后续 PE 和 Win32 笔记再展开。

逆向定位入口的思路

  1. 从 PE 头读取 AddressOfEntryPoint
  2. 计算实际虚拟地址并在调试器中下断点。
  3. 判断入口代码属于编译器启动代码、壳代码还是业务代码。
  4. 关注调用关系、字符串引用和参数准备。
  5. 结合符号或 IDA 交叉引用逐步定位 main

入口点只是分析起点,不一定是最值得阅读的业务起点。

学习检查清单

检查项 状态
能区分 .data.data?.const.code 待复盘
能说明代码页为何通常不应同时可写 待复盘
能用 ImageBase + RVA 计算入口点 VA 待复盘
能区分 main、PE 入口点和导出函数 待复盘
能复述程序从映射到进入业务逻辑的主线 待复盘