c++ thunk

来源:互联网 发布:电子工程师必备app软件 编辑:程序博客网 时间:2024/05/29 17:09

之前参加了一次面试,问到了SetTimer中回调设计成类成员函数的问题.后来想写一下,但是一直没有找到相关文章.后来lori哥指点,原来这就是C++ Thunk技术.

 

Thunk : 将一段机器码对应的字节保存在一个连续内存结构里, 
然后将其指针强制转换成函数. 即用作函数来执行.

这份代码用于将C语方的回调函数转换为具有this指针的C++成
员函数.

在Windows系统编程中, 可以将一些系统回函数封装成C++类成
员函数, 例如

// 定时器回调函数
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, 
UINT_PTR idEvent, DWORD dwTime);

// 线程回调函数
DWORD WINAPI ThreadProc(LPVOID lpParameter);

// 窗口过程回调函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, 
WPARAM wParam, LPARAM lParam);

 

/********************************************************************

 

Copyright 2006-2008 ZHANG Luduo. All Rights Reserved.

 

Permission to use, copy, modify, distribute and sell this software

and its documentation for any purpose is hereby granted without fee,

provided that the above copyright notice appear in all copies and

that both that copyright notice and this permission notice appear

in supporting documentation.

 

********************************************************************/

 

/*

 

代码描述 : 

 

Thunk : 将一段机器码对应的字节保存在一个连续内存结构里, 

然后将其指针强制转换成函数. 即用作函数来执行.

这份代码用于将C语方的回调函数转换为具有this指针的C++成

员函数.

 

在Windows系统编程中, 可以将一些系统回函数封装成C++类成

员函数, 例如

 

// 定时器回调函数

VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, 

UINT_PTR idEvent, DWORD dwTime);

 

// 线程回调函数

DWORD WINAPI ThreadProc(LPVOID lpParameter);

 

// 窗口过程回调函数

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, 

WPARAM wParam, LPARAM lParam);

 

2008-10-22修正了在Windows XP SP2, Windows server 2003环

境下开启DEP功能后运行崩溃的BUG

 

关于作者:

 

姓名  - 张鲁夺, ZHANG Luduo

MSN   - zhangluduo@msn.com

Email - zhangluduo@163.com

QQ群  - 34064264, 56918155, 56918241

网站  - http://www.oopfans.com

 

为所有爱我的人和我爱的人努力!

 

*/

 

#ifndef _THUNK_H

#define _THUNK_H

 

// type redefinition

typedef unsigned char  u1byte; // an 8 bit unsigned character type

typedef unsigned short u2byte; // a 16 bit unsigned integer type

typedef unsigned long  u4byte; // a 32 bit unsigned integer type

typedef void*          pvoid;  // a 32 bit undefined type pointer

 

class Thunk

{

public:

 

Thunk();

/*virtual*/ ~Thunk();

 

pvoid Thiscall(pvoid pThis, u4byte MemberFxnAddr);

pvoid Stdcall (pvoid pThis, u4byte MemberFxnAddr);

 

template <typename T> 

static u4byte GetMemberFxnAddr(T MemberFxnName)

{

union 

{

T From; 

u4byte To;

}union_cast;

union_cast.From = MemberFxnName;

return union_cast.To;

}

 

private:

 

#pragma pack (push, 1)

typedef struct _BYTECODE_THISCALL

{

u1byte Mov;     // 0xB9

u4byte This;    // this pointer

u1byte Jmp;     // 0xE9

u4byte Adrr;    // relative jmp

}BYTECODE_THISCALL, *PBYTECODE_THISCALL;

 

typedef struct _BYTECODE_STDCALL

{

u1byte Push[3]; // push dword ptr[esp] ;

u4byte Move;    // mov dword ptr [esp + 4], ?? ?? ?? ?? ;

u4byte This;    // this pointer

u1byte Jmp;     // 0xE9

u4byte Adrr;    // relative jmp

}BYTECODE_STDCALL, *PBYTECODE_STDCALL;

 

#pragma pack (pop)

 

// u1byte m_BytecodeThiscall[10];

// u1byte m_BytecodeStdcall [16];

 

BYTECODE_THISCALL m_BytecodeThiscall;

BYTECODE_STDCALL  m_BytecodeStdcall;

 

Thunk* m_pThis;

};

 

#endif // #define _THUNK_H

 

/********************************************************************

 

Copyright 2006-2008 ZHANG Luduo. All Rights Reserved.

 

Permission to use, copy, modify, distribute and sell this software

and its documentation for any purpose is hereby granted without fee,

provided that the above copyright notice appear in all copies and

that both that copyright notice and this permission notice appear

in supporting documentation.

 

********************************************************************/

 

/*

 

代码描述 : 

 

Thunk : 将一段机器码对应的字节保存在一个连续内存结构里, 

然后将其指针强制转换成函数. 即用作函数来执行.

这份代码用于将C语方的回调函数转换为具有this指针的C++成

员函数.

 

在Windows系统编程中, 可以将一些系统回函数封装成C++类成

员函数, 例如

 

// 定时器回调函数

VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, 

UINT_PTR idEvent, DWORD dwTime);

 

// 线程回调函数

DWORD WINAPI ThreadProc(LPVOID lpParameter);

 

// 窗口过程回调函数

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, 

WPARAM wParam, LPARAM lParam);

 

2008-10-22修正了在Windows XP SP2, Windows server 2003环

境下开启DEP功能后运行崩溃的BUG

 

关于作者:

 

姓名  - 张鲁夺, ZHANG Luduo

MSN   - zhangluduo@msn.com

Email - zhangluduo@163.com

QQ群  - 34064264, 56918155, 56918241

网站  - http://www.oopfans.com

 

为所有爱我的人和我爱的人努力!

 

*/

 

//#include "StdAfx.h"

#include "Thunk.h"

#include <windows.h>

 

Thunk::Thunk()

{

m_pThis = (Thunk*)VirtualAlloc(NULL, sizeof (Thunk), 

MEM_COMMIT, PAGE_EXECUTE_READWRITE);

}

 

Thunk::~Thunk()

{

if (NULL == m_pThis)

return;

VirtualFree(m_pThis, 0, MEM_RELEASE);

}

 

pvoid Thunk::Thiscall(pvoid pThis, u4byte MemberFxnAddr)

{

/**

encoded machine instruction   equivalent assembly languate notation

---------------------------   -------------------------------------

B9 ?? ?? ?? ??                mov ecx, pThis  ; load ecx with this pointer

E9 ?? ?? ?? ??                jmp target addr ; jump to target function

*/

 

/**

#define GETBYTE(b, n) ((u1byte)(b >> ((n - 1) * 8) & 0x000000FF))

 

u4byte dwJmpAdr = MemberFxnAddr - 

(u4byte)(&(m_pThis->m_BytecodeThiscall)) - 

sizeof (m_pThis->m_BytecodeThiscall);

 

m_pThis->m_BytecodeThiscall[0] = 0xB9;

m_pThis->m_BytecodeThiscall[1] = GETBYTE((u4byte)pThis, 1);

m_pThis->m_BytecodeThiscall[2] = GETBYTE((u4byte)pThis, 2);

m_pThis->m_BytecodeThiscall[3] = GETBYTE((u4byte)pThis, 3);

m_pThis->m_BytecodeThiscall[4] = GETBYTE((u4byte)pThis, 4);

m_pThis->m_BytecodeThiscall[5] = 0xE9;

m_pThis->m_BytecodeThiscall[6] = GETBYTE((u4byte)dwJmpAdr, 1);

m_pThis->m_BytecodeThiscall[7] = GETBYTE((u4byte)dwJmpAdr, 2);

m_pThis->m_BytecodeThiscall[8] = GETBYTE((u4byte)dwJmpAdr, 3);

m_pThis->m_BytecodeThiscall[9] = GETBYTE((u4byte)dwJmpAdr, 4);

 

FlushInstructionCache(GetCurrentProcess(), 

&(m_pThis->m_BytecodeThiscall), 

sizeof (m_BytecodeThiscall));

 

return &(m_pThis->m_BytecodeThiscall);

*/

 

m_pThis->m_BytecodeThiscall.Mov  = 0xB9;

m_pThis->m_BytecodeThiscall.This = (u4byte)pThis;

m_pThis->m_BytecodeThiscall.Jmp  = 0xE9;

m_pThis->m_BytecodeThiscall.Adrr = MemberFxnAddr - 

(u4byte)(&(m_pThis->m_BytecodeThiscall)) - 

sizeof (BYTECODE_THISCALL);

 

FlushInstructionCache(GetCurrentProcess(), 

&(m_pThis->m_BytecodeThiscall), 

sizeof (BYTECODE_THISCALL));

 

return &(m_pThis->m_BytecodeThiscall);

}

 

pvoid Thunk::Stdcall (pvoid pThis, u4byte MemberFxnAddr)

{

/**

encoded machine instruction   equivalent assembly languate notation

---------------------------   -------------------------------------

FF 34 24                      push dword ptr [esp]          ; save (or duplicate)

; the return Address into stack

C7 44 24 04 ?? ?? ?? ??       mov  dword ptr [esp+4], pThis ; overwite the old

; return address with 'this pointer'

E9 ?? ?? ?? ??                jmp  target addr              ; jump to target function

*/

 

/**

#define GETBYTE(b, n) ((u1byte)(b >> ((n - 1) * 8) & 0x000000FF))

 

u4byte dwJmpAdr = MemberFxnAddr - 

(u4byte)(&(m_pThis->m_BytecodeStdcall)) - 

sizeof (m_pThis->m_BytecodeStdcall);

 

m_pThis->m_BytecodeStdcall[ 0] = 0xFF;

m_pThis->m_BytecodeStdcall[ 1] = 0x34;

m_pThis->m_BytecodeStdcall[ 2] = 0x24;

m_pThis->m_BytecodeStdcall[ 3] = 0xC7;

m_pThis->m_BytecodeStdcall[ 4] = 0x44;

m_pThis->m_BytecodeStdcall[ 5] = 0x24;

m_pThis->m_BytecodeStdcall[ 6] = 0x04;

m_pThis->m_BytecodeStdcall[ 7] = GETBYTE((u4byte)pThis, 1);

m_pThis->m_BytecodeStdcall[ 8] = GETBYTE((u4byte)pThis, 2);

m_pThis->m_BytecodeStdcall[ 9] = GETBYTE((u4byte)pThis, 3);

m_pThis->m_BytecodeStdcall[10] = GETBYTE((u4byte)pThis, 4);

m_pThis->m_BytecodeStdcall[11] = 0xE9;

m_pThis->m_BytecodeStdcall[12] = GETBYTE((u4byte)dwJmpAdr, 1);

m_pThis->m_BytecodeStdcall[13] = GETBYTE((u4byte)dwJmpAdr, 2);

m_pThis->m_BytecodeStdcall[14] = GETBYTE((u4byte)dwJmpAdr, 3);

m_pThis->m_BytecodeStdcall[15] = GETBYTE((u4byte)dwJmpAdr, 4);

 

FlushInstructionCache(GetCurrentProcess(), 

&(m_pThis->m_BytecodeStdcall), 

sizeof (m_BytecodeStdcall));

 

return &(m_pThis->m_BytecodeStdcall);

*/

 

m_pThis->m_BytecodeStdcall.Push[0] = 0xFF;

m_pThis->m_BytecodeStdcall.Push[1] = 0x34;

m_pThis->m_BytecodeStdcall.Push[2] = 0x24;

m_pThis->m_BytecodeStdcall.Move    = 0x042444C7;

m_pThis->m_BytecodeStdcall.This    = (u4byte)pThis;

m_pThis->m_BytecodeStdcall.Jmp     = 0xE9;

m_pThis->m_BytecodeStdcall.Adrr    = MemberFxnAddr - 

(u4byte)(&(m_pThis->m_BytecodeStdcall)) - 

sizeof (BYTECODE_STDCALL);

FlushInstructionCache(GetCurrentProcess(), 

&(m_pThis->m_BytecodeStdcall), 

sizeof (BYTECODE_STDCALL));

 

return &(m_pThis->m_BytecodeStdcall);

}

原创粉丝点击