0x0一、前言ios
对C++ 了解的人都应该知道虚函数(Virtual Function)是经过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,因此,当咱们用父类的指针来操做一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图同样,指明了实际所应该调用的函数。windows
这里咱们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——若是有多层继承或是多重继承的状况下)。 这意味着咱们经过对象实例的地址获得这张虚函数表,而后就能够遍历其中函数指针,并调用相应的函数。函数
0x0二、实例分析性能
咱们假设有这么一个类,有三个虚函数测试
class Base1 { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } };
咱们生成类的实例去遍历这个虚表的方式也很简单spa
typedef void(*Fun)(void);
Base b; Fun pFun = NULL; // cout << "虚函数指针的地址:" << (int*)(&b) << endl; 解两次&b保存着指向虚表的指针 cout << "虚函数表地址:" << (int*)*(int*)(&b) << endl; pFun = (Fun)*((int*)*(int*)(&b)+0); // Base::f() pFun(); pFun = (Fun)*((int*)*(int*)(&b)+1); // Base::g() pFun(); pFun = (Fun)*((int*)*(int*)(&b)+2); // Base::h() pFun();
而结果也是很明显的.net
而虚函数的存储位置咱们能够经过这张图来了解3d
&b就是虚函数表的地址,按声明顺序保存着虚函数的地址。最后一个点表示结束,对于不一样编译器这个值是不一样的。指针
咱们看看虚表的内存code
咱们能够看到前12个字节分别保存着0030104b、0030118六、0030122b就是咱们上图调用函数的地址,就是声明的虚函数地址,然后面则是保存着咱们函数输出的字符串。
咱们试试64位下
咱们能够看到只是地址变成8字节了,咱们把上面的int变成DWORD64就行,虚表中保存的地址为8字节。
咱们分为如下4种继承状况分析虚表
咱们的子类对父类的虚函数不加以实现,子类加入本身的虚函数
class Derive1:public Base1 { public: virtual void f1() { cout << "Derive::f1" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } virtual void h1() { cout << "Derive::h1" << endl; } };
那咱们的虚函数表就变成这样了,子类实现的虚函数会放在父类虚函数表后面
class Son1:public Base1 { public: void f() { cout << "Son::f" << endl; } //对父类的实现 virtual void g1() { cout << "Son::g1" << endl; } virtual void h1() { cout << "Son::h1" << endl; } };
当咱们的子类对父类中虚函数覆盖时,咱们子类中实现的函数地址就会覆盖虚函数表中父类原来虚函数的地址
这里就实现了多态
class Derive : public Base1, public Base2, public Base3 { public: virtual void f1() { cout << "Derive::f1" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } };
在子类的虚函数表是下面这样的
咱们遍历这个虚表
Derive d; int** pVtab = (int**)&d; //Base1's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); pFun = (Fun)pVtab[0][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); pFun = (Fun)pVtab[0][1]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2]; pFun(); //Derive's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); pFun = (Fun)pVtab[0][4]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][5]; cout<<pFun<<endl; //Base2's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1]; pFun(); pFun = (Fun)pVtab[1][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[1][3]; cout<<pFun<<endl; //Base3's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1]; pFun(); pFun = (Fun)pVtab[2][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[2][3]; cout<<pFun<<endl;
获得遍历的结果
咱们&d的地址
能够看到有三个地址,分别对应着三个虚函数表
咱们的实现中,第一个地址保存着第一个父类Base1中的三个虚函数,还有本身实现的两个虚函数,一共是5个,第六个2d6e7552截止。
第二个地址003f7848和第三个地址003f7834分别对应着Base2,Base3的虚函数表
class Derive : public Base1, public Base2, public Base3 { public: void f1() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } };
咱们能够看到,对于三个虚函数表,子类的实现将其都覆盖了 ,继续用上面那个例子遍历,不过子类的虚函数实现只有一个因此pFun = (Fun)pVtab[0][4];就是虚函数表中的结束的地址,不能在遍历,否则会崩溃
能够看到三个虚表中的第一个成员都被子类实现,而且都更新到虚表中。
下面是完整测试代码
// VirtualTable.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> using namespace std; #include <Windows.h> class Base1 { public: virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derive : public Base1, public Base2, public Base3 { public: void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } }; class Son:public Base1 { public: virtual void f1() { cout << "Son::f1" << endl; } virtual void g1() { cout << "Son::g1" << endl; } virtual void h1() { cout << "Son::h1" << endl; } }; class Son1:public Base1 { public: void f() { cout << "Son::f" << endl; } virtual void g1() { cout << "Son::g1" << endl; } virtual void h1() { cout << "Son::h1" << endl; } }; typedef void(*Fun)(void); int main() { Fun pFun = NULL; /* //遍历父类虚函数表 Base1 b; //cout << "虚函数表地址:" << (int*)(&b) << endl; cout << "虚函数表地址:" << (DWORD64*)*(DWORD64*)(&b) << endl; pFun = (Fun)*((DWORD64*)*(DWORD64*)(&b)+0); // Base::f() cout<<"Base f Address:"<<pFun<<endl; pFun(); pFun =(Fun)*((DWORD64*)*(DWORD64*)(&b)+1); // Base::g() cout<<"Base g Address:"<<pFun<<endl; pFun(); pFun =(Fun)*((DWORD64*)*(DWORD64*)(&b)+2); // Base::h() cout<<"Base h Address:"<<pFun<<endl; pFun(); */ /* //通常继承 无覆盖 //遍历子类虚函数表 Son son; cout<<"子类虚函数表地址: "<<(int*)*(int*)&(son)<<endl; pFun = (Fun)*((int*)*(int*)&(son)+0); cout<<"Base f Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+1); cout<<"Base g Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+2); cout<<"Base h Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+3); cout<<"son f Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+4); cout<<"son g Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+5); cout<<"son h Address:"<<pFun<<endl; pFun(); */ /* 通常继承 实现覆盖 Son1 son; cout<<"子类虚函数表地址: "<<(int*)*(int*)&(son)<<endl; pFun = (Fun)*((int*)*(int*)&(son)+0); cout<<"son f Address:"<<pFun<<"我覆盖了啦啦啦"<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+1); cout<<"Base g Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+2); cout<<"Base h Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+3); cout<<"son g Address:"<<pFun<<endl; pFun(); pFun = (Fun)*((int*)*(int*)&(son)+4); cout<<"son h Address:"<<pFun<<endl; pFun(); */ /*多重继承 无覆盖 Derive d; int** pVtab = (int**)&d; //Base1's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); pFun = (Fun)pVtab[0][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); pFun = (Fun)pVtab[0][1]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2]; pFun(); //Derive's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); pFun = (Fun)pVtab[0][4]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][5]; cout<<pFun<<endl; //Base2's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1]; pFun(); pFun = (Fun)pVtab[1][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[1][3]; cout<<pFun<<endl; //Base3's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1]; pFun(); pFun = (Fun)pVtab[2][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[2][3]; cout<<pFun<<endl; */ /*多重继承 有覆盖*/ Derive d; int** pVtab = (int**)&d; //Base1's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); pFun = (Fun)pVtab[0][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); pFun = (Fun)pVtab[0][1]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2]; pFun(); //Derive's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][4]; cout<<pFun<<endl; //Base2's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1]; pFun(); pFun = (Fun)pVtab[1][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[1][3]; cout<<pFun<<endl; //Base3's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1]; pFun(); pFun = (Fun)pVtab[2][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[2][3]; cout<<pFun<<endl; return 0; }
参考:http://blog.csdn.net/haoel/article/details/1948051/
0x0三、简单虚表钩子
咱们知道虚函数表了,那么咱们有种Hook叫作虚表钩子,替换对象中虚表的函数地址,走进咱们的函数,而后在实现完成以后调用原来函数
代码以下
// COMHook.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> using namespace std; #include <windows.h> typedef void(*Fun)(void); Fun FuncAddres; class A { public: int a ; int b ; A(){} ~A(){} virtual void f1(){ printf("Founction f1 called\n"); } virtual void f2(){ printf("Founction f2 called\n"); } virtual void f3(){ printf("Founction f3 called\n"); } private: int n; }; class B :public A { public: void f1(); void f2(); void f3(); }; void B::f1() { printf("Hello f1\r\n"); } void B::f2() { printf("Hello f2\r\n"); } void B::f3() { printf("Hello f3\r\n"); } void myfunc() { cout<<"我是大坏蛋"<<endl; FuncAddres(); } int _tmain(int argc, _TCHAR* argv[]) { B* b = new B; long** pplVrtable= (long**)(b); //取得虚函数表的指针 cout<<"My Func Address : "<<myfunc<<endl; HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId()); MEMORY_BASIC_INFORMATION mbi = {0}; if (VirtualQueryEx(hProcess, (LPVOID)(*pplVrtable), &mbi, sizeof(mbi)) != sizeof(mbi)) return 0; DWORD dwOldProtect = 0; if(!::VirtualProtectEx(hProcess, mbi.BaseAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect)) return 0; FuncAddres = (Fun)*(int*)*pplVrtable;//保存原来的函数地址 *(int*)*pplVrtable =(long)myfunc;//(LONG)pplVrtable[0][1];//将虚函数表的指针指向虚函数表第二个值。 DWORD dwTemp = 0; ::VirtualProtectEx(hProcess, mbi.BaseAddress, 4, dwOldProtect, &dwTemp); CloseHandle(hProcess); b->f1(); b->f2(); b->f3(); delete b; return 0; }
须要注意的是在测试的时候 B b;这样申明以后,虽然改了内存中虚表地址,可是不会调用咱们的myfunc函数。只有B*b = new B;这样生成替换以后才能走进咱们的myfunc函数。