C++类虚函数逆向学习总结
[摘要]受menting影响,也对C++也逆向了一把.由于刚刚接触逆向,都是自己学习总结的,又是第一次发贴,因此不免有误解之处,还请各位多多指教,谢谢.
[正文]先来看一下C++类的虚函数的实例:
class clsC
{
private:
int m_c;
public:
clsC(){m_c=0;}
~clsC(){};
void show(){cout<<"I'm in BaseC Class.";};
void show2(){cout<<"I'm in BaseC Class2.";}
};
class clsB:public clsC
{
private:
int m_b;
public:
clsB(){m_b=0;}
~clsB(){};
virtual void show(){cout<<"I'm in BaseB Class.";}
virtual void show2(){cout<<"I'm in BaseB Class2.";}
};
class clsA:public clsB
{
private:
int m_a;
int m_b;
int m_c;
public:
clsA();
~clsA(){};
virtual void show(){cout<<"I'm in sub Class.";}
};
clsA::clsA():m_b(1),m_c(2),m_a(0)
{
}
void main()
{
clsA a; //局部实例类,main函数反汇编代码如下:
clsB *pb = &a;
clsC *pc = &a;
pb->show2();
pc->show();
}

sub esp,24 在堆栈中为局部变量a和指针pb、pc、临时指针分配36字节.其中clsA类大小为24字节(clsA有3*4=12字节成员变量, clsB有4字节成员变量, clsC有4字节成员变量,虚函数有个指向vtable的虚函数表指针,占4字节.因此一共12+4+4+4=24)
构造函数反汇编如下:

派生类的构造函数首先调用基类的构造函数,基类clsB的构造函数如下:

类clsB的构造函数又调用基类clsC的构造函数:

由于顶层基类clsC无虚函数,因此可见,clsB类的虚函数表vtable指针也占用类的第1个DWORD空间.故类的派生结构体系中,如果存在虚函数,则虚函数表vtable指针一定占用类的第1个DWORD位置.基类clsB的构造函数将基类的所有虚函数的地址存放到虚函数地址表vtable中:
下面看一下这两个地址所指向的汇编代码:


可见基类clsB的虚函数表指向基类clsB的虚函数地址show()和show2()
但基类构造完后,派生类的构造函数改写了这个虚函数表vtable指针.下面看下派生类的虚函数表vtable指针:
下面看一下这两个地址所指向的汇编代码:


由于派生类无show2()函数,因此可见派生类的虚函数表指向派生类的虚函数地址show()和基类的虚函数show2().
因此,整个构造函数中,派生类的虚函数表vtable指针会改写基类的虚函数表vtable指针来实现虚函数的特性.以后调用虚函数时直接从虚函数表vtable中获取函数的地址来调用.
如果派生类中没有重写基类的某虚函数,则将在派生类的虚函数表vtable中使用基类的某虚函数地址.
这里pc->show();的调用由于为非虚函数,因此不再使用虚函数特性.反汇编代码显示在编译时使用了clsC:: show()函数地址的硬编码的调用方式.
最后整个clsA类在栈的分配结构图如下:
