C++逆向学习(四) 类
字数 1522 2025-08-05 08:18:36

C++逆向工程:类结构分析详解

1. 类的基本结构分析

1.1 this指针机制

在C++逆向分析中,识别this指针是理解类成员函数的关键:

  • Linux环境下(g++编译):this指针通过rdi寄存器传递
  • Windows环境下(MSVC编译):this指针通过rcx寄存器传递

识别特征

  • 如果一个函数在rcx/rdi寄存器未初始化前就被使用,很可能是类的成员函数
  • 成员函数第一个参数通常是this指针

1.2 类成员变量布局

类成员变量在内存中按照声明顺序排列,考虑对齐规则:

class base {
public:
    int a;      // 4字节
    double b;   // 8字节
    // 总大小:16字节(考虑对齐)
};

内存布局:

+---+---+---+---+---+---+---+---+
|       a (int)        | padding |
+---+---+---+---+---+---+---+---+
|          b (double)           |
+---+---+---+---+---+---+---+---+

2. 构造函数与析构函数分析

2.1 构造函数特征

构造函数的关键识别特征:

  1. 虚表指针初始化

    • 构造函数首先将虚表地址赋值给类实例的首字段
    • 这是识别构造函数的最重要标志
  2. 初始化顺序

    • 基类构造函数先于派生类构造函数执行
    • 成员变量按照声明顺序初始化

2.2 析构函数特征

析构函数的关键识别特征:

  1. 虚表指针重置

    • 析构函数也会重新赋值虚表指针
    • 确保在析构过程中调用虚函数时行为正确
  2. 调用顺序

    • 派生类析构函数先于基类析构函数执行
    • 与构造函数顺序相反

3. 虚函数机制分析

3.1 虚表结构

虚表(vtable)是虚函数调用的核心:

  1. 虚表指针

    • 位于类实例的首地址
    • 指向虚函数表
  2. 虚表内容

    • 按声明顺序存放虚函数指针
    • 虚表前有typeinfo信息(RTTI)
// 获取虚表指针示例
int** vtable = *(int***)object_ptr;

3.2 虚函数调用过程

虚函数调用在汇编层面的表现:

  1. 通过对象指针获取虚表指针
  2. 从虚表中获取目标函数地址
  3. 调用该函数,传递this指针
; 示例x86_64汇编
mov     rax, [rdi]      ; 获取虚表指针
mov     rax, [rax]      ; 获取第一个虚函数地址
call    rax             ; 调用虚函数

4. 特殊构造场景分析

4.1 全局/静态对象构造

全局和静态对象的构造由编译器生成的代理函数处理:

  1. 构造时机

    • 在main函数执行前完成构造
    • 通过initterm系列函数调用构造代理
  2. 识别方法

    • 查找atexit调用点(析构注册)
    • 构造代理函数通常在附近

4.2 构造/析构代理函数

  1. 构造代理函数

    • 调用实际构造函数
    • 注册析构函数到atexit
  2. 析构代理函数

    • 通过atexit注册
    • 在程序退出时调用

5. 继承关系分析

5.1 单继承

简单继承的内存布局:

  • 基类成员在前
  • 派生类成员在后
  • 只有一个虚表指针

5.2 多重继承

多重继承的复杂情况:

class Derived : public Base1, public Base2 {
    // ...
};

内存布局:

  1. Base1部分(包含虚表指针)
  2. Base2部分(包含虚表指针)
  3. Derived新增成员

5.3 虚继承(菱形继承)

虚继承的特殊内存布局:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

关键特征:

  1. 虚基类(A)的数据被放在公共位置
  2. 派生类(B/C)保存指向虚基类的偏移量
  3. 避免了数据冗余

逆向识别要点

  • 查找额外的偏移量字段
  • 注意非直接访问基类成员的情况

6. RTTI(运行时类型识别)分析

RTTI相关数据结构:

  1. typeinfo

    • 位于虚表前一个位置
    • 包含类型名称、继承关系等信息
  2. RTTICompleteObjectLocator

    • 通过虚表指针-1的位置访问
    • 包含完整的类型信息
// 获取RTTI信息示例
int** vtable = *(int***)object_ptr;
RTTICompleteObjectLocator* rtti = *(RTTICompleteObjectLocator**)(vtable[-1]);

7. 逆向分析技巧总结

  1. 识别类结构的线索

    • 虚表指针初始化 → 构造函数
    • 虚表指针重置 → 析构函数
    • 特定寄存器作为第一个参数 → 成员函数
  2. 工具使用建议

    • 在IDA中添加类/结构体定义
    • 利用交叉引用追踪虚表使用
    • 关注atexit调用点找到全局对象
  3. 调试技巧

    • atexit下断点定位全局对象构造
    • 跟踪虚表指针变化理解对象生命周期

8. 推荐学习资源

  1. 《深度探索C++对象模型》- Stanley B. Lippman

    • 详细讲解C++对象内存布局的历史和设计考量
  2. 《逆向工程核心原理》- 李承远

    • 包含C++逆向分析的实用技巧
  3. IDA Pro官方文档

    • 学习如何有效使用IDA进行C++逆向分析
C++逆向工程:类结构分析详解 1. 类的基本结构分析 1.1 this指针机制 在C++逆向分析中,识别this指针是理解类成员函数的关键: Linux环境下(g++编译):this指针通过rdi寄存器传递 Windows环境下(MSVC编译):this指针通过rcx寄存器传递 识别特征 : 如果一个函数在rcx/rdi寄存器未初始化前就被使用,很可能是类的成员函数 成员函数第一个参数通常是this指针 1.2 类成员变量布局 类成员变量在内存中按照声明顺序排列,考虑对齐规则: 内存布局: 2. 构造函数与析构函数分析 2.1 构造函数特征 构造函数的关键识别特征: 虚表指针初始化 : 构造函数首先将虚表地址赋值给类实例的首字段 这是识别构造函数的最重要标志 初始化顺序 : 基类构造函数先于派生类构造函数执行 成员变量按照声明顺序初始化 2.2 析构函数特征 析构函数的关键识别特征: 虚表指针重置 : 析构函数也会重新赋值虚表指针 确保在析构过程中调用虚函数时行为正确 调用顺序 : 派生类析构函数先于基类析构函数执行 与构造函数顺序相反 3. 虚函数机制分析 3.1 虚表结构 虚表(vtable)是虚函数调用的核心: 虚表指针 : 位于类实例的首地址 指向虚函数表 虚表内容 : 按声明顺序存放虚函数指针 虚表前有typeinfo信息(RTTI) 3.2 虚函数调用过程 虚函数调用在汇编层面的表现: 通过对象指针获取虚表指针 从虚表中获取目标函数地址 调用该函数,传递this指针 4. 特殊构造场景分析 4.1 全局/静态对象构造 全局和静态对象的构造由编译器生成的代理函数处理: 构造时机 : 在main函数执行前完成构造 通过 initterm 系列函数调用构造代理 识别方法 : 查找 atexit 调用点(析构注册) 构造代理函数通常在附近 4.2 构造/析构代理函数 构造代理函数 : 调用实际构造函数 注册析构函数到 atexit 析构代理函数 : 通过 atexit 注册 在程序退出时调用 5. 继承关系分析 5.1 单继承 简单继承的内存布局: 基类成员在前 派生类成员在后 只有一个虚表指针 5.2 多重继承 多重继承的复杂情况: 内存布局: Base1部分(包含虚表指针) Base2部分(包含虚表指针) Derived新增成员 5.3 虚继承(菱形继承) 虚继承的特殊内存布局: 关键特征: 虚基类(A)的数据被放在公共位置 派生类(B/C)保存指向虚基类的偏移量 避免了数据冗余 逆向识别要点 : 查找额外的偏移量字段 注意非直接访问基类成员的情况 6. RTTI(运行时类型识别)分析 RTTI相关数据结构: typeinfo : 位于虚表前一个位置 包含类型名称、继承关系等信息 RTTICompleteObjectLocator : 通过虚表指针-1的位置访问 包含完整的类型信息 7. 逆向分析技巧总结 识别类结构的线索 : 虚表指针初始化 → 构造函数 虚表指针重置 → 析构函数 特定寄存器作为第一个参数 → 成员函数 工具使用建议 : 在IDA中添加类/结构体定义 利用交叉引用追踪虚表使用 关注 atexit 调用点找到全局对象 调试技巧 : 对 atexit 下断点定位全局对象构造 跟踪虚表指针变化理解对象生命周期 8. 推荐学习资源 《深度探索C++对象模型》- Stanley B. Lippman 详细讲解C++对象内存布局的历史和设计考量 《逆向工程核心原理》- 李承远 包含C++逆向分析的实用技巧 IDA Pro官方文档 学习如何有效使用IDA进行C++逆向分析