从应用层到MCU,看Windows处理键盘输入 [1.在应用层调试Notepad.exe (按键消费者)]

文本编辑器/文本编辑框是应用层常见的键盘处理程序。微软泄露的WinXP源码下有文本编辑器Notepad的实现:

Microsoft_leaked_source_code\nt5src\Source\XPSP1\NT\shell\osshell\accesory\notepad

文本编辑器的实现并不复杂,微软又(被迫)提供了Sample,因此本文就不重复造轮子了。本文从调试器的角度观察Notepad.exe如何消费键盘按键.

  1. Notepad接收WM_CHAR

首先评估一下调试Notepad.exe的难易程度(虽然有源码,我还是装作没有):

无壳且看着像是C++编译器生成

Notepad.exe属于标准的Windows窗口程序

既然猜测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,思路如下:

  1. 定位GetMessage API;

  1. 分析GetMessage返回的消息,筛选出WM_CHAR消息;

下面看我分步实现上述思路:

  1. 定位GetMessage API。

先用IDA和windbg查找并定位Notepad.exe调用GetMessage API的身影:

根据IDA分析,Notepad.exe在WinMain中进行消息循环

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栈变量就可以获得窗体消息。

  1. 分析GetMessage返回的消息消息,筛选出WM_CHAR消息

为了使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上输入几个字母,windbg艰难的吐出几个日志。效果挺好,就是挺卡的

  1. Notepad处理WM_CHAR/显示输出

Notepad.exe以文件映射的方式实现文本读写,它会将收到的按键内容暂存在所映射内存中,通过某种机制将这段内存内容显示在文本(文本编辑框)上。提一个问题,如果修改这段内存,是否导致最终文本内容被修改?以下面文本为例:

测试文本内容

  1. 验证Notepad是以文件映射的方式更新文本内容:

用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

windbg的搜索结果

借用前面搜索指定字符串的输出结果,可以确定文本内容被映射到内存地址: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...    "

测试文本修改后的内容

由此,证明了我的猜想。

  1. 链接键盘输入和显示输出过程

上一节提出了一个问题: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怎么处理键盘按键

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Windows 操作系统在过去几年中得到了显着更新。 但是,默认的 Windows 记事本在 20 年内未更新。 如果你需要一个简单的编程任务的文本编辑器,请考虑使用 Notepad3 中文版吧!我们使用默认 Windows 记事本当前缺少的所有功能创建了 Notepad3。 Notepad3 是一个快速,轻量级的基于 Scintilla 的文本编辑器,具有语法高亮功能。 它占用的内存很小,但功能强大,足以处理大多数编程工作。 Windows 记事本替代工具 Notepad3 中文多语特别版 Windows 记事本替代工具 Notepad3 中文多语特别版 基本 Notepad3 功能 此外,Notepad3 具有以下功能:代码折叠,括号匹配,自动缩进,字自动完成,转换各种格式(ASCII,UTF-8和UTF-16)之间的字符编码,换行格式转换(在 DOS 之间(CR/LF),Unix(LF)和 Macintosh(CR)格式),多个撤销或重做,书签和基于正则表达式的查找和替换。 支持的语言 以前我们提到 Notepad3 几乎可以处理任何编程工作,我们不会撒谎。只需查看下面支持的编程语言列表,找到您最喜欢的。 它为 ASP,汇编语言,AutoHotkey,AutoIt3,AviSynth,Bash,BAT,C,C ++,C#,CMake,CoffeeScript,通用网关接口(CGI),层叠样式表(CSS),DIFF,HTML,INF 提供语法高亮支持。 INI,Inno Setup,Java,JavaScript,LaTeX,Lua,Markdown,NSIS,Pascal,Perl,PHP,Python,REG,Ruby,SQL,Tcl,Visual Basic(VB),VBScript,VHDL 脚本,XHTML,XML,YAML ,D 源脚本,Go 源脚本,JSON,Makefile,MATLAB,Nim 源代码,Power Shell 脚本,资源脚本,Shell 脚本以及对 NFO ANSI 艺术的改进支持。 下载 Notepad3 Notepad3 有两种风格。用于将其安装到计算机并可选择替换默认 Windows 记事本的设置。便携式版本可直接从 U 盘或便携式硬盘驱动器运行。只需下载您喜欢的任何一个。
### 回答1: 在 Windows 中执行 "notepad.exe /etc/wsl.conf" 找不到系统路径的原因是因为 /etc/wsl.conf 是一个 Linux 系统中的路径,而不是 Windows 系统中的路径。 Windows Subsystem for Linux (WSL) 是一个可以在 Windows 中运行 Linux 命令行程序的功能,但是 Windows 程序和 Linux 程序在文件系统上是独立的。因此,在 Windows 中,无法直接访问 Linux 系统的文件路径,例如 /etc/wsl.conf。 如果要在 Windows 中编辑 WSL 的配置文件,可以使用 Windows 上的文本编辑器,如 Notepad,通过 WSL 命令行将文件复制到 Windows 中的临时目录,然后再在 Windows 中打开文件进行编辑。例如,可以使用以下命令: ``` cp /etc/wsl.conf /mnt/c/temp/wsl.conf notepad.exe C:\temp\wsl.conf ``` ### 回答2: 当在Windows中执行`notepad.exe /etc/wsl.conf`命令时,出现找不到系统路径的错误,可能有以下几个原因: 1. WSL未安装或未启用:Windows Subsystem for Linux(WSL)是一个在Windows上运行Linux操作系统的兼容层,它需要在系统中进行安装并启用才能执行Linux命令。如果未安装WSL或未启用WSL,系统将无法找到`/etc/wsl.conf`路径。 2. WSL版本不兼容:如果WSL版本较旧且不支持将Linux文件系统与Windows文件系统进行集成,那么操作系统将无法找到`/etc/wsl.conf`路径。在这种情况下,您可以尝试升级WSL或使用适用于您的WSL版本的其他命令。 3. 文件路径错误:`/etc/wsl.conf`是Linux系统中存储WSL配置文件的位置。在Windows下,文件路径与Linux系统不同,因此操作系统将无法找到该路径。您可以尝试使用Windows的文件浏览器或命令行找到正确的文件路径。 4. 文件不存在:`/etc/wsl.conf`是Linux系统中的一个文件,如果该文件不存在,操作系统将报错。请确保在执行命令之前在WSL环境中创建了相应的`wsl.conf`文件。 总之,要解决"找不到系统路径"的错误,您需要确保WSL已正确安装和启用,使用正确的文件路径,并确保所需的文件存在于该路径中。如果问题仍然存在,您可能需要参考WSL的官方文档或进行更详细的排查。 ### 回答3: 当在Windows系统上执行"notepad.exe /etc/wsl.conf"命令时,出现找不到系统路径的错误,可能有以下几个原因: 1. 文件路径错误:"/etc/wsl.conf"是一个Linux系统的文件路径,而Windows系统的文件路径是以驱动器字母和反斜杠(\)开头的。因此,当执行该命令时,Windows系统无法找到相应的路径。 2. WSL未安装或未启用:WSL(Windows Subsystem for Linux)是Windows系统上的Linux子系统,可以允许在Windows上运行Linux应用程序。如果WSL未安装或未启用,那么在执行Linux命令时会出现找不到系统路径的错误。 3. 文件不存在:如果文件"/etc/wsl.conf"在WSL子系统中不存在,执行该命令时会出现找不到系统路径的错误。在执行命令之前,应确保文件存在于正确的路径中。 解决此问题的方法是: 1. 通过更改命令来使用适用于Windows的正确文件路径,例如使用"notepad.exe C:\path\to\wsl.conf"。 2. 确保WSL已正确安装并启用。可以通过在Windows的控制面板或设置中搜索"启用或关闭Windows功能"来检查WSL是否已安装并启用。 3. 在WSL子系统中检查文件是否存在于"/etc/wsl.conf"路径中。如果文件不存在,可以创建新的wsl.conf文件并将其放置在该位置。 总之,出现找不到系统路径的错误可能是由于文件路径错误、WSL未安装或未启用,以及文件不存在所致,需要根据具体情况进行相应的解决方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值