3.多重继承虚表
多重继承是指一个类同时继承多个父类。多重继承与单重继承相比,可以有多个父类。
可以看出main函数与单重继承无区别。在cderived构造函数中,首先按继承顺序调用两个基类的构造函数,然后执行自己的构造函数代码。
因为两个基类都有虚函数,在这种情况下编译器会为派生类生成两个虚表,在构造函数时初始化。虚表项的构造顺序和上一个例子基本一致,唯一的区别在于派生类新增的虚函数挂在了第一个虚表后面。
4.菱形继承虚表
两个子类继承自同一个父类,又有子类同时继承这两个子类(在子类的子类中出现)。在内存布局会出现两个相同的父类内存结构。
main函数中的构造函数调用有点特殊,多传递了一个标识是否调用基类构造函数的标志参数。按语法B的构造函数要先构造A,这样编译器就要在B类的构造函数里调用A类的构造函数。顺序没有问题,然而在BC的构造也要先构造B构造C,在BC的构造函数里调用了B类的构造函数和C的构造函数。这里就有问题,因为这样子A的构造函数就被调用了两次。而这是多传递了一个参数就是用来表示是否调用虚基类的构造这样就可以防止虚基类被调用两次。
在调用虚基类构造函数之前,出现了一个初始化虚基类偏离表的操作。这是因为存在徐继承,虚基类对象的内存在派生类的内存中只保留一份,所以在编译器方便定位虚基类在对象内存中的位置,做一个虚基类偏移表。
所以我们在逆向分析时,如果发现构造函数有初始化虚基类偏移表的操作就可以怀疑这个类继承层次带有虚继承。
5.抽象类虚表
在C++中,含有纯虚函数的类称为抽象类,它不能实例化对象。面向对象设计中常用抽象类给子类规范接口,接口的功能通常都是重要功能。它最大的特点就是没有实现代码。如果在逆向分析时找到一个类的抽象类那么就可以通过他的构造函数就可以定位它的所有子类,得到重要功能。
可以看出父类为抽象类的实现代码和单重继承没有太大区别,唯一的别就是虚表。由于纯虚函数没有实现代码,编译器默认填充_purecall函数地址。
如果发现一个类的虚表里面有_purecall虚表项。