🎨 颜色标记说明

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

返回:全书总览 · 上一章:函数原理 · 下一章:数组与指针

本章主线

反汇编里没有“局部变量”“全局变量”这样的源码标签,只有地址和访问动作。判断变量身份时要回答三个问题:

  1. 地址从哪里来?
  2. 这块内存何时创建、何时失效?
  3. 哪些函数能够访问它?

变量类型的恢复,本质上是对存储区域、生命周期和访问宽度的联合推断。

全局变量

全局变量通常位于模块映像的静态数据区:

  • 已初始化且可写的数据常在 .data
  • 未显式初始化的数据可能位于零初始化区域。
  • 只读常量常在 .rdata,但具体节名取决于编译器和链接器。

x86 中常见绝对地址访问,x64 中常见 RIP 相对寻址:

1
2
mov eax, dword ptr [global_value]
mov dword ptr [rip+disp32], eax

分析时把 VA 转成 RVA,再判断地址属于哪个节。ASLR 会改变模块基址,但同一映像内的 RVA 通常保持稳定。

栈上的局部变量

局部变量常位于线程栈中,传统未优化函数可能使用:

1
2
mov eax, dword ptr [ebp-4]
mov dword ptr [rsp+30h], ecx

典型特征:

  • 地址相对当前栈帧计算。
  • 生命周期通常受函数或语句块约束。
  • 同一槽位可能在不同阶段复用给多个源码变量。
  • 优化后变量可能只存在于寄存器中,根本没有内存地址。

“看到 [ebp-x] 就命名为一个独立变量”并不可靠。编译器可能复用栈槽,也可能把临时值、保存寄存器和对齐空间放在同一区域。

局部静态变量

局部静态变量具有局部作用域,但存储期贯穿整个进程。反汇编中通常表现得更像带初始化保护的全局变量:

1
2
3
4
检查初始化标志
若尚未初始化:构造对象或写入初值
设置初始化完成状态
返回或继续使用静态数据

C++11 以后,函数内静态对象的首次初始化还需要满足线程安全要求,可能出现运行库守卫函数或同步逻辑。具体形式依编译器版本而变。

堆变量

堆变量的地址通常来自分配函数:

  • C:malloc / calloc / realloc
  • C++:operator new / operator new[]
  • Windows:HeapAlloc / VirtualAlloc 等。

逆向时要追踪完整生命周期:

1
分配 -> 保存指针 -> 读写 -> 传递所有权 -> 释放

freedeleteHeapFreeVirtualFree 并非可以随意混用。识别分配器与释放器是否配对,是发现内存错误的重要步骤。

寄存器中的变量

优化构建会尽量让热点数据留在寄存器:

  • 参数进入函数后可能一直不落栈。
  • 循环计数器常驻寄存器。
  • 多个源码变量可共享同一寄存器的不同时段。
  • 一个源码变量也可能在寄存器和栈槽之间迁移。

因此变量恢复应该使用“值的定义与使用链”,不能只靠地址命名。

访问宽度反推类型

指令访问 候选宽度
byte ptr 1 Byte
word ptr 2 Byte
dword ptr 4 Byte
qword ptr 8 Byte

再结合 MOVSXMOVZX、比较方式、算术指令和后续 API 原型判断有无符号及语义类型。

变量恢复记录表

字段 记录内容
地址来源 模块、栈、堆、寄存器或 TLS
访问宽度 1/2/4/8 Byte 或向量宽度
首次定义 首次被赋值的位置
使用范围 读取、修改和传递位置
生命周期 创建到最后一次有效使用
候选类型 整数、指针、对象、数组等
置信度 已确认 / 高概率 / 待验证

学习检查清单

检查项 状态
能根据节区和寻址方式识别全局数据 待复盘
能区分栈变量与寄存器临时值 待复盘
能说明局部静态变量的初始化守卫 待复盘
能沿分配与释放追踪堆对象生命周期 待复盘
能结合访问宽度和符号扩展推断候选类型 待复盘