0212、Windows 逆向入门:C++ 对象模型逆向
这一部分专门复盘 C++ 对象在二进制里的样子。源码里的类、继承、多态、虚函数,进入反汇编后会变成对象地址、成员偏移、虚表指针、间接调用和构造析构序列。
1、内容范围
| 小节 | 内容 | 复盘点 |
|---|---|---|
| 16.7 - 16.9 | 对象模型与 thiscall | 对象存储、成员偏移、this 指针 |
| 17.0 - 17.3 | 对象传参返回 | 调用约定、类对象返回、编译器优化 |
| 17.4 - 17.9 | 构造析构与拷贝 | 构造触发、堆对象、全局对象、深拷贝 |
| 18.0 - 18.2 | 构造限制与常成员 | 默认参数、静态成员、const 成员函数 |
| 18.3 - 18.7 | 继承对象模型 | 单继承、多继承、菱形继承 |
| 18.8 - 19.4 | 多态与虚表 | 虚函数指针、虚表、多态调用、纯虚对象 |
| 19.5 - 19.6 | 模板使用与特化 | 模板实例化、特化和代码拷贝 |
2、对象模型
普通 C++ 对象通常只保存成员变量;成员函数在代码区。调用成员函数时会传入 this 指针。
1 | 对象内存: |
3、thiscall 与成员偏移
x86 thiscall 中,ECX 常保存 this。成员访问可能表现为:
1 | mov eax, [ecx+4] |
还原思路:
ecx很可能是对象地址。+4是成员偏移。- 访问宽度决定成员大小。
4、静态成员
静态成员不属于单个对象,通常像全局变量一样保存。静态成员函数没有 this 指针。
5、对象传参和返回
小对象可能按值传递;大对象或带复杂构造的对象可能通过隐藏指针传递/返回。优化等级不同,表现也不同。
| 场景 | 逆向观察 |
|---|---|
| 引用参数 | 传对象地址 |
| 指针参数 | 传对象地址 |
| 值参数 | 可能复制对象 |
| 返回对象 | 可能使用隐藏返回指针 |
6、构造函数与析构函数
构造函数负责建立对象有效状态,析构函数负责释放资源。
逆向观察点:
| 现象 | 说明 |
|---|---|
| 初始化成员 | 给对象偏移写入初值 |
| 设置虚表指针 | 有虚函数的对象常见 |
| 调用基类构造 | 继承对象先构造基类 |
| 析构顺序相反 | 先析构派生类,再析构基类 |
7、继承布局
单继承通常是基类子对象在前,派生类成员在后。多继承会出现多个基类子对象,指针转换可能需要调整地址。
| 继承类型 | 复盘重点 |
|---|---|
| 单继承 | 基类字段 + 派生字段 |
| 多继承 | 多个基类区域和多个虚表可能出现 |
| 菱形继承 | 重复基类或虚继承布局 |
8、虚表与多态
有虚函数的类通常含有虚表指针。虚表里保存虚函数地址。
1 | 对象 -> vptr -> vtable -> virtual function address |
动态绑定常表现为:
1 | mov eax, [ecx] ; 取虚表指针 |
9、纯虚函数与抽象类
纯虚函数让类成为抽象类。逆向时可能看到纯虚函数占位、运行库 purecall 相关行为。
10、模板与特化
模板是编译期生成代码。不同类型实例化后,可能产生多份相似函数。特化会让某些类型走独立实现。
11、学习检查清单
| 检查项 | 状态 |
|---|---|
| 能说明对象成员和成员函数分别在哪里 | 待复盘 |
能根据 this + offset 推测成员访问 |
待复盘 |
| 能识别构造函数中设置虚表指针的行为 | 待复盘 |
| 能画出单继承和多继承的大致对象布局 | 待复盘 |
| 能解释虚表间接调用和多态的关系 | 待复盘 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ruiqy~!






