原理分析

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

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

实验

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

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

知识点

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

* 类成员函数指针的执行

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

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

实验代码

  1. // virtual void fnFoo(); ///< cc's fnFoo
  2. typedef void (CC::*PFN_fnFoo)();
  3. typedef union un_function_pt
  4. {
  5. PFN_fnFoo pfn;
  6. int ifunAddr;
  7. }UN_FUNCTION_PT;
  8. void fnReplaceVirtualFunction()
  9. {
  10. /// 替换虚表函数的实验
  11. /// 通过实验可知, CC虚函数有2个
  12. /// 虚函数1 CC析构函数
  13. /// 虚函数2 CC::fnFoo
  14. /// 我们将 CC::fnFoo 在虚表中替换为 fnNewVirtualFunction()
  15. int iVirtualTblAddr = 0; ///< CC虚函数表地址
  16. int iVirtualFunctionAddr_CC_fnFoo = 0; ///< CC::fnFoo 对象方法的地址
  17. UN_FUNCTION_PT unFunPt; ///< 用于int转fun*, 绕过编译器限制
  18. DWORD dwOldProtect = 0;
  19. CA* pCA = new CC();
  20. iVirtualTblAddr = *((int*)pCA);
  21. iVirtualFunctionAddr_CC_fnFoo = *((int*)iVirtualTblAddr + 1);
  22. /// 执行CC.fnFoo虚函数的原始函数
  23. unFunPt.ifunAddr = iVirtualFunctionAddr_CC_fnFoo;
  24. (((CC*)pCA)->*unFunPt.pfn)();
  25. /// 手工执行一下pCA的fnNewFunctionSameDefineAsfnFoo
  26. /// 让CC实例执行我们自己的指定的CC类成员函数
  27. /// 必须是CC类已经有的同参同返回值的函数
  28. unFunPt.pfn = &CC::fnNewFunctionSameDefineAsfnFoo;
  29. (((CC*)pCA)->*unFunPt.pfn)();
  30. // make memory writable
  31. if (VirtualProtect((void*)iVirtualTblAddr, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect))
  32. {
  33. /// 替换虚表中的CC::fnFoo 为 CC::fnNewFunctionSameDefineAsfnFoo
  34. unFunPt.pfn = &CC::fnNewFunctionSameDefineAsfnFoo;
  35. ///< 不解除代码段0x0040的写限制, 会C05
  36. *((int*)iVirtualTblAddr + 1) = unFunPt.ifunAddr;
  37. // reprotect
  38. VirtualProtect((void*)iVirtualTblAddr, 8, dwOldProtect, NULL);
  39. /// 执行pCA->fnFoo变成了执行pCA->fnNewFunctionSameDefineAsfnFoo
  40. pCA->fnFoo();
  41. /// ok 已经执行了我们自己指定的CC中的和CC::fnFoo同参同返回值的函数
  42. /// 这个函数可以是非虚函数
  43. }
  44. }
  1. // ClassTest.h: interface for the CClassTest class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #if !defined(AFX_CLASSTEST_H__D794CC4B_D79E_4A61_9D5A_95110788AE39__INCLUDED_)
  5. #define AFX_CLASSTEST_H__D794CC4B_D79E_4A61_9D5A_95110788AE39__INCLUDED_
  6. #if _MSC_VER > 1000
  7. #pragma once
  8. #endif // _MSC_VER > 1000
  9. #include <iostream>
  10. using namespace std;
  11. class CA
  12. {
  13. public:
  14. CA();
  15. virtual ~CA();
  16. virtual void fnFoo();
  17. };
  18. class CB : public CA
  19. {
  20. public:
  21. CB();
  22. virtual ~CB();
  23. virtual void fnFoo();
  24. };
  25. class CC : public CB
  26. {
  27. public:
  28. CC();
  29. virtual ~CC();
  30. virtual void fnFoo();
  31. void fnNewFunctionSameDefineAsfnFoo();
  32. };
  33. #endif // !defined(AFX_CLASSTEST_H__D794CC4B_D79E_4A61_9D5A_95110788AE39__INCLUDED_)
  1. // ClassTest.cpp: implementation of the CClassTest class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "ClassTest.h"
  5. //////////////////////////////////////////////////////////////////////
  6. // CA
  7. //////////////////////////////////////////////////////////////////////
  8. CA::CA()
  9. {
  10. cout << "CA::CA" << endl;
  11. }
  12. CA::~CA()
  13. {
  14. cout << "CA::~CA" << endl;
  15. }
  16. void CA::fnFoo()
  17. {
  18. cout << "CA::fnFoo" << endl;
  19. }
  20. //////////////////////////////////////////////////////////////////////
  21. // CB
  22. //////////////////////////////////////////////////////////////////////
  23. CB::CB()
  24. {
  25. cout << "CB::CB" << endl;
  26. }
  27. CB::~CB()
  28. {
  29. cout << "CB::~CB" << endl;
  30. }
  31. void CB::fnFoo()
  32. {
  33. cout << "CB::fnFoo" << endl;
  34. }
  35. //////////////////////////////////////////////////////////////////////
  36. // CC
  37. //////////////////////////////////////////////////////////////////////
  38. CC::CC()
  39. {
  40. cout << "CC::CC" << endl;
  41. }
  42. CC::~CC()
  43. {
  44. cout << "CC::~CC" << endl;
  45. }
  46. void CC::fnFoo()
  47. {
  48. cout << "CC::fnFoo" << endl;
  49. }
  50. void CC::fnNewFunctionSameDefineAsfnFoo()
  51. {
  52. /// 用来替换虚函数的同参, 同返回值的函数
  53. cout << "CC::fnNewFunctionSameDefineAsfnFoo" << endl;
  54. }

实行效果

  1. CA::CA
  2. CB::CB
  3. CC::CC
  4. CC::fnFoo
  5. CC::fnNewFunctionSameDefineAsfnFoo
  6. CC::fnNewFunctionSameDefineAsfnFoo

http://blog.csdn.net/lostspeed/article/details/50359445

类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)的更多相关文章

  1. C++虚函数表原理

    C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指 向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技 ...

  2. C++类成员空间分配和虚函数表

    最近在自学python,看到继承和类,就顺便复习了C++的类和继承等方面的知识. 先看Base基类 class Base { private: virtual void display() { cou ...

  3. C++ 类中有虚函数(虚函数表)时 内存分布

    虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主是要一个类的虚函数的地址表 ...

  4. (C/C++学习)4.C++类中的虚函数表Virtual Table

    说明:C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为V-Table.在这个表中,主要为一个类的虚函数的地址表,这张表解决了继承.覆写的问题,保证其真实反应实际的虚函数调用 ...

  5. 我理解的C++虚函数表

    今天拜读了陈皓的C++ 虚函数表解析的文章,感觉对C++的继承和多态又有了点认识,这里写下自己的理解.如果哪里不对的,欢迎指正.如果对于C++虚函数表还没了解的话,请先拜读下陈皓的C++ 虚函数表解析 ...

  6. 关于C++中虚函数表存放位置的思考

    其实这是我前一段时间思考过的一个问题,是在看<深入探索C++对象模型>这本书的时候我产生的一个疑问,最近在网上又看到类似的帖子,贴出来看看: 我看到了很多有意思的答案,都回答的比较好,下面 ...

  7. C++虚函数表和对象存储

    C++虚函数表和对象存储 C++中的虚函数实现了多态的机制,也就是用父类型指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种技术可以让父类的指针有"多种形态",这 ...

  8. C++ 虚函数表解析

    转载:陈皓 http://blog.csdn.net/haoel 前言 C++中 的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实 ...

  9. C++虚函数和虚函数表

    前导 在上面的博文中描述了基类中存在虚函数时,基类和派生类中虚函数表的结构. 在派生类也定义了虚函数时,函数表又是怎样的结构呢? 先看下面的示例代码: #include <iostream> ...

随机推荐

  1. Java面试题之一

    下面也将收集一些经典的java面试题,琢磨这些面试题还是非常有好处,可以弄清楚一些容易混淆的知识点,下面面试题的答案部分来自网络,有些来自自己的理解,都是自己精心归纳整理的,有问题的地方,希望大家指出 ...

  2. NYoj-街区最短路径问题

    街区最短路径问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描写叙述 一个街区有非常多住户,街区的街道仅仅能为东西.南北两种方向. 住户仅仅能够沿着街道行走. 各个街道之 ...

  3. 小猪猪C++笔记基础篇(四)数组、指针、vector、迭代器

    小猪猪C++笔记基础篇(四) 关键词:数组,Vector. 一.数组与指针 数组相信大家学过C语言或者其他的语言都不陌生,简单的就是同一个变量类型的一组数据.例如:int a[10],意思就是从a开始 ...

  4. 使用copy函数完成数据库迁移

    最近在该一个迁移工具的迁移方式,从ora8迁移到postgresql使用原来的插入迁移速度太慢了,老板说让使用缓存迁移,即使用postgresql的copy函数,因此去pg官网查阅了相关资料,我们需要 ...

  5. C++拾遗

    1三个概念 字符串字面值是一串常量字符(是一个常量),字符串字面值常量用双引号括起来的零个或多个字符表示,为兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符.字符串字面值的类型 ...

  6. POJ 1279 Art Gallery 半平面交求多边形核

    第一道半平面交,只会写N^2. 将每条边化作一个不等式,ax+by+c>0,所以要固定顺序,方便求解. 半平面交其实就是对一系列的不等式组进行求解可行解. 如果某点在直线右侧,说明那个点在区域内 ...

  7. 基于visual Studio2013解决算法导论之012计数排序

     题目 计数排序 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #in ...

  8. JavaDoc的生成规则---ShinePans

    使用方法: javadoc [options] [packagenames] [sourcefiles] [@files] -overview <file>          从 HTML ...

  9. ioc构架demo

    1.视图 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:too ...

  10. webform--常用的控件

    一.简单控件 1.Lable——标签:在网页中呈现出来的时候会变成span标签 属性:Text——标签上的文字  BackColor,ForeColor——背景色,前景色 Font——字体 Bold- ...