天天看點

偷換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已經不複存在,偷換成功~

本篇完~

繼續閱讀