天天看点

偷换windows窗口过程

    ​​ Window user32子动态库控件封装和消息分发浅析​​  这篇文章提到窗口程序在分发消息时会依次调用:UserCallWinProcCheckWow--->_InternalCallWinProc-->各个窗口程序的WndProc。各类控件的窗口程序不同,比如Button控件用的是Button_WndProc,程序通过_InternalCallWinProc最终进入Button_WndProc对Button事件处理。受到这篇文章的启示,我尝试用windbg修改calc.exe的窗口程序,并记录于此。

    网上一些文章提及万能消息断点时,无不例外的都会带到_InternalCallWinProc这个函数,综合各家所言和自己调试的过程,我得到如下结论:

1._InternalCallWinProc的函数接口可能为:

_InternalCallWinProc(WndProc*,Hwnd,UINT,WPARAM,LPARAM);      

参数1是程序在调用RegisterClass(Ex)时注册的窗口过程,参数2是接收消息的窗口的句柄,参数3是消息号。从参数2到参数5,类似于传递给WndProc接口的参数。

2.在_InternalCallWinProc函数中会以参数1传入的函数指针作为消息的窗口过程,将参数2-5压栈传给它并跳转到该窗口过程。可能的实现如下(xp):

_InternalCallWinProc()
{
    (*WndProc)(Hwnd,UINT,WPARAM,LPARAM);
}      

(win7)

_InternalCallWinProc()
{
    push LPARAM
    push WPARAM
    push UINT
    push Hwnd
    jmp WndProc
}      

    回到我的正题上,我要做的是偷换窗口过程。xp到win7都没有检测函数指针WndProc的合法性(至少在windbg中是这样),所以,我们可以把这个指针值偷换为其他代码(可以通过注入代码的形式实现)。作为演示的目的,我把calc.exe按键7的WndProc替换为ExitProcess函数,以下为实现步骤:

1.用spy++定位按键7的句柄:

偷换windows窗口过程

2.windbg attach到calc.exe,设置符号并查找符号_InternalCallWinProc

0:001> x *!*internalcall* @查找符号InternalCallWinProc地址
77d1870c USER32!InternalCallWinProc = <no type information>
0:001> x *!Button_* @查找Button控件符号地址
771a7725 comctl32!Button_CalcRect = <no type information>
0:001> bp USER32!InternalCallWinProc
0:001> g
Breakpoint 0 hit
USER32!InternalCallWinProc:
77d1870c 55              push    ebp
0:000> kb
ChildEBP RetAddr  Args to Child              
00>0007fd30 77d18816 7365912a 000101b2 00000219 USER32!InternalCallWinProc
01>0007fd98 77d189cd 00000000 7365912a 000101b2 USER32!UserCallWinProcCheckWow+0x150
02>0007fdf8 77d18a10 0007fee8 00000000 0007ff1c USER32!DispatchMessageWorker+0x306
03>0007fe08 010021a7 0007fee8 7c80b731 000a2348 USER32!DispatchMessageW+0xf
...
0007fff0 00000000 01012475 00000000 78746341 kernel32!BaseProcessStart+0x23
@栈回溯00>处的0x7365912a是计算机窗口过程的函数指针
0:000> ln 7365912a 
(7365912a)   msctfime!UIWndProc   |  (7365913a)   msctfime!CtfImeDestroyInputContext
Exact matches:
    msctfime!UIWndProc (<no parameter info>)
@查看地址0x7365912a附近的符号是msctfime!UIWndProc,有点像一个窗口过程      

3.准备下条件断点。来分析一下断点要满足的需求:如果我们在Button控件(Button控件的窗口过程为comctl32!Button_CalcRect)"7"(spy++显示句柄值0x001016A判断)上按下左键(消息值为0x201)时,使windbg中断。把这段话转变为windbg能理解的命令:

0:000> bp comctl32!Button_WndProc ".if(dwo(esp+4)==0001016A&dwo(esp+8)==201){.echo Button7Down;}.else{gc;}"
0:000> g      

其中dwo(esp+4)用于判断按键7的窗口句柄值,dwo(esp+8)用于判断消息值是否为左键按下。我们看下断点的效果:

偷换windows窗口过程

    当我按下7,windbg在输出窗口输出"Button7Down",看来断点生效了~再来看下调用堆栈:

0:000> kb
ChildEBP RetAddr  Args to Child              
00> 0007fd04 77d18734 0001016a 00000201 00000001 comctl32!Button_WndProc
01> 0007fd30 77d18816 771a8eb4 0001016a 00000201 USER32!InternalCallWinProc+0x28
02> 0007fd98 77d189cd 00000000 771a8eb4 0001016a USER32!UserCallWinProcCheckWow+0x150
03> 0007fdf8 77d18a10 0007fee8 00000000 0007ff1c USER32!DispatchMessageWorker+0x306
04> 0007fe08 010021a7 0007fee8 7c80b731 000a2348 USER32!DispatchMessageW+0xf
...
0007ffc0 7c817067 00330039 00360037 7ffd3000 calc+0x125e9
0007fff0 00000000 01012475 00000000 78746341 kernel32!BaseProcessStart+0x23      

按调用栈可以看到是USER32!InternalCallWinProc调用了comctl32!Button_WndProc,现在我们回过头来看下前面我得到的结论对不对----USER32!InternalCallWinProc的参数1存放了处理消息的窗口过程:

0:000> ln 771a8eb4 
(771a8eb4)   comctl32!Button_WndProc   |  (771a99c9)   comctl32!InitButtonClass
Exact matches:
    comctl32!Button_WndProc = <no type information>      

果不其然,0x771a8eb4是Button控件的消息处理函数!

    既然USER32!InternalCallWinProc对参数1传入的值不加甄别,我完全可以对USER32!InternalCallWinProc下同样的条件断点,触发断点后在函数入口处修改参数1的值,把他指向进程空间中其他一段可执行代码,那就ExitProcess了:

0:000> x *!*ExitProcess*
7c81cafa kernel32!ExitProcess = <no type information>
0:000> bp USER32!InternalCallWinProc ".if(dwo(esp+8)==0001016A&dwo(esp+c)==201){.echo Button7Down;}.else{gc;}"
0:000> g
Button7Down
eax=c0000000 ebx=00000000 ecx=40000000 edx=00000040 esi=771a8eb4 edi=0007fd6c
eip=77d1870c esp=0007fd34 ebp=0007fd98 iopl=0         ov up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a87
USER32!InternalCallWinProc:
77d1870c 55              push    ebp
0:000> kb
ChildEBP RetAddr  Args to Child              
0007fd30 77d18816 771a8eb4 0001016a 00000201 USER32!InternalCallWinProc
0007fd98 77d189cd 00000000 771a8eb4 0001016a USER32!UserCallWinProcCheckWow+0x150
@注意传入给InternalCallWinProc的函数指针位于参数2,所以要修改的是esp+8
0:000> ed 0007fd30+8 7c81cafa 
0:000> g
eax=000000c0 ebx=00000102 ecx=77d2b401 edx=0007f720 esi=000000d4 edi=00000000
eip=7c92e4f4 esp=00bfff24 ebp=00bfff88 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:    <--------------进程退出
7c92e4f4 c3              ret      

    修改参数2后,继续执行windbg马上就能看到进程退出,有意思吧?来看下任务栏的截图:

偷换windows窗口过程

calc.exe已经不复存在,偷换成功~

本篇完~

继续阅读