Thunk,thunk,thunk
---------------------------
作 者:intret
时 间:2011年3月13日星期日
源码格式: VS2008 工程,其实无所谓版本
源码下载:Thunk v0.4 by intret
---------------------------
【摘要】
Thunk是一种技术,你觉得它是不是Knuth(Donald.E.Knuth 爷爷的名字)反过来而得的呢?它做了运行时函数行为的更改,即当执行了Thunk代码,Thunk代码会转而执行类成员函数代码,而这段Thunk代码是精心构造的,包括参数入栈和堆栈平衡工作,当然ATL有ATL的Thunk,我也写了一段。
【用处】
在需要把类成员函数当做普通回调函数来使用的时候,比如窗口过程、线程的Proc、计时器的Proc等),请放心,几行汇编代码并不会影响窗口过程的调用速率。
目录
『一』函数调用约定(Call convention)
1.普通回调函数__stdcall方式的调用
CALLBACK在默认情况下被宏定义定义为:__stdcall.我们看窗口过程函数的实现和被调用:
LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
return 5;
}
void main()
{
//…主函数的准备工作
WindowProc(NULL, 1, 2, 3);
01361C8E push 3 // 第4个参数入栈
01361C90 push 2 // 第3个参数入栈
01361C92 push 1 // 第2个参数入栈
01361C94 push 0 // 第1个参数入栈
01361C96 call WindowProc (136128Fh) // 调用窗口过程函数,call导致下一条指令地址01361C96+6入栈
}
// …主函数的收尾工作
2.类成员函数__stdcall方式调用
class Wnd
{
public:
int __stdcall WndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return 0;
};
};
void main()
{
Wnd wnd;
wnd.WndProc( NULL, 2, 3, 4);
000F13EB push 4
000F13ED push 3
000F13EF push 2
000F13F1 push 0
000F13F3 lea eax,[wnd] // this指针地址
000F13F6 push eax // 至此压入了5个参数,编译器添加了this指针参数
000F13F7 call Wnd::WndProc (0F104Bh)
}
『二』汇编指令
1.CALL指令
call指令会导致『返回地址』(即call的下一条指令的地址)入栈,然后跳转到目标地址执行代码。
2.JMP指令
JMP指令因为有短跳和长跳:可以只修改IP,也可以同时修改CS和IP。
查询Intel80x86 OPCODES.pdf文件,无条件跳转有如下类型,引用如下:
JMP-Unconditional Jump (to sam segment)
short | 1110 1011 : 8-bit displacement |
direct | 1110 1001 : full displacement |
register indirect | 1111 1111 : 11 100 reg |
memory indirect | 1111 1111 : mod 100 r/m |
JMP – Unconditional Jump (to other segment)
direct intersegment | 1110 1010 : unsigned full offset, selector |
indirect intersegment | 1111 1111 : mod 101 r/m |
我们可以获取Wnd::WndProc类成员函数的地址0x0F104B,然后jmp至该地址,jmp之前,堆栈需要满足的条件是:
-
相对于参数入栈前,入栈后堆栈里面多了6个数。(当然call指令导致返回地址入栈也算在其中)
-
第5个入栈的参数是Wnd对象的地址,即this指针。(然后才到call导致的返回地址入栈)
但是,消息处理函数总是会这样调用窗口过程函数,使得窗口过程可以得到消息处理机会:
此时要jmp到Wnd::WndProc之前,堆栈状态不能满足以上条件。
call指令导致的结果是什么呢?我翻开王爽的《汇编语言》,确认了一下它做两件事:
-
IP或CS:IP入栈。(导致堆栈状态的变化)
-
转移。(导致目标地址的代码得到执行)
也就是说,返回地址(下一条指令的地址)的入栈是call导致的,那么this指针在call指令之前就可以自由入栈,而且函数局部变量的寻址是根据EBP的偏移定位的。this入栈导致的栈顶指针ESP的变化不会影响局部变量的定位。
回调函数为__stdcall,类成员函数为__stdcall的Thunk code如下:
pthunk | FF 34 24 | push | dword ptr [esp] |
pthunk+3 | C7 44 24 04 44 33 22 00 | mov | dword ptr [esp+4],223344h |
pthunk+11 | E9 33 22 11 00 | jmp | 0x00112233 |
I can write no longer…去年非常用心地写,现在已没有兴趣写了,那就共享之,CSDN源码下载地址在上面。