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

posted @ 2009-10-17 19:27  荷包蛋  阅读(3388)  评论(0编辑  收藏  举报