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函數。