类虚函数表原理实现分析

原理分析

当调用一个虚函数时编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数已经不是在调用函数名了.

当我们将虚表地址[n]中的函数实现改为另外的函数虚函数的实现就由我们来控制了.


实验

根据虚表原理, 实验一下修改自己程序的虚函数表项地址. 

使编译器生成的代码执行一个虚函数A时, 执行的是我们自己定义的非虚函数B.

知识点

* 使用union赋值, 绕过编译器函数与变量强转赋值的限制

* 类成员函数指针的执行

* 修改和恢复自己的代码段属性

* 虚函数表项的定位和读写

实验代码

// virtual void fnFoo(); ///< cc's fnFoo
typedef void (CC::*PFN_fnFoo)();

typedef union un_function_pt
{
    PFN_fnFoo pfn;
    int ifunAddr;
}UN_FUNCTION_PT;

void fnReplaceVirtualFunction()
{
    /// 替换虚表函数的实验
    /// 通过实验可知, CC虚函数有2个
    /// 虚函数1 CC析构函数
    /// 虚函数2 CC::fnFoo
    
    /// 我们将 CC::fnFoo 在虚表中替换为 fnNewVirtualFunction()
    
    int iVirtualTblAddr = 0; ///< CC虚函数表地址
    int iVirtualFunctionAddr_CC_fnFoo = 0; ///< CC::fnFoo 对象方法的地址
    UN_FUNCTION_PT unFunPt; ///< 用于int转fun*, 绕过编译器限制
    DWORD dwOldProtect = 0;
    
    CA* pCA = new CC();
    
    iVirtualTblAddr = *((int*)pCA);
    iVirtualFunctionAddr_CC_fnFoo = *((int*)iVirtualTblAddr + 1);
    
    /// 执行CC.fnFoo虚函数的原始函数
    unFunPt.ifunAddr = iVirtualFunctionAddr_CC_fnFoo;
    (((CC*)pCA)->*unFunPt.pfn)();
    
    /// 手工执行一下pCA的fnNewFunctionSameDefineAsfnFoo
    /// 让CC实例执行我们自己的指定的CC类成员函数
    /// 必须是CC类已经有的同参同返回值的函数
    unFunPt.pfn = &CC::fnNewFunctionSameDefineAsfnFoo;
    (((CC*)pCA)->*unFunPt.pfn)();
    
    // make memory writable
    if (VirtualProtect((void*)iVirtualTblAddr, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect))
    {
        /// 替换虚表中的CC::fnFoo 为 CC::fnNewFunctionSameDefineAsfnFoo
        unFunPt.pfn = &CC::fnNewFunctionSameDefineAsfnFoo;
        
        ///< 不解除代码段0x0040的写限制, 会C05
        *((int*)iVirtualTblAddr + 1) = unFunPt.ifunAddr;
        
        // reprotect
        VirtualProtect((void*)iVirtualTblAddr, 8, dwOldProtect, NULL);
        
        /// 执行pCA->fnFoo变成了执行pCA->fnNewFunctionSameDefineAsfnFoo
        pCA->fnFoo();
        
        /// ok 已经执行了我们自己指定的CC中的和CC::fnFoo同参同返回值的函数
        /// 这个函数可以是非虚函数
    }
}


// ClassTest.h: interface for the CClassTest class.
//
//

#if !defined(AFX_CLASSTEST_H__D794CC4B_D79E_4A61_9D5A_95110788AE39__INCLUDED_)
#define AFX_CLASSTEST_H__D794CC4B_D79E_4A61_9D5A_95110788AE39__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <iostream>

using namespace std;

class CA
{
public:
    CA();
    virtual ~CA();
    virtual void fnFoo();
};

class CB : public CA
{
public:
    CB();
    virtual ~CB();
    virtual void fnFoo();
};

class CC : public CB
{
public:
    CC();
    virtual ~CC();
    virtual void fnFoo();
    void fnNewFunctionSameDefineAsfnFoo();
};


#endif // !defined(AFX_CLASSTEST_H__D794CC4B_D79E_4A61_9D5A_95110788AE39__INCLUDED_)

// ClassTest.cpp: implementation of the CClassTest class.
//
//

#include "ClassTest.h"

//
// CA
//

CA::CA()
{
    cout << "CA::CA" << endl;
}

CA::~CA()
{
    cout << "CA::~CA" << endl;
}

void CA::fnFoo()
{
    cout << "CA::fnFoo" << endl;
}

//
// CB
//

CB::CB()
{
    cout << "CB::CB" << endl;
}

CB::~CB()
{
    cout << "CB::~CB" << endl;
}

void CB::fnFoo()
{
    cout << "CB::fnFoo" << endl;
}

//
// CC
//

CC::CC()
{
    cout << "CC::CC" << endl;
}

CC::~CC()
{
    cout << "CC::~CC" << endl;
}

void CC::fnFoo()
{
    cout << "CC::fnFoo" << endl;
}

void CC::fnNewFunctionSameDefineAsfnFoo()
{
    /// 用来替换虚函数的同参, 同返回值的函数
    cout << "CC::fnNewFunctionSameDefineAsfnFoo" << endl;
}



实行效果

CA::CA
CB::CB
CC::CC
CC::fnFoo
CC::fnNewFunctionSameDefineAsfnFoo
CC::fnNewFunctionSameDefineAsfnFoo



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值