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

虚表(vtable)的编译后形态

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

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

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

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

方法一:寻找虚表指针(vptr)的初始化

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

  1. 定位构造函数:在 C++ 程序中,当你使用 new 关键字创建一个对象时,编译器会调用该类的构造函数。在反汇编代码中,你会看到对 new 操作的封装,然后是对构造函数的调用
  2. 查找虚表指针的赋值:在构造函数的开头,通常会有类似 mov [ecx], offset class_vtablemov [this], offset class_vtable 的指令(取决于调用约定和寄存器)
    • this 指针(通常在 ecxrcx 寄存器中)指向新创建的对象
    • 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 或类似的名字
Copyright © 版权信息 all right reserved,powered by Gitbook该文件修订时间: 2025-09-25 03:13:05

results matching ""

    No results matching ""