avatar

Catalog
消息的分发

要点回顾

前篇学习了关于消息的接收,主要是围绕GetMessage函数展开的,了解到GetMessage函数不仅仅读取消息,还会处理SentMessagesListHead队列中的消息。并且消息队列不止一个,共有七个。本篇学习了解消息的分发,主要是围绕DispatchMessage展开。

核心逻辑

关于处理窗口消息的核心逻辑如下,这里再回顾下:

c
1
2
3
4
5
6
MSG msg;  						
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

其它队列的消息处理

消息队列共有7个队列,现在已经知道GetMessage可以用来处理SentMessagesListHead中的消息,也就是SendMessage函数发来的消息,那么仍然有6个队列的消息未被处理。

DispatchMessage

(实验:注释掉DispatchMessage函数后运行窗口程序并给窗口发送消息,这里省略代码及实验过程,以后补上)

在注释掉DispatchMessage函数的情况下,对于窗口的任何操作(鼠标点击或者敲击键盘)始终是没有反应的,显然没有了DispatchMessage函数,除SendMessage发送的消息外均无法被处理。

现在可以确认DispatchMessage是用来处理其余队列中消息的函数,但想要了解本质,就得进一步跟进这个函数。

(这里省略跟进过程,比较简单,所以不想跟了)

不用想,这个DispatchMessage肯定是没有做处理的,因为这个函数根本没有进入0环,而窗口对应的结构体_WINDOW_OBJECT位于0环,不进入0环,怎么调用窗口过程函数的呢?所以DispatchMessage只是一个入口,用来调用win32k.sys中提供的函数NtUserDispatchMessage。这个函数完成了对消息的分发,它做了下面2件事:

  • 根据窗口句柄找到窗口对象_WINDOW_OBJECT。
  • 根据窗口对象得到窗口过程函数,由0环发起调用。

这样也产生了两个问题,第一,窗口句柄哪里来?第二,如何从0环发起调用?关于从0环发起调用,与GetMessage一样,都是调用一个叫做KeUserModeCallback的回调函数进入3环,再去调用窗口过程函数。至于窗口句柄,DispatchMessage只有一个参数msg,所以就要把目光放在msg上了。

Msg结构体

DispatchMessage只有一个参数msg,其结构如下:

c
1
2
3
4
5
6
7
8
9
//msg
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;

可以看到这个msg里面,第一个成员就是窗口句柄,表明当前消息是发给哪个窗口的,前面说过,窗口句柄与内核对象句柄类似,都是提供给3环用的,DispatchMessage在调用了NtUserDispatchMessage后,就可以通过窗口句柄在窗口句柄表中找到对应_WINDOW_OBJECT结构体的地址,从而调用窗口回调函数,完成消息的分发。

消息的转换

DispatchMessage是用来消息的分发的,那么TranslateMessage有啥用呢?顾名思义,TranslateMessage起到翻译的作用,相当于一种优化。

(实验:分别构造WM_KEYDOWN与WM_CHAR两个回调函数,测试注释掉TranslateMessage函数的情况,此处省略,以后补上)

经过测试就可以发现,TranslateMessage是针对键盘类消息的一种优化,如果没有TranslateMessage,那么键盘消息属于WM_KEYDOWN类型,打印出来为ASCII对应的10进制的值,而使用TranslateMessage后,键盘消息会被转换成WM_CHAR类型,打印出来的就是键盘上按下的符号。所以有没有TranslateMessage影响不大,只是对消息的类型进行转换。

默认的消息处理函数

窗口与线程一篇中就看到过,每时每刻的消息是非常多的,而又不能给每一种消息写一个窗口过程函数,那样太程序就臃肿了,所以一般来说,我们只对关注的消息设计窗口过程函数,其它消息可以交予Windows来管理。Windows也提供了默认的窗口过程函数,放在Default语句中即可:

c
1
2
//默认窗口过程处理函数
return DefWindowProc(hWnd, uMsg, wParam, lParam);

总结

  1. DispatchMessage用于消息的分发,具体实现由底层的NtUserDispatchMessage完成,参数msg提供窗口句柄。
  2. TranslateMessage用于对键盘消息的转换。
  3. 大部分消息可以交由默认窗口过程处理函数DefWindowProc进行处理。

参考资料

参考教程:

  • 海哥逆向中级预习班

参考链接:

Author: cataLoc
Link: http://cataloc.gitee.io/blog/2020/09/09/%E6%B6%88%E6%81%AF%E7%9A%84%E5%88%86%E5%8F%91/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶