赞
踩
文本编辑器/文本编辑框是应用层常见的键盘处理程序。微软泄露的WinXP源码下有文本编辑器Notepad的实现:
Microsoft_leaked_source_code\nt5src\Source\XPSP1\NT\shell\osshell\accesory\notepad
文本编辑器的实现并不复杂,微软又(被迫)提供了Sample,因此本文就不重复造轮子了。本文从调试器的角度观察Notepad.exe如何消费键盘按键.
首先评估一下调试Notepad.exe的难易程度(虽然有源码,我还是装作没有):
既然猜测Notepad.exe是标准的窗口程序,那它一定按窗口程序的模板(如下)处理窗口消息,而作为键盘按键的消费者,WM_CHAR等按键消息亦包含其中:
- while (GetMessage((LPMSG)&msg, (HWND)NULL, 0, 0))
- {
- if (TranslateAccelerator(hwndNP, hAccel, (LPMSG)&msg) == 0)
- {
- TranslateMessage ((LPMSG)&msg);
- DispatchMessage ((LPMSG)&msg);
- }
- }
在跟踪消费者WM_CHAR的行为前,先要从茫茫众消息(窗体消息中有大量的鼠标移动的消息干扰分析)中筛选出WM_CHAR,思路如下:
定位GetMessage API;
分析GetMessage返回的消息,筛选出WM_CHAR消息;
下面看我分步实现上述思路:
先用IDA和windbg查找并定位Notepad.exe调用GetMessage API的身影:
- 0:001> x notepad!*main* ;先找符号winMain,再找GetMessage调用处
- 00007ff7`e4d0ad6c NOTEPAD!wWinMain (<no parameter info>)
-
- 0:001> uf NOTEPAD!wWinMain
- 00007ff7`e4d0b010 488d4d0f lea rcx,[rbp+0Fh] ;<--获得窗体消息msg变量的地址
- 00007ff7`e4d0b014 4533c0 xor r8d,r8d
- 00007ff7`e4d0b017 33d2 xor edx,edx
- 00007ff7`e4d0b019 48ff1500bc0100 call qword ptr [NOTEPAD!_imp_GetMessageW (00007ff7`e4d26c20)]
- 00007ff7`e4d0b020 0f1f440000 nop dword ptr [rax+rax]
- 00007ff7`e4d0b025 85c0 test eax,eax
简单说明上面windbg的输出:
L5处:GetMessage需要4个参数,参数1传入窗体消息MSG msg的地址。而我的OS是64位系统,所以Notepad.exe也是64位程序。64位程序依次通过rcx/rdx/r8/r9传入函数的前4个参数;
L9处:GetMessage已经返回,在此处下断点,查看MSG msg栈变量就可以获得窗体消息。
为了使windbg能正确解析各个成员变量,需要明确告知windbg从GetMessage返回的窗体消息是个MSG结构体。MSG定义在combase.dll中:
- 0:001> dt combase!MSG
- +0x000 hwnd : Ptr64 HWND__
- +0x008 message : Uint4B
- +0x010 wParam : Uint8B
- +0x018 lParam : Int8B
- +0x020 time : Uint4B
- +0x024 pt : tagPOINT
在GetMessage返回处下断点,windbg停下后解析MSG内容:
- 0:001> bp 00007ff7`e4d0b020
- 0:001> g
- Breakpoint 0 hit
-
- 0:000> dt combase!MSG [rbp+f]
- +0x000 hwnd : 0x00000000`001001fe HWND__
- +0x008 message : 0xf
- +0x010 wParam : 0
- +0x018 lParam : 0n0
- +0x020 time : 0xaa1dbe
- +0x024 pt : tagPOINT
其中:
L6处为窗口句柄,这和前面Spy++获得的窗口句柄值一致
L7处为消息类型,值0x0f对应WM_PAINT
- #define WM_SETTEXT 0x000C
- #define WM_GETTEXT 0x000D
- #define WM_GETTEXTLENGTH 0x000E
- #define WM_PAINT 0x000F
- #define WM_CLOSE 0x0010
修改一下前面的断点,让它变为条件断点(条件断点略复杂,请移步windbg设置条件断点),每当Notepad.exe中按键,windbg打印WM_CHAR:
- 0:001> bp 00007ff7`e4d0b020 ".block{r @$t0=poi(rbp+0xf+0x08);.if(@$t0==0x102){.printf @\"WM_CHAR enter\";gc;};.else{gc;}}"
- 0:000> g
- WM_CHAR enterWM_CHAR enter
Notepad.exe以文件映射的方式实现文本读写,它会将收到的按键内容暂存在所映射内存中,通过某种机制将这段内存内容显示在文本(文本编辑框)上。提一个问题,如果修改这段内存,是否导致最终文本内容被修改?以下面文本为例:
用Cheat Engine附加到Notepad进程,"Memory view"--Search--"Find memory"--在Find对话框中输入要查找的文本,同时勾选Unicode复选框输,即可在Cheat Engine中找到acpi.h中部分内容:
至此,可以确定Notepad.exe的内容被映射的内存块。
(PS:其实用windbg在内存映射中搜索指定字符串,也可以取得相同结果。然而第一次尝试搜索时我以ansi字符串的方式搜索,没有找到该字符,疑惑中改用Cheat Engine)
0:003> s -u 0x20181900000 L?100000 "State" #windbg 搜索指定Unicode string
借用前面搜索指定字符串的输出结果,可以确定文本内容被映射到内存地址:0000020181967050。
#搜索acpi.h文件中第一行文字,确定所在的内存起始地址: 0:001> s -u 0x20181900000 L?100000 "typedef struct _GAS_20 {" 00000201`81967050 0074 0079 0070 0065 0064 0065 0066 0020 t.y.p.e.d.e.f. . #以Unicode字符串形式打印起始地址的内容: 0:001> du 00000201`81967050 00000201`81967050 "typedef struct _GAS_20 {.. UI" 00000201`81967090 "NT8...AddrSpcID; //The " 00000201`819670d0 "address space where the data str" 00000201`81967110 "ucture or register exists... " 00000201`81967150 " " 00000201`81967190 "//Defined values are above " 00000201`819671d0 " " 00000201`81967210 " .. UINT8...RegBitWidth;" 00000201`81967250 "..//The size in bits of the give" 00000201`81967290 "n register. ...........//When ad" 00000201`819672d0 "dressing a data structure, this " 00000201`81967310 "field must be zero... UINT8.."
我们尝试修改该内存块,如果修改内存后直接会反应到文本上,那么可以证明Notepad确实通过内存映射的方式访问文件。
修改前我们再核对一下acpi.h的面貌,因为待会马上要整容了:
- #以Unicode string方式修改内存
- 0:004> eu 0x20181967050 "I don't know what to write"
- #查看修改结果
- 0:004> du 0x20181967050
- 00000201`81967050 "I don't know what to write UI"
- 00000201`81967090 "NT8...AddrSpcID; //The "
- 00000201`819670d0 "address space where the data str"
- 00000201`81967110 "ucture or register exists... "
由此,证明了我的猜想。
上一节提出了一个问题:Notepad通过某种机制将这段内存内容显示在文本(文本编辑框)上。这一节简单的回答这个问题。
a.输入端:Notepad接收到WM_CHAR消息后,通过DispatchMessage,将消息传给文本编辑框句柄hwndEdit(为什么hwndEdit就是文本编辑框的句柄?这个可以参考张银奎老师的《格蠹汇编》一书);
b.hwndEdit所在窗体的Callback处理WM_CHAR,将键盘消息插入到内存映射所对应的Unicode String的恰当位置;
c.输出端:由hwndEdit调用SetDlgItemText将Unicode String显示到Notepad.exe对应的文本编辑框。Notepad源码通过下列方式,从hwndEdit窗口句柄获得文本内容:
- hEText= (HANDLE) SendMessage( hwndEdit, EM_GETHANDLE, 0, 0 ); //获得文本句柄
- if( !hEText ) // silently return if we can't get it
- {
- return( bStatus );
- }
- pStart= LocalLock( hEText ); //获得文本
本文完,下一篇将从应用层进入驱动层,看下i8042.sys怎么处理键盘按键
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。