这里想说的是:代码中的关键点为用指令jmp pFunc跳转到你想要运行的函数pFunc。

指令“jmp xxxx”占5个字节,代码中用了个一字节对齐的结构体struct Thunk ,

当然也能够用 unsigned char code[5]; 说还有一个关键点就是地址计算了,jmp xxxx指令用了相对跳转地址,

相对地址 = 要跳转函数的地址 - “jmp xxxx”指令的下一条指令的地址。

以下代码中的class C 仅仅有m_thunk一个数据成员,没有虚函数和在m_thunk前没有声明别的数据成员,

因此相对地址 = pFunc - [ (int)this + sizeof(struct Thunk) ]

如上所述,若有虚函数和在m_thunk前声明了别的数据成员,则相对地址的计算要做改动。

:)本来画个表会说得比較清楚,但本人嫌麻烦,就作罢了!

/////////////////////////////////////////////以下是所转的文章////////////////////////////////////////////////////////////////////////////////

实际上C++ 的THUNK技术是须要改变指令代码的,这里发一个贴说明之 





// 此程序演示 执行时 改变 指令代码   

  

//实质是 C++ 实现多态  的 THUNK 技术思想的简陋模拟 



//在VC6.0 中编译通过。 



#include <windows.h> 

#include <iostream.h> 







typedef void(*pFUN)();  //函数类型 



#pragma pack(push,1) //强制编译器,使数据按字节边界对齐。 

                     //默认情况下VC6.0是按4字节对齐,VC7.0按8字节对齐 

                     //指令本不是按双字边界对齐的,所以必须使其按字节边界对齐,否则出错 



// 以下是存储机器代码的结构 

struct Thunk //有趣的是:这个结构不储存数据,而是储存指令。一个jmp跳转指令 

{   //我们将改变这个结构,然后让程序运行此代码,此结构的运行将会改变程序的运行路径 

    BYTE    m_jmp; // 储存jmp指令的操作码 

    

DWORD   m_adrr;      // 储存相对jmp指令的偏移地址(指令操作数) 

};  // 

#pragma pack(pop)//撤销数据按字节对齐,数据按双字对齐的主要目的是优化运行速度 



class C 



public: 

    Thunk    m_thunk;  //产生一个 Thunk 实例 



    void Init(pFUN pFun) 

    { 

         

        m_thunk.m_jmp = 0xe9;// 跳转指令的操作码是 0xe9 所以。。。 

        

        m_thunk.m_adrr = (int)pFun - ((int)this+sizeof(Thunk)); 

           // JMP跳转是相对跳转,也就是说:它是跳转到的地址是: 当前指令地址(EIP)+相对操作数 

  // 相对操作数有符号的! 

         //当指令运行到Thunk 中指令的时候,我们须要跳转到pFun,而当前EIP指为(int)this+sizeof(Thunk) 

//原因:在顺序运行指令时,EIP在运行一条指令后会自己主动增,这里当然增的是sizeof(Thunk) 

//又因为没有virtual指针,所以 m_thunk的地址就是this指向地址,可是运行此指令后EIP会自己主动加,

//所以EIP内容为(int)this+sizeof(Thunk) 

//所以 pFun=m_thunk.m_adrr+((int)this+sizeof(Thunk)),移项可得上式 





FlushInstructionCache(GetCurrentProcess(),  

                              &m_thunk, sizeof(m_thunk)); //强制刷新指令缓冲, 

                                       //目的是使指令CACHE与主存相一致 



    } 



 //实验的第一函数 

  void function() 

    { 

        



        // 初始化thunk 

         



        // 获得thunk代码地址 

        pFUN pFun = (pFUN)&(m_thunk); 



        // 调用StaticFun 

        pFun(); 



       

    } 

   static void Fun1() 

    { 

        cout << "this is Fun1" << endl; 

    } 



     



static void Fun2()  



cout << "this is Fun2" << endl; 







}; 



int main() 



   C *pC=new C; 



   pC->Init(C::Fun1); 

   pC->function(); //1 



   pC->Init(C::Fun2); 

    

   pC->function();//2 

    

   //请注意,上面调用同一个函数,第一个运行的是C::Fun1,第二个却运行的是C::Fun2 

   //这充分说明实现了多态性! 

    return 0; 

}

c++ THUNK技术的更多相关文章

  1. 理解ATL中的一些汇编代码(通过Thunk技术来调用类成员函数)

    我们知道ATL(活动模板库)是一套很小巧高效的COM开发库,它本身的核心文件其实没几个,COM相关的(主要是atlbase.h, atlcom.h),另外还有一个窗口相关的(atlwin.h), 所以 ...

  2. C++中的Thunk技术 / 非静态类成员函数作为回调函数 的实现方法

    原文:https://blog.twofei.com/616/ 用我的理解通俗地解释一下什么是C++中的Thunk技术吧! Thunk技术就是申请一段可执行的内存, 并通过手动构造CPU指令的形式来生 ...

  3. thunk技术

    Thunk : 将一段机器码对应的字节保存在一个连续内存结构里, 然后将其指针强制转换成函数. 即用作函数来执行,通常用来将对象的成员函数作为回调函数. #include "stdafx.h ...

  4. C++函数调用的反汇编过程及Thunk应用

    x86汇编基础知识 1. 汇编常用寄存器 esp,(Extended stack pointer)栈顶指针.因为x86的栈内存是向下扩展的,因此当push入栈时,esp–.pop出栈时,esp++.e ...

  5. Java多态与C++中多态的实现

    大牛的文章,值得拜读http://www.ibm.com/developerworks/cn/java/j-lo-polymorph/ 粘贴过来好多图片丢失了 /(ㄒoㄒ)/~~ 众所周知,多态是面向 ...

  6. Delphi对象变成Windows控件的前世今生(关键是设置句柄和回调函数)goodx

    ----------------------------------------------------------------------第一步,准备工作:预定义一个全局Win控件变量,以及一个精简 ...

  7. duilib底层机制剖析:窗体类与窗体句柄的关联

    转载请说明原出处,谢谢~~ 看到群里朋友有人讨论WTL中的thunk技术,让我联想到了duilib的类似技术.这些技术都是为了解决c++封装的窗体类与窗体句柄的关联问题. 这里是三篇关于thunk技术 ...

  8. [转]《深度探索C++对象模型》读书笔记[二]

    3.3 Data Member的存取1.   不管什么情况,每一个static data member只有一个实体,放在程序的data segment之中,每次程序取用static member,不管 ...

  9. 【高级】C++中虚函数机制的实现原理

    多态是C++中的一个重要特性,而虚函数却是实现多态的基石.所谓多态,就是基类的引用或者指针可以根据其实际指向的子类类型而表现出不同的功能.这篇文章讨论这种功能的实现原理,注意这里并不以某个具体的编译器 ...

随机推荐

  1. SQL从入门到基础–03 SQLServer基础1(主键选择、数据插入、数据更新)

    一.SQL语句入门 1. SQL语句是和DBMS“交谈”专用的语句,不同DBMS都认SQL语法. 2. SQL语句中字符串用单引号. 3. SQL语句中,对于SQL关键字大小写不敏感,对于字符串值大小 ...

  2. Windows计划任务 未能启动

    近期在windows server 2003上运行的备份脚本,在7月23日之后,没再运行,在计划任务里看到的状态是:未能启动.结果手动运行了一下备份脚本,没有问题,可以正常运行,但是在计划任务里为什么 ...

  3. ionic初体验

    inoic使用入门安装inoic1.安装nodejs2.通过npm install -g iomic 在全局安装ionic3.通过ionic --help来查看帮助(其他命令详见弹出提示脚本) 后续收 ...

  4. 关于PHP伪静态Rewrite设置

    Rewirte主要的功能就是实现URL的跳转和隐藏真实地址,基于Perl语言的正则 表达式规范.平时帮助我们实现拟静态,拟目录,域名跳转,防止盗链等   一.Apache配置:   1.支持httpd ...

  5. JS函数与call()apply()详解

    JavaScript中的每个函数都是一个对象. 因为函数都是对象,它们有自己的属性和方法.我们可以把它们看作数据(data). 函数和方法的区别? 函数立足于它们自己(例如:alert()), 而方法 ...

  6. Js自动截取字符串长度,添加省略号“……”

    JavaScript字符串处理函数,根据定义的长度截取字符串,超出部分裁掉追加……,很多时候网页上显示的内容需要缩成“...”该方法用于处理字符串显示固定长度,超长部分用“...”代替: /**参数说 ...

  7. python 的内置函数(1)

     19.内置函数:              abs():求绝对值              bool():求一个值是True or False ,其中False值有 0 ,空字符串'',None,空 ...

  8. Python3 如何优雅地使用正则表达式(详解六)

    修改字符串 我们已经介绍完如何对字符进行搜索,接下来我们讲讲正则表达式如何修改字符串. 正则表达式使用以下方法修改字符串: 方法 用途 split() 在正则表达式匹配的地方进行分割,并返回一个列表 ...

  9. 使用开源word操作组件DocX的记录

    1.DocX简介 1.1 简介 DocX是一个在不需要安装word的情况下对word进行操作的开源轻量级.net组件,是由爱尔兰的一个叫Cathal Coffey的博士生开发出来的.DocX使得操作w ...

  10. Spark保存到HDFS或本地文件相关问题

    spark中saveAsTextFile如何最终生成一个文件 http://www.lxway.com/641062624.htm 一般而言,saveAsTextFile会按照执行task的多少生成多 ...