1. 程式人生 > >通過虛擬函式指標繞過虛擬函式的保護機制

通過虛擬函式指標繞過虛擬函式的保護機制

#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;
}