在IDA Pro里看C++程序,虚函数表最容易让人误判的地方,不是完全找不到,而是看到了一个函数指针数组,却不知道它到底是不是虚表。Hex-Rays官方文档把这件事说得很直接:如果一个C++类包含虚函数,IDA会尝试为它重建虚函数表,类名如果叫A,对应的虚表类型通常会被建成`A_vtbl`,而类结构里那根指向虚表的指针会被命名为`__vftable`。也就是说,识别虚表时,先看“类结构里有没有`__vftable`”,再看“本地类型里有没有`类名_vtbl`”,比只盯数据段里一串地址更稳。
一、IDA Pro怎么识别虚函数表
先不要一上来就在数据段里硬找连续函数地址。更稳的做法,是先从类类型入手,再回头看具体地址。因为官方文档已经明确说明,IDA的C++类型系统本来就会把类和虚表成对建出来,只要类型信息站稳,后面的识别会轻松很多。
1、先看本地类型里有没有类和虚表成对出现
如果你已经导入或整理过类类型,先去看本地类型里有没有类似`A`和`A_vtbl`这种成对类型。官方说明里给了明确示例:`struct __cppobj A{A_vtbl*__vftable;int data;}`,这类结构一旦出现,虚表识别基本就已经有了可靠起点。
2、再看类结构里有没有`__vftable`成员
这一步很关键。Hex-Rays官方明确写到,`__vftable`这个名字不是普通命名习惯,而是让IDA把这根指针识别成虚表指针的重要标记。所以你看到结构首成员是函数指针表地址时,不要随便改成别的名字,先按`__vftable`这条线去理解。
3、遇到多重继承时不要只找一张表
官方文档还专门说明,多重继承类可能同时使用两张虚表,IDA和反编译器都能利用它们生成更清楚的虚调用代码。也就是说,一个对象里如果在不同偏移处都挂了表指针,这不一定是分析错了,反而很可能正是次基类虚表一起存在的正常结果。
4、自动重建不理想时再补类型信息
如果类层级复杂,内置解析不够稳,Hex-Rays官方还给了另一条更强的路,也就是IDAClang。官方文档说明,IDAClang能利用clang的内部VTableLayout来生成vtable类型,在更复杂的C++情况下通常更可靠。所以自动识别不顺时,先补类型,不要急着手工把整张表硬定义完。
二、IDA Pro虚函数表交叉引用怎么看
虚表识别出来以后,真正有价值的下一步不是“看表长什么样”,而是“看谁在引用它”。Hex-Rays在9.2的发布说明里已经把这件事做得更清楚了:新的Xref Tree可以同时显示对当前地址的引用和从当前地址发出的引用,而且代码引用和数据引用都会一起显示。拿虚表分析来说,这正好适合看“哪些构造流程把对象的vfptr指到这张表上”,以及“这张表里的表项又会跳到哪些虚函数实现”。
1、先把光标停在虚表地址或相关表项上
如果你已经定位到虚表起始地址,或者点到了某个具体表项,先把当前位置定在这里,再去开交叉引用。这样看到的结果才是围绕这张表本身展开,而不是被别的对象引用带偏。这个判断和官方对Xref Tree“围绕当前函数或地址显示引用”的定义是一致的。
2、优先用Cross References Tree看整体关系
在IDA 9.2及以后,官方说明可以通过`View→Open Subview→Cross References Tree`打开,也可以直接在有引用的函数或地址上按`Shift-X`。这个视图会同时展示引用到当前项和从当前项出去的关系,而且支持同步到当前IDA View,用来梳理虚表引用链会比传统一条条弹窗看更顺。
3、需要图形关系时再用Xref Graph
如果你想看更直观的“谁连到谁”,9.2的官方说明还给了Xref Graph。它既能从右键菜单里的`Xrefs graph from...`或`Xrefs graph to...`进入,也支持继续向外展开引用节点。对虚表这类“构造点、表地址、表项函数”关系比较绕的对象,图形视图更适合看整体脉络。
4、伪代码层面想看调用现场,再用反编译引用
Hex-Rays在9.2还加入了`Show all Call/Xref Decompilations`,可以从伪代码右键菜单直接看所有引用某个代码或数据项的伪代码片段。对虚表分析来说,这一步很适合在你已经找到某个虚函数实现以后,回头看哪些地方以虚调用形式落到了它。
三、IDA Pro虚表分析先从对象还是表项入手
这一步特别容易做反。很多人一看到`A_vtbl`就先逐项追函数,结果越追越散;也有人反过来,只盯着构造函数里给对象写vfptr的那一条赋值,最后始终没把整张表看全。更稳的办法通常不是二选一,而是先从对象入手确认“这是谁的表”,再从表项入手确认“这张表具体提供了哪些虚行为”。Hex-Rays官方文档把类、`__vftable`和`类名_vtbl`绑在一起,又把Xref Tree和Xref Graph做成围绕当前地址展开的工具,其实就是在支持这种先对象、后表项的分析顺序。
1、先从对象侧确认虚表归属
先看哪个类结构里挂了`__vftable`,再确认它对应的是哪张`类名_vtbl`。这一步先做,后面你看到的表项才不会失去上下文。
2、再从表项侧确认虚函数实现
当归属已经站稳,再逐项去看表里的函数指针,确认哪些是基类继承下来的,哪些是当前类新增或重写的。多重继承场景尤其要这样拆,不然不同基类的表项很容易串。
3、最后再用交叉引用把两边串起来
当对象归属和表项内容都已经清楚,再用Xref Tree或Xref Graph把“对象写表”“表项落函数”“函数被谁调”三层串起来,这时整个虚表链路通常就会清很多。
总结
IDA Pro识别虚函数表,真正稳的起点通常不是数据段里那串地址,而是类结构里的`__vftable`和本地类型里的`类名_vtbl`。虚函数表交叉引用怎么看,重点也不是只看谁引用了表地址,而是把对象归属、表项函数和交叉引用链一起连起来。先把“这张表属于谁”弄清,再去追“这张表被谁写、被谁用、表项又跳到谁”,虚表分析这件事通常就不会越看越散。
