要点回顾
前两篇关于消息机制的学习主要介绍了以下2点:
一个GUI线程有一个消息队列
Code1
普通线程 -> GUI线程 -> THREAD.Win32Thread -> THREADINFO -> 消息队列
一个线程可以有多个窗口,所有窗口共享一个消息队列
Code1
2_WINDOW_OBJECT -> PTHREADINFO pti //所属线程
-> WNDPROC lpfnWndProc //窗口过程(窗口回调函数)
窗口的创建过程
在3环创建窗口时,需要先创建并注册一个窗口类的对象,并注册窗口的样式,过程函数等。然后调用CreateWindow创建一个窗口。
而本质上,CreateWindow只不过是一个3环的接口,最终调用的是位于win32k.sys中的0环函数,并在0环给窗口创建一个_WINDOW_OBJECT结构体,每个窗口在0环都有这样一个结构体。
综上可以得出如下的结论:创建类对象与创建窗口的过程,本质上就是为创建一个_WINDOW_OBJECT结构作准备。
![](/2020/09/08/%E6%B6%88%E6%81%AF%E7%9A%84%E6%8E%A5%E6%94%B6/CreateWindow.png)
消息队列的结构
在消息队列一篇中提到过,一旦线程调用win32k.sys提供的图形界面函数后,线程结构体中的成员Win32Thread就会指向一个结构体THREADINFO,该结构体中有一个成员MessageQueue就是消息队列,消息队列中包含7组队列(旧版ReactOS才有),用于处理不同类型的消息。
![](/2020/09/08/%E6%B6%88%E6%81%AF%E7%9A%84%E6%8E%A5%E6%94%B6/MessageList_1.png)
其中3个是比较常见的消息队列:
- SentMessagesListHead:接到SendMessage发来的消息。
- PostedMessagesListHead:接到PostMessage发来的消息。
- HardwareMessagesListHead:接到鼠标、键盘的消息。
GetMessage的功能
在编写3环的窗口程序时,总会写如下一段代码:
1 | MSG msg; |
其中GetMessage函数负责从7个队列中取消息,并让TranslateMessage与DispatchMessage翻译并分发消息。但GetMessage函数真的只负责取消息吗?
本篇研究消息的接收,我们知道消息会先进入7个队列中,而TranslateMessage与DispatchMessage又是处理消息的,所以研究的重点就在GetMessage上。先来看函数原型:
1 | GetMessage( |
GetMessage有4个参数,其中3个都是过滤条件,包括指定接收消息的窗口;另一个就从队列中取得的消息。所以表面上,GetMessage通过循环判断是否有该窗口的消息,如果有,将消息存储到MSG结构中,并将消息从原先的消息队列中(7个队列中的某个)删除。接下来,将消息交给TranslateMessage与DispatchMessage去处理。
DispatchMessage通常就是将消息转发至窗口过程函数,从而使得过程函数被调用。但实际上,GetMessage也会消息进行处理。
(实验:测试只保留GetMessage的情况下是否能够执行窗口回调函数。此处省略,以后补上……)
为什么说GetMessage也会对消息进行处理呢?这里没有实验,就直接说明一下好了,GetMessage调用的是win32k.sys中的NtUserGetMessage函数,在NtUserGetMessage函数内部有如下逻辑(ReactOS版本不对没找到,IDA中跟了一下也没找到,所以就直接按着海哥分析的结果来):
1 | do |
在一个内部的do…while循环中,NtUserGetMesssage会先判断SentMessagesListHead中是否有消息,如果有的话,就调用窗口回调函数处理掉。接着直到处理完SentMessagesListHead中的所有消息后,才会判断其它6个队列中的消息,此时就不会对这些消息作处理了,而是直接将消息返回。所以,GetMessage也是会对消息进行处理的,但是只对SentMessagesListHead中的消息作处理。
SendMessage与PostMessage
这两个函数都是像窗口发送消息的函数,区别是SendMessage是同步的,而PostMessage是异步的。
(实验:分别发送SendMessage与PostMessage到一个注释掉DispatchMessage函数的窗口。此处省略,以后补上……)
这里实验省略,直接说结论:
- SendMessage发送消息,GetMessage接收时会进入0环遍历SentMessagesListHead是否有消息,有就处理,没有就返回。有消息就必须处理完才返回,SendMessage要接收到对方执行完并返回处理结果才会结束,否则会一直堵塞在这。
- PostMessage发送消息,GetMessage只会接收它的消息,不会处理,它的消息由TranslateMessage与DispatchMessage来处理。PostMessage不会等待对方返回处理结果,发完就立马结束。
总结
- GetMessage除了接收消息外,还会处理SentMessagesListHead队列中的消息。
- SendMessage与消息处理是同步的,会等待处理结果。PostMessage与消息处理是异步的,发完就结束执行。
参考资料
参考教程:
- 海哥逆向中级预习班
参考链接:
- https://blog.csdn.net/weixin_42052102/article/details/83787929 (My classmates-消息机制)