类虚函数表原理实现分析
发布日期:2021-06-30 22:07:57
浏览次数:2
分类:技术文章
本文共 3234 字,大约阅读时间需要 10 分钟。
原理分析
当调用一个虚函数时, 编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数. 已经不是在调用函数名了.
当我们将虚表地址[n]中的函数实现改为另外的函数, 虚函数的实现就由我们来控制了.
实验
根据虚表原理, 实验一下修改自己程序的虚函数表项地址.
使编译器生成的代码执行一个虚函数A时, 执行的是我们自己定义的非虚函数B.
知识点
* 使用union赋值, 绕过编译器函数与变量强转赋值的限制* 类成员函数指针的执行
* 修改和恢复自己的代码段属性
* 虚函数表项的定位和读写
实验代码
// virtual void fnFoo(); ///< cc's fnFootypedef 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#includeusing 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::CACB::CBCC::CCCC::fnFooCC::fnNewFunctionSameDefineAsfnFooCC::fnNewFunctionSameDefineAsfnFoo
转载地址:https://lostspeed.blog.csdn.net/article/details/50359445 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
表示我来过!
[***.240.166.169]2024年04月23日 21时31分17秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
CodeForces - 931B World Cup (思维 模拟)
2019-04-30
ACM 2018 青岛区域赛 J-Books (模拟)
2019-04-30
ACM 2016 沈阳区域赛 E - Counting Cliques (dfs)
2019-04-30
ACM 2017 北京区域赛 J-Pangu and Stones(区间dp)
2019-04-30
HDU - 5643 King's Game (约瑟夫环变式)
2019-04-30
UVA - 1452 Jump (约瑟夫环变式)
2019-04-30
POJ - 3517 And Then There Was One (约瑟夫环变式)
2019-04-30
HDU - 2068 RPG的错排 (错排+组合数)
2019-04-30
CodeForces 591C Median Smoothing(思维 模拟)
2019-04-30
升级yosemite后java出错的解决
2019-04-30
mac || Linux 命令行下实现批量重命名
2019-04-30
java常用类 String面试题
2019-04-30
Windows10下的powershell美化教程
2019-04-30
利用ffmpeg合并音频和视频
2019-04-30
刷好老毛子系统进不了老毛子系统后台的解决办法
2019-04-30
Parallels Desktop 16 不能联网的解决办法
2019-04-30
SLAM中TUM数据集更改图片名字
2019-04-30
手把手教你--jquery chosen插件的使用和API(html下拉框美化)
2019-04-30