通用 Thunk

http://tech.ddvip.com   2008年05月25日    社区交流

内容摘要:这篇文章提出了一种基于Thunk技术,让一个成员函数成为一个回调函数的通用方法。文章主要讨论原理,同时也提供了一份实现和示例。

  PUSH 1212 ; 使得堆栈增加4

  CALL func; 使得堆栈也增加4(因为返回地址也被压入堆栈)

  0x50000:...;被调用者返回这里,我们假设这里的地址是0x50000

  调用者希望被调用者使用 RET 4 (使得堆栈减少8:参数1212使用4,返回地址0x50000也使用4)来平衡堆栈,所以在这之后没有多余的机器码。所以,在这之后,堆栈是这个样子:

...
1212
0x50000 <- ESP

  然后,我们来看看使用__thiscall 的被调用者所希望的参数和返回地址。一个真正的成员函数被调用时。

C obj;
obj.func(1212);

  编译器以这样的方式准备参数:

PUSH 1212;
MOV ECX,obj;
CALL C::func

  所以,在这之后,堆栈是这个样子:


1212
0x50000 <- ESP

  ECX 保存着 this 指针。

  这也就是被调用者(void __thiscall C::func(int); ) 需要的形式。

  第3,我们看看被调用者如何返回。

  事实上,它使用 RET 4 来返回到0x50000

  所以,我们唯一需要做的就是准备好this指针,然后跳转到成员函数。(不需要更多的工作,参数和返回值已在正确位置,堆栈也将被正确的平衡。)

  设计 ThisToStd

  在我们设计第1个,也是最简单的类 ThisToStd 之前,我们还需要3种信息。

  1、我们需要一种得到函数地址的方法。

  对于数据指针,我们可以转化(cast)它到一个 int 值

void *p = &someValue;
int address = reinterpret_cast<int>(p);
/* 如果检查对64位机的可移植性,将会得到一个警告。不过可以忽略它,因为这个thunk只用在32位机上^_^*/
不同于数据指针,函数指针有更多的限制。void __stdcall fun(int) { … }
void C::fun(int) {}
//int address = (int)fun; // 不允许
//int address = (int)&C::fun; // 同样错误
有2种方法来进行一个强力的转化template<typename dst_type,typename src_type>
dst_type pointer_cast(src_type src) {
  return *static_cast<dst_type*>( static_cast<void*>(&src) );
}
template<typename dst_type,typename src_type>
dst_type union_cast(src_type src) {
  union {
    src_type src;
    dst_type dst;
  } u = {src};
  return u.dst;
}
所以,我们可以实现一个方法template<typename Pointer>
int PointerToInt32(Pointer pointer)
{
 return pointer_cast<int>(pointer); // or union_cast<int>(pointer);
}
int address = PointerToInt32(&fun); // 可以
int address = (int)&C::fun; // 也可以
更多详细信息见 ThunkBase.h

来源:vckbase    作者:OwnWaterloo    责编:豆豆技术应用

正在加载评论...