Thunk:根据编译器的调用规范,在代码中人工设置函数调用方式。
作用:修改回调函数的参数,将类的成员函数设置为回调函数。
原理:
以窗口函数为例 LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
stdcall的调用方式
push LPARAM
push WPARAM
push UINT
push HWND
call WNDPROC
要修改参数在call前加一句代码:mov dword ptr [esp+0x4], value
现在用一个结构体保存机器码来替代 call 的代码
typedef struct _STDCALL_
{
unsigned int mov; // mov dword ptr [esp + 4], value ;
unsigned int value; // value
unsigned char jmp; // 0xE9
unsigned int addr; // relative jmp
} STDCALL;
在可执行的内存中分配一段内存存放这个结构体,就可以把这个结构体当函数调用了,而且修改了第一个参数的值
类似的 thiscall 是将 this 指针放在了 ecx 寄存器,只要使用同样的方法就能将 thiscall 变成 stdcall
typedef struct _THISCALL_
{
unsigned char mov; // 0xB9
unsigned int obj; // this pointer
unsigned char jmp; // 0xE9
unsigned int addr; // relative jmp
} THISCALL;
封装好的类:
template<class T>
class Thunk
{
typedef ::LRESULT (T::* WndProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM);
public:
Thunk()
{
m_thunk = (Thunk*)VirtualAlloc(NULL, sizeof (Thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}
~Thunk()
{
if (NULL != m_thunk) VirtualFree(m_thunk, 0, MEM_RELEASE);
}
template<typename R, typename U>
R Thiscall(T* obj, U fn)
{
m_thunk->m_thiscall.mov = 0xB9;
m_thunk->m_thiscall.obj = reinterpret_cast<unsigned int>(obj);
m_thunk->m_thiscall.jmp = 0xE9;
m_thunk->m_thiscall.addr = union_cast<unsigned int>(fn) - (unsigned int)(&(m_thunk->m_thiscall)) - sizeof(THISCALL);
FlushInstructionCache(GetCurrentProcess(), &(m_thunk->m_thiscall), sizeof(THISCALL));
return (R)&(m_thunk->m_thiscall);
}
template<typename R, typename U>
R Stdcall (T* obj, U fn)
{
m_thunk->m_stdcall.mov = 0x042444C7;
m_thunk->m_stdcall.obj = reinterpret_cast<unsigned int>(obj);
m_thunk->m_stdcall.jmp = 0xE9;
m_thunk->m_stdcall.addr = union_cast<unsigned int>(fn) - (unsigned int)(&(m_thunk->m_stdcall)) - sizeof(STDCALL);
FlushInstructionCache(GetCurrentProcess(), &(m_thunk->m_stdcall), sizeof(STDCALL));
return (R)&(m_thunk->m_stdcall);
}
private:
#pragma pack (push, 1)
typedef struct _THISCALL_
{
unsigned char mov; // 0xB9
unsigned int obj; // this pointer
unsigned char jmp; // 0xE9
unsigned int addr; // relative jmp
} THISCALL;
typedef struct _STDCALL_
{
unsigned int mov; // mov dword ptr [esp + 4], obj;
unsigned int obj; // this pointer
unsigned char jmp; // 0xE9
unsigned int addr; // relative jmp
} STDCALL;
#pragma pack (pop)
THISCALL m_thiscall;
STDCALL m_stdcall;
Thunk* m_thunk;
};