编写测试代码如下,定义一个基类Base,并第一个虚函数 func。定义一个子类Derived,继承基类Base,并覆盖虚函数 func

main函数中创建临时对象 Derived, 并使用基类指针指向Derived对象,通过指针调用虚函数 func。调用结果如下:

指令层面分析
首先进入分析main函数代码:

上述是main函数的汇编指令,由于是局部申明对象,申明后的对象在栈中保存,保存地址是 rbp + 0x10,将该地址通过rdi寄存器传参,然后调用Derived的构造函数。调用完构造函数后,先取对象首地址处的值拿到虚表指针,然后取虚表指针的第一个值,拿到虚函数地址,并调用虚函数。
接着进入到Derived的构造函数处分析:

这是一个套壳的构造函数,实际上只是对真实构造函数的调用,进入到真实构造函数处:

构造函数的参数就是对象的首地址,也就是this指针,构造函数先调用基类Base的构造函数,调用完成后,将Derived类基表+0x10处的地址保存在了对象首地址处。看下基类的构造函数:

基类的构造函数同样是将基类的虚表地址+0x10 保存在了对象首地址处。那么继续看下这两个类的虚表数据:

虚表的首地址是 0,然后是该类的type_info,虚表+0x10处才是第一个虚函数的地址。最后面保存的是盖类的析构函数地址。
1、如果类中有定义虚函数,编译器会生成该类的虚函数表,并在创建对象时,在对象的头部保存虚指针。
2、对象初始化时,在对象的构造函数中,会调用基类的构造函数,在构造函数中会初始化虚指针,基类构造函数中先初始化虚指针,然后在子类构造函数中初始化时,覆盖了基类构造函数中初始化的虚指针值。
3、虚函数不能定义成构造函数,因为虚函数通过虚指针来定位虚表中的虚函数,而虚指针在构造函数完成时才会完成初始化。
4、虚表是一个指针数组,gcc实现中虚表第一个指针保留0,第二个指针存储typeinfo信息指针,第三个指针开始存放虚函数。
上述代码在前面的基础上,新增了两个类用于多继承,其中Base2中的虚方法没有覆盖,同样直接分析对象的构造函数:
[培训]科锐逆向工程师培训第53期2025年7月8日开班!
最后于 2025-6-6 11:25
被CCTV果冻爽编辑
,原因: