从应用层到MCU,看Windows处理键盘输入 [2.a.1.传球手User32.dll]

news2024/11/18 4:17:22

副标题:精准型消息断点

引言1.

前文作为系列的开篇,我们站在Notepad.exe的视角,看它接过系统传来的消息,交由Notepad的窗口处理函数(WndProc)进行处理的过程。User32.dll!DispatchMessage API是前面"系统传来"4个字中的一环,也是最靠近应用层的一环。本文从该API切入,逐渐远离熟悉的应用层。开始前先回顾1个User32.dll导出的API(也是本文的主角):

1. User32.dll!DispatchMessage。 当Notepad.exe接受到消息后,通过该API调用RegisterClassEx注册的窗口过程。

引言2.

【原本本文我想结合Ollydbg的消息断点做佐证,奈何消息断点实在有各种限制,因此我在DispatchMessage上下条件断点,更精准的达到相同目的。】

用过Ollydbg的读者一定对Ollydbg中的条件断点并不陌生,我先演示在CrackMe中下条件断点的标准流程。

2.1.查找RegisterClassEx并下断

2.2.Notepad被断点中断后,分析调用RegisterClassExW时传入的窗口类:

上图堆栈区ESP指向函数返回地址(指向notepad.exe) ;ESP+4存放传入RegisterClassExW的WNDCLASSEX结构的地址。WNDCLASS结构定义如下,域变量lpfnWndProc位于结构体中的Offset 8 Byte处:

typedef struct tagWNDCLASSEXW {
    UINT        cbSize; //4 Byte
    /* Win 3.x */
    UINT        style; //4 Byte
    WNDPROC     lpfnWndProc; //窗口过程地址
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCWSTR     lpszMenuName;
    LPCWSTR     lpszClassName;
    /* Win 4.0 */
    HICON       hIconSm;
} WNDCLASSEXW, *PWNDCLASSEXW, NEAR *NPWNDCLASSEXW, FAR *LPWNDCLASSEXW;

在ESP+4处右键"Follow in dump",即可在数据区查看WNDCLASSEX各个域变量的值。图中0x19FB64(WNDCLASSEX + 0x08)处存有Notepad窗口过程(WndProc)地址: 

数据区0x19FB64处右键"Follow DWORD in Disassembler",即可在指令窗口显示Notepad窗口过程的反汇编。(我用LoadMapEx加载了Notepad的map文件,因此Comment区域会显示窗口过程的符号名)

 Notepad的窗口过程位于0x0401B90(很明显该过程位于Notepad.exe 代码段内),先记录这个地址并在窗口过程入口处下断点

 2.3.给Notepad.exe窗口下条件断点.

原本想给Notepad.exe下消息断点,奈何消息断点无效(百度搜索"消息断点失效",提问者不少解答者寥寥),于是变通为给DispatchMessageA/W下条件断点。DispatchMessage的唯一参数为MSG,结构如下:

typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
#ifdef _MAC
    DWORD       lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

先给DispatchMessage下个普通断点,断下后结合堆栈来拼凑成条件断点:

上图CommandBar中用于给 DispatchMessageW下断点;

堆栈区:ESP ==>指向返回地址;ESP+4指向参数MSG的地址。

在ESP+4处右键"Follow in Dump"将在数据区解析MSG各个成员:
MSG+0x00 (0x19FCD0):0x00307E4,为窗口句柄
MSG+0x04 (0x19FCD4):0x60,为消息值

这是最终条件断点的表达式: 

2.4.F9运行Ollydbg

当Notepad收到按键抬起消息时,ollydbg会中断在条件断点处。一般为了追踪处理消息的窗口过程中,Cracker此时会在Ollydbg中下内存访问断点,对于本文就是对Notepad的代码段下内存访问断点。

点击Ollydbg工具栏的"M",显示模块窗口:

 再次运行ollydbg,程序马上会暂定在Notepad的代码段中。当然也包含窗口过程所在的地址。

等等别走,还没完。此处,我提出2个调试过程中遇到的值得深思的问题:

1.如果Notepad.exe的代码量极大,窗口过程恰好位于其他dll中,那么通过下内存访问断点来定位窗口过程的方式是不是失效了?

2.有别于练手的CrackMe程序,对于多线程程序,就如Notepad.exe,设置内存访问断点后,其他线程也会访问代码段,如何从中挑选出窗口过程?这无异于引入了大量的噪声,增加的分析的难度。(简单如Notepad.exe也有15个线程)

 如何解决上述2个问题?让我们深入DispatchMessage函数。

1.USER32._InternalCallWinProc

借助前面给窗口过程下的断点,继续运行Ollydbg,Ollydbg中断。点击Ollydbg工具栏"K",查看函数调用堆栈:

第一第四栈帧有点眼熟,它显示了窗口程序在User32模块中从DispatchMessage API进入窗口过程的全过程。借助IDA为User32.dll生成map文件/Ollydbg LoadMap插件加载新生成的Map文件,可以获得相对友好的调用堆栈:

1.1. IDA生成User32.Map

IDA加载User32.dll,点击File--Produce file--Create MAP file,生成User32.map:

 

 1.2. Ollydbg加载Map

Ollydbg--Plugins--LoadMapEx--LoadMapEx加载IDA生成的MAP:

(注:一定要在User32.dll模块的地址空间中加载User32.map,否则会干扰ollydbg的分析功能.一旦干扰ollydbg的分析功能,只能通过移除分析结果来恢复)

 再次打开调用堆栈,得到较为友好的调用堆栈:

虽然图中有部分函数地址没有解析出来,但是通过单步跟踪可以得到如下调用链:

DispatchMessageW
|-->DispatchMessageWorker
    |-->UserCallWinProcCheckWow
        |-->InternalCallWinProc

同时借助泄露的win xp源码一探究竟:

UserCallWinProcCheckWow的实现位于NT\windows\core\ntuser\client\clmsg.c

LRESULT
UserCallWinProcCheckWow(
    PACTIVATION_CONTEXT pActCtx,
    WNDPROC pfn,
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam,
    PVOID pww,
    BOOL fEnableLiteHooks)
{
    BOOL fInsideHook;
    LRESULT lRet = 0;

    BEGIN_CALLWINPROC(fInsideHook, lRet)

        BOOL fOverride = fInsideHook && fEnableLiteHooks && IsMsgOverride(msg, &guah.uoiWnd.mm);

        pfn = MapKernelClientFnToClientFn(pfn);

        if (fOverride) {
            /*
             * NOTE: It is important that the same lRet is passed to all three
             * calls, allowing the Before and After OWP's to examine the value.
             */
            void * pvCookie = NULL;
            if (guah.uoiWnd.pfnBeforeOWP(hwnd, msg, wParam, lParam, &lRet, &pvCookie)) {
                goto DoneCalls;
            }

            lRet = (IsWOWProc(pfn) ? (*pfnWowWndProcEx)(hwnd, msg, wParam, lParam, PtrToUlong(pfn), KPVOID_TO_PVOID(pww)) :
                InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn), hwnd, msg, wParam, lParam));

            if (guah.uoiWnd.pfnAfterOWP(hwnd, msg, wParam, lParam, &lRet, &pvCookie)) {
                // Fall through and exit normally
            }
DoneCalls:
            ;
        } else {
            lRet = (IsWOWProc(pfn) ? (*pfnWowWndProcEx)(hwnd, msg, wParam, lParam, PtrToUlong(pfn), KPVOID_TO_PVOID(pww)) :
                InternalCallWinProc((WNDPROC)KPVOID_TO_PVOID(pfn), hwnd, msg, wParam, lParam));
        }
    END_CALLWINPROC(fInsideHook)

    return lRet;
#ifdef _WIN64
    UNREFERENCED_PARAMETER(pww);
#endif // _WIN64
}

InternalCallWinProc是宏,定义于NT\windows\core\ntuser\client\callproc.h 

#define InternalCallWinProc(winproc, hwnd, message, wParam, lParam)    \
    (winproc)(hwnd, message, wParam, lParam)

其中winproc是函数指针,随之猜想,winproc可能会有机会指向Notepad.exe代码段中的某个地址。

2.精准型消息断点

先给前面猜想的结论,当UserCallWinProcCheckWow函数调用InternalCallWinProc时,winproc有机会(winproc还会指向ntdll中的回调函数)会指向Notepad的窗口过程:

 如上图,调用InternalCallWinProc,EBX指向notepad.NPWndProc。既然如此,我可以在调用InternalCallWinProc时下条件断点(我就称它为精准型消息断点)

图中ebx的取值需要根据代码段的起始/结束位置做调整。

当ollydbg中断时,结合InternalCallWinProc的函数原型可知:

EBX指向窗口过程

ESI指向接受消息的窗口 ;

EDI指向消息类型

当然,我们还能进一步编辑条件断点的条件,过滤出特定的窗口消息WM_KEYUP消息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/570141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kerberos认证原理及相关漏洞

Kerberos认证协议 Kerberos认证协议也称三头犬协议,因为在Kerberos认证过程中,需要有三个角色:Client、Server以及KDC(Key Distribution Center)密钥分发中心。 Kerberos认证协议的目的是为客户端/服务端提供身份验证。最主要的问题是如何证明…

Win11硬盘分区

电脑重装了Win11系统,按WinE打开主文件夹,再点击此电脑,发现: 磁盘只有一个C盘。硬盘的所有空间都在该盘上了,那么我们怎么将其分区呢? Win11硬盘分区步骤: 步骤1: 按WinR输入dis…

数据库中的事务,隔离级别,以及数据展示

想要知道和学习数据库中的锁,要先学习数据库的事务和并发事务所带来的问题! 1.数据库中的事务! 1.1什么事务 事务是由一组SQL语句组成的逻辑处理单元(多个sql进行修改,新增等),这些操作要么同时…

跟踪任何目标(想跟踪什么就跟踪什么)

结果展示 介绍 该项目是一个简单的跟踪工具,可以用于跟踪任何你感兴趣的东西。它提供了一个基于Web的界面,让用户可以轻松地创建和管理跟踪列表,同时也提供了一个RESTful API,可以方便地进行数据交互。 项目的原理是将用户需要跟踪的内容,通过创建跟踪项的方式存储到数据…

Hausdorff 距离

1. 定义 给定欧氏空间中的两点集 A { a 1 , a 2 , . . . } \rm A\left \{a_1, a_2,... \right\} A{a1​,a2​,...} 和 B { b 1 , b 2 , . . . } \rm B\left \{b_1, b_2,... \right\} B{b1​,b2​,...} , H a u s d o r f f {\rm Hausdorff} Hausdorff 距离就是用…

基于SSM的在线考试系统开发与设计-(附源码文档)-毕业设计

文章目录 1.适用人群2.你将收获3.项目介绍4.系统需求分析4.1 需求特性分析4.2 功能需求分析 5.系统设计5.1 系统总体结构设计5.2 数据库设计5.2.1 数据库概念原则设计5.2.2 数据库各部分模块设计5.2.3 数据库表设计 6.系统详细设计6.1 系统各模块功能设计6.1.1 登录模块6.1.2 注…

chatgpt赋能python:如何利用Python加快计算速度

如何利用Python加快计算速度 在大数据时代,计算效率的问题成为了企业和科研机构普遍关注的焦点问题。Python是一种高级编程语言,其具有灵活、易学、语法简洁、运行速度快等优点,因此在数据分析和科学计算领域广泛应用。然而,Pyth…

chatgpt赋能python:Python内部函数介绍

Python内部函数介绍 Python是一门功能强大、易于学习的编程语言,拥有许多内部函数可供使用。本文将介绍Python的内部函数和其用途,以便更好地利用和理解Python。 什么是内部函数? 内部函数是Python提供的一组内置函数,它们可以…

2023/5/25总结

学习CSS list-style:none 去掉无序列表的带有的样式,比如原点。 border-radius:length 设置圆角,也可以写%,不一定需要些半径大小,也可以顺时针写半径大小,就会出现四个顶点不一样的圆角。或者写:borde…

chatgpt赋能python:Python写Log的技巧与最佳实践

Python 写 Log 的技巧与最佳实践 在编写 Python 应用程序时,日志记录(Logging)是一项非常重要的功能,尤其是在调试或部署过程中。本文将介绍一些 Python 写 Log 的技巧和最佳实践,以帮助你更好地处理日志记录并提高应…

Java的String(字符串详解)

字符串 1.字符串的常见构造方法 主要有三种,一种是直接使用常量去构造,要么使用new String来构造,或者还可以使用字符数组的形式。 public static void main(String[] args) { // 使用常量串构造 String s1 "hello"; System.ou…

order by排序语句的用法

文章目录 学习连接语法用法示例1、按单个列的值排序2、按多个列的值排序3、按指定的规则排序4、按中文拼音字母顺序5、Order by和where条件共用 数据库中常用order by关键字对结果集进行排序,又可使用desc和asc来进行指定规则的排序。 学习连接 数据库:…

chatgpt赋能python:单行for循环:Python编程的神器

单行for循环:Python编程的神器 Python是一门功能强大的编程语言,其中的单行for循环功能更是让它的编程效率倍增。在本文中,我们将探讨单行for循环是如何提升编程效率的。 什么是单行for循环 单行for循环是一种简单而强大的编程方式。它可以…

【利用AI让知识体系化】V8引擎相关知识

文章目录 I. 引言V8引擎的背景和概述 II. V8的设计和工作原理V8的整体设计V8的工作流程和运行机制V8在浏览器中的应用场景 III. 内存管理内存模型和内存管理策略垃圾回收机制和算法内存泄漏和内存优化 IV. JIT编译器JIT编译器的作用和优势V8的编译流程和编译器类型编译器优化技…

make的路径搜索

文章目录 前言一、VPATH二、vpath三、vpath 与 VPATH 的差别四、GPATH 用法总结 前言 在大型软件项目中,通常会存在多个目录,包含有源代码、头文件、库文件等不同类型的文件。在编译或链接时,需要指定相应的文件路径才能正确地进行构建。但是…

【计算机图形学】曲线和曲面(Bezier曲线 Bezier曲面)

模块5 曲线和曲面 一 实验目的 编写曲线和曲面的算法 二 实验内容 1:绘制Bezier曲线,并采用自行设计输入和交互修改数据点的方式。 实验结果如下图所示: 第一步:输入特征多边形的顶点个数,并按照顺序输入顶点的坐…

css3新增特性

1. 初始化 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, …

怎么通过ecs云服务器来给小程序发送消息

如果您想通过 ECS 云服务器向小程序发送消息&#xff0c;可以使用 WebSocket 技术。具体步骤如下&#xff1a; 1. 在 ECS 云服务器上搭建 WebSocket 服务器。您可以使用 Node.js、Java、Python 等编程语言来实现 WebSocket 服务器&#xff0c;具体实现方式可参考相关技术文档或…

Java笔记——KMP算法

KMP算法 文章目录 KMP算法KMP算法介绍主要逻辑Next数组KMP搜索代码解释生成next数组模式串匹配 源码展示 KMP算法介绍 KMP算法是一种串的模式匹配算法&#xff0c;用来求子串在主串的位置。是数据结构中比较难的一种算法。KMP算法的核心在于点在于如何利用子串生成next数组&am…

vim的使用、vim入门的三种常用模式、以及vim中常用的命令(超详细)

vim 入门的三种常用模式&#xff1a;分别是 1. 命令模式、2. 插入/编辑模式、3. 底行模式 1. 命令模式 控制屏幕光标的移动&#xff0c;字符、字或行的删除&#xff0c;移动复制某区段及进入Insert mode下&#xff0c;或者到 last line mode 如下&#xff0c;这个就是命令模式…