Thunk,thunk,thunk

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等),请放心,几行汇编代码并不会影响窗口过程的调用速率。

 

    

 

目录

【摘要】    1

【用处】    1

『一』函数调用约定(Call convention)    2

1.普通回调函数__stdcall方式的调用    2

2.类成员函数__stdcall方式调用    2

『二』汇编指令    3

1.CALL指令    3

2.JMP指令    3

 

 

『一』函数调用约定(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之前,堆栈需要满足的条件是:

  1. 相对于参数入栈前,入栈后堆栈里面多了6个数。(当然call指令导致返回地址入栈也算在其中)
  2. 第5个入栈的参数是Wnd对象的地址,即this指针。(然后才到call导致的返回地址入栈)

 

但是,消息处理函数总是会这样调用窗口过程函数,使得窗口过程可以得到消息处理机会:

 

此时要jmp到Wnd::WndProc之前,堆栈状态不能满足以上条件。

call指令导致的结果是什么呢?我翻开王爽的《汇编语言》,确认了一下它做两件事:

  1. IP或CS:IP入栈。(导致堆栈状态的变化)
  2. 转移。(导致目标地址的代码得到执行)

 

也就是说,返回地址(下一条指令的地址)的入栈是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源码下载地址在上面。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值