Files
2025-09-25 04:02:19 +08:00

42 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### 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` 或类似的名字