|

楼主 |
发表于 2007-4-9 17:13:22
|
显示全部楼层
//pop ecx,Proc已压栈,弹出Proc到ecx <br/> thunk->Code[0] = 0x59; //pop ecx<br/> <br/> //mov dword ptr [esp+0x4],this<br/> //Proc已弹出,栈顶是返回地址,紧接着就是HWND了。<br/> //[esp+0x4]就是HWND<br/> thunk->Code[1] = 0xC7; // mov<br/> thunk->Code[2] = 0x44; // dword ptr<br/> thunk->Code[3] = 0x24; // disp8[esp]<br/> thunk->Code[4] = 0x04; // +4<br/> thunk->Window = this;<br/> <br/> //偷梁换柱成功!跳转到Proc<br/> //jmp [ecx]<br/> thunk->Jmp = 0xFF; // jmp [r/m]32<br/> thunk->ECX = 0x21; // [ecx]<br/> <br/> m_thunk = (WNDPROC)thunk;<br/> return m_thunk;<br/> }<p></p><p> 这样m_thunk虽然是一个结构,但其数据是一段可执行的代码,而其类型又是WNDPROC,系统就会忠实地按窗口过程规则调用这段代码,m_thunk就把Window字段里记录的this指针替换掉堆栈中的hWnd参数,然后跳转到静态的stdProc:</p><p><table bordercolor="#55aaff" cellspacing="0" cellpadding="0" rules="none" width="500" align="center" bgcolor="#ddedfb" border="1"><tbody><tr><td width="10"><br/></td><td> //本回调函数的HWND调用之前已由m_thunk替换为对象指针<br/> LRESULT WINAPI CMyWnd::stdProc(HWND hWnd,UINT uMsg,UINT wParam,LONG lParam)<br/> {<br/> CMyWnd* w = (CMyWnd*)hWnd;<br/> <br/> return w->WindowProc(uMsg,wParam,lParam);<br/> }</td></tr></tbody></table></p><p> 这样就把窗口过程转向到了类成员函数WindowProc,当然这样还有一个问题,就是窗口句柄hWnd还没来得及记录,因此一开始的窗口过程应该先定位到静态的InitProc,CreateWindow的时候给最后一个参数,即初始化参数赋值为this指针:</p><p><table bordercolor="#55aaff" cellspacing="0" cellpadding="0" rules="none" width="500" align="center" bgcolor="#ddedfb" border="1"><tbody><tr><td width="10"><br/></td><td> CreateWindowEx(<br/> dwExStyle,<br/> szClass,<br/> szTitle,<br/> dwStyle,<br/> x,<br/> y,<br/> width,<br/> height,<br/> hParentWnd,<br/> hMenu,<br/> hInst,</td></tr></tbody></table></p>this //初始化参数<br/> );, <p> 在InitProc里面取出该指针:</p><p> LRESULT WINAPI CMyWnd::InitProc(HWND hWnd,UINT uMsg,UINT wParam,LONG lParam)<br/> { <br/> if(uMsg == WM_NCCREATE)<br/> {<br/> CMyWnd *w = NULL;<br/> w = (CMyWnd*)((LPCREATESTRUCT)lParam)->lpCreateParams;<br/> if(w)<br/> {<br/> //记录hWnd<br/> w->m_hWnd = hWnd;<br/> <br/> //改变窗口过程为m_thunk<br/> SetWindowLong(hWnd,GWL_WNDPROC,(LONG)w-CreateThunk());<br/> return (*(WNDPROC)(w->GetThunk()))(hWnd,uMsg,wParam,lParam); <br/> }<br/> } <br/> return DefWindowProc(hWnd,uMsg,wParam,lParam);<br/> }</p><p></p><p> 这样就大功告成。</p><p> 窗口过程转发流程:</p><p> 假设已建立CMyWnd类的窗口对象 CMyWnd *window,初始化完毕后调用window->Create,这时Create的窗口其窗口过程函数是静态CMyWnd::InitWndProc</p><p align="center"><img src="http://www.pconline.com.cn/pcedu/empolder/gj/vc/0409/pic/040929mfc.gif" alt=""/></p><p> 题外话:thunk的汇编代码全部写在注释里了,把这段汇编转成数据可费了不少劲,当时手头没有合适的工具,只有一本《8086/8088汇编语言程序设计》,根据附录中的指令码汇总表转成机器码数据,那里面根本没有EAX,ECX,ESP等的概念,只能连蒙带猜加调试,非法操作了n(n>10)回才得到那些数据,当时真是长出了一口气:TNND,终于搞定了!:-)</p><p> </p> |
|