通過虛擬函式指標繞過虛擬函式的保護機制
阿新 • • 發佈:2019-02-09
#include <iostream> using namespace std; class A { public: A() { a = 10; } private: virtual void Func1() { cout << "class A Func1" << endl; cout << a << endl; } virtual void Func2() { cout << "class A Func2" << endl; } private: int a; }; int _tmain(int argc, _TCHAR* argv[]) { A a; //得到虛表的地址 int* v_ptrAdrs = *((int**)&a); //得到虛表中的第一個值 int* funcAddr = *((int**)v_ptrAdrs); typedef void (*gFUNC)(); A* pp = &a; //如果你用的是vs,可以通過彙編壓入this指標,目前只在vs2008下測試過 __asm mov ecx, pp; //直接呼叫虛擬函式,此處繞過了private的保護 (*(gFUNC)funcAddr)(); return 0; }
當然,這隻能是在沒有引數的情況下才能呼叫成功,如果存在引數,那麼還是會失敗,下面我們修改一下類A,為函式新增一個入參:
class A { public: A() { a = 10; } public: virtual void Func1(int para) { cout << "class A Func1" << endl; cout << a*para << endl; } virtual void Func2() { cout << "class A Func2" << endl; } private: int a; };
我們通過彙編程式碼來檢視為什麼會失敗,擷取彙編程式碼如下: (*(gFUNC)funcAddr)(10); 004144FD 8B F4 mov esi,esp 004144FF 6A 0A push 0Ah --入棧,壓參10 00414501 FF 55 DC call dword ptr [funcAddr] --呼叫函式,跟入函式內部,我們會發現,函式的最後會進行出棧操作 00414504 83 C4 04 add esp,4 --出棧 00414507 3B F4 cmp esi,esp 00414509 E8 BE CC FF FF call @ILT+455(__RTC_CheckEsp) (4111CCh)
通過上述彙編程式碼,就會發現,錯誤出在呼叫方式上,下面只要稍作改動就可以成功了。
int _tmain(int argc, _TCHAR* argv[])
{
A a;
//得到虛表的地址
int* v_ptrAdrs = *((int**)&a);
//得到虛表中的第一個值
int* funcAddr = *((int**)v_ptrAdrs);
typedef void (__stdcall *gFUNC)(int);
A* pp = &a;
//如果你用的是vs,可以通過彙編壓入this指標,目前只在vs2008下測試過
__asm mov ecx, pp;
//直接呼叫虛擬函式,此處繞過了private的保護
(*(gFUNC)funcAddr)(10);
return 0;
}