012、继承和多重继承
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:虚函数 · 下一章:异常处理
基类子对象派生类对象通常内含一个或多个完整的基类子对象。访问控制只影响源码编译检查,不会把基类的私有成员从派生对象内存中删除。
单继承的概念布局:
12345Derived object+0x00 Base subobject Base fields Base vfptr(如有)+0x20 Derived fields
把 Derived* 转为主基类 Base* 时,地址可能不变;转到位于非零偏移的其他基类时,需要调整指针。
构造与析构顺序12构造:基类 -> 派生类成员 -> 派生类函数体析构:派生类函数体 -> 派生类成员 -> 基类
多重继承时,普通基类通常按声明顺序构造,逆序析构。虚基类由最派生类负责初始化,顺序还需结合 ABI 与源码声明。
成员访问与基类偏移如果某成员函数把 ...
011、虚函数
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:构造与析构 · 下一章:继承
动态绑定虚函数让“通过基类指针或引用调用接口”在运行时选择实际派生类实现。常见 ABI 使用:
对象中的 虚表指针(vfptr/vptr)。
只读数据区中的 虚函数表(vftable/vtable)。
表内按槽位排列的函数地址。
典型虚调用可抽象为:
12mov rax, qword ptr [rcx] ; 读取对象的虚表指针call qword ptr [rax+slot] ; 调用某个虚表槽位
RCX 在 Windows x64 中通常是 this,但仍应结合调用约定验证。
虚表与对象通常同一动态类型的对象共享一张虚函数表,每个对象只保存指向该表的指针。虚表中的函数代码不复制到对象里。
12345object+0x00 vfptr ------> vtable+0x08 field_a ...
010、构造函数和析构函数
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:结构体和类 · 下一章:虚函数
机器码里没有“构造”关键字构造函数和析构函数最终仍然是函数。它们的特殊性来自编译器自动插入调用,并按照 C++ 对象生命周期规则组织执行顺序。
识别构造函数常见证据:
对象首次使用前被调用。
首参数是对象地址。
初始化多个固定偏移字段。
调用基类或成员对象构造函数。
含虚函数时向对象写入虚表地址。
识别析构函数常见证据:
对象作用域结束、异常清理或 delete 前被调用。
释放对象持有的堆、句柄、锁等资源。
调用成员对象和基类析构函数。
某些实现中会改写虚表指针或进入删除析构包装函数。
“第一次成员调用就是构造、最后一次成员调用就是析构”只能作为线索,不能单独作为结论。
初始化顺序派生对象的构造顺序通常是:
1234虚基类(如有)普通基类成员对象(按声明顺序)当前类构造函数体
析构顺序与之相反。成员初始化列表的书写顺序不能改变成员对 ...
009、结构体和类
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:数组与指针 · 下一章:构造与析构
反汇编中的结构体与类源码层面,struct 和 class 的默认访问权限不同;生成机器码后,public、private、protected 通常不再形成运行时边界。反汇编主要看到:
一块对象内存。
固定偏移的数据成员。
接收对象地址的成员函数。
可能存在的虚表、基类子对象和编译器辅助数据。
因此恢复类型时更适合先写“候选结构体”,再逐步确认它是否对应 C++ 类。
对象内存布局没有继承和虚函数时,对象通常按成员声明顺序安排,但会受内存对齐影响:
12345struct Record { char flag; // offset 0 int value; // 常见 offset 4 short code; // 常见 offset 8};
中间空出的字节是填充,结构体末尾也可能补齐到 ...
008、数组和指针的寻址
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:变量与内存 · 下一章:结构体和类
数组的内存本质数组是一段连续的同类型元素。第 i 个元素地址为:
1element_address = base + i * sizeof(element)
x86/x64 的比例因子寻址非常适合表达这个公式:
1mov eax, dword ptr [base+index*4]
比例因子只能直接取 1、2、4、8,其他元素宽度常由 IMUL、LEA、移位和加法组合计算。
局部数组与全局数组局部数组通常基于栈帧地址连续访问,全局数组通常基于模块静态地址连续访问。识别时观察:
多次访问是否共享同一个基址。
索引是否按固定元素宽度增长。
相邻元素是否使用一致的操作宽度。
是否存在初始化循环或批量复制。
连续内存不一定就是数组,也可能是结构体、对象或临时缓冲区,仍需结合“元素类型是否一致”判断。
数组作为函数参数C/C ...
007、变量在内存中的位置和访问方式
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:函数原理 · 下一章:数组与指针
本章主线反汇编里没有“局部变量”“全局变量”这样的源码标签,只有地址和访问动作。判断变量身份时要回答三个问题:
地址从哪里来?
这块内存何时创建、何时失效?
哪些函数能够访问它?
变量类型的恢复,本质上是对存储区域、生命周期和访问宽度的联合推断。
全局变量全局变量通常位于模块映像的静态数据区:
已初始化且可写的数据常在 .data。
未显式初始化的数据可能位于零初始化区域。
只读常量常在 .rdata,但具体节名取决于编译器和链接器。
x86 中常见绝对地址访问,x64 中常见 RIP 相对寻址:
12mov eax, dword ptr [global_value]mov dword ptr [rip+disp32], eax
分析时把 VA 转成 RVA,再判断地址属于哪个节。ASLR 会改变模块基址,但同一映像内的 RVA 通 ...
006、函数的工作原理
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:流程控制 · 下一章:变量与内存
CALL 与 RETCALL 保存下一条指令地址并转移控制流;RET 从栈中取得返回地址。
12CALL ≈ push 返回地址 + jump 目标RET ≈ 从栈顶恢复下一条执行地址
直接相对调用常见 E8 rel32,间接调用常见 FF /2,后者经常对应函数指针、IAT 或虚函数。
经典 x86 栈帧123push ebpmov ebp, espsub esp, local_size
典型布局:
位置
常见含义
[ebp]
保存的上一层 EBP
[ebp+4]
返回地址
[ebp+8]
第一个栈参数
[ebp-4]
局部变量或临时空间
函数尾声常见:
123mov esp, ebppop ebpret
或 leave; ret。
参数和局部变量的偏移只是传统未优化栈帧的常见形式,不是所有函数都必须 ...
005、流程控制语句的识别
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:表达式求值 · 下一章:函数原理
先画基本块,再猜语法基本块是一段单入口、单出口的连续指令。控制流图以基本块为节点、跳转关系为边。
反汇编中真正存在的是比较和跳转;if、switch、for 是对控制流的高级解释。
单分支 if12if (x > 10) work();
常见形态:条件不成立时跳过主体。
1234cmp eax, 10jle short end_ifcall workend_if:
编译器也可能反转条件,因此不要把某一个跳转助记符机械翻译成源码条件。
if-else12345 条件 / \分支A 分支B \ / 汇合点
其中一个分支末尾通常需要无条件跳转,避免继续落入另一分支。
多条件与判定树连续的 if/else if 可能表现为线性比较,也可能被优化成更平衡的判定树。恢复时应先列出每条边的条件,再组合成高 ...
004、观察各种表达式的求值过程
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:启动函数 · 下一章:流程控制
从数据流恢复表达式机器代码不会保留括号和源码排版。恢复表达式时要跟踪每个值的来源、运算和写回目标。
1234mov eax, [a]imul eax, [b]add eax, [c]mov [result], eax
候选语义是 result = a * b + c,但还需结合宽度、符号和调用上下文确认。
算术与赋值
源码语义
常见指令
加减
ADD、SUB、LEA
乘法
IMUL、MUL、移位与加法组合
除法
IDIV、DIV、乘倒数优化
取负
NEG
自增自减
INC/DEC 或 ADD/SUB 1
复合赋值通常直接对原位置读改写:
1add dword ptr [value], 5
LEA 不访问其计算出的目标内存,常被编译器用于整数运算:
1lea eax, [ecx+ecx*4] ; eax = e ...
003、认识启动函数,找到用户入口
🎨 颜色标记说明
粉色:特别重要、主线必掌握、优先复盘的内容。
蓝色:核心概念、术语、分类名。
橙色:需要关注的边界、易错点、注意事项。
绿色:解释类内容、帮助理解知识点。
返回:全书总览 · 上一章:基本数据类型 · 下一章:表达式求值
PE 入口不等于 mainPE 可选头中的 AddressOfEntryPoint 指向映像的入口 RVA:
1入口虚拟地址 = ImageBase + AddressOfEntryPoint
Windows 把控制权交给入口后,通常先执行 C/C++ 运行库启动代码,而不是立刻进入用户编写的 main。
逆向时要区分加载器入口、CRT 启动函数和用户入口三个层次。
CRT 启动代码负责什么典型职责包括:
初始化安全 Cookie 和运行库状态。
初始化全局及静态 C++ 对象。
准备命令行参数和环境变量。
选择并调用 main、wmain、WinMain 或 wWinMain。
处理返回值、退出函数和清理逻辑。
12345PE 入口 → CRT 初始化 → 全局构造 → 用户入口 → 全局析构与退出
寻找 ma ...














