### C++ 程序怎么去逆向找虚表

**虚表（vtable）的编译后形态**

在 C++ 中，当一个类包含虚函数时，编译器会做两件事：

1. 为该类生成一个**虚表**。这个虚表本质上是一个**函数指针数组**。数组中的每个元素都指向该类中一个虚函数的实际地址
2. 为该类的每个对象（实例）在内存布局的开头添加一个隐藏的**虚表指针（vptr）**。这个指针指向该类的虚表

因此，逆向寻找虚表的过程，就是找到这个隐藏的 `vptr`，并顺藤摸瓜找到它指向的虚表

**逆向寻找虚表的三种主要方法**

**方法一：寻找虚表指针（vptr）的初始化**

这是最直接也最常用的方法。虚表指针通常在对象的构造函数中被初始化

1. **定位构造函数**：在 C++ 程序中，当你使用 `new` 关键字创建一个对象时，编译器会调用该类的构造函数。在反汇编代码中，你会看到对 `new` 操作的封装，然后是对构造函数的调用
2. **查找虚表指针的赋值**：在构造函数的开头，通常会有类似 `mov [ecx], offset class_vtable` 或 `mov [this], offset class_vtable` 的指令（取决于调用约定和寄存器）
   - `this` 指针（通常在 `ecx` 或 `rcx` 寄存器中）指向新创建的对象
   - `offset class_vtable` 是虚表的地址，这是一个常量，通常由链接器确定
   - 这条指令的含义是：将虚表的地址存入对象的第一个成员变量中，这个变量就是 `vptr`
3. **识别虚表**：一旦你找到了虚表的地址，你就可以跳转到这个地址，IDA Pro 或 Ghidra 通常会将其识别为数据段中的一个指针数组

**方法二：从虚函数的调用处反推**

如果你无法直接找到构造函数，可以从虚函数的调用点入手。虚函数的调用通常是通过 `vptr` 进行的间接调用

1. **识别间接调用**：寻找类似 `call [eax+offset]` 或 `call [vptr]` 的指令
   - `eax` 通常包含 `this` 指针，指向对象实例
   - `offset` 是一个数字，通常是 `vptr` 在对象内存布局中的偏移量（在单继承情况下通常是 `0`）
   - 这条指令的含义是：从 `eax` 指向的内存位置（即 `vptr`）获取一个地址，然后再加上一个偏移量，最终跳转到那个地址执行代码
2. **分析偏移量**：通过观察偏移量，你可以判断这是虚表中的第几个虚函数。例如，`call [eax+8]` 意味着调用虚表中的第二个函数（因为每个函数指针通常是 4 或 8 字节）
3. **反推虚表**：找到 `vptr` 的地址，然后跳转到该地址。你可以向上或向下遍历这个指针数组，来识别其他的虚函数

**方法三：利用 IDA Pro 的自动化识别功能**

IDA Pro 和 Ghidra 这样的高级反汇编器拥有强大的自动化分析能力，可以极大地简化寻找虚表的过程

1. **启用 C++ RTTI 分析**：在 IDA 的 `Options -> General -> IDA` 窗口中，确保 C++ 的**RTTI (Run-Time Type Information)** 分析选项已启用。这能帮助 IDA 识别类和虚表结构
2. **函数识别**：让 IDA 自动分析程序，它通常会尝试识别标准库中的虚表
3. **数据段搜索**：在 IDA 的数据段（通常是 `.data` 或 `.rdata`）中搜索，寻找**指针数组**。如果一个数组中的元素都是函数地址，并且这些函数之间有逻辑关联，那它很可能就是一个虚表。IDA 通常会把这些识别出来的虚表标记为 `vftable` 或类似的名字