解析windows消息处理机制

解析windows消息处理机制

众所周知Windows系统通过消息机制来管理交互,Windows的应用程序一般包含窗口 (Window),它主要为用户提供一种可视化的交互方式,窗口是由线程(Thread)创建的。消息 (Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue),以保持线程间的独占性,那具体又是如何处理的呢?

消息由一个叫MSG的结构体定义,包括窗口句柄(HWND),消息ID(UINT),参数(WPARAM, LPARAM)等等:

struct MSG

{

    HWND hwnd;

    UINT message;

    WPARAM wParam;

    LPARAM lParam;

    DWORD time;

     POINT pt;

};

每个线程维护了一个THREADINFO的结构体,里面保存了5个消息队列。

typedef struct _tagTHREADINFO           // 156 elements, 0x208 bytes (sizeof)

{

/*0x0BC*/     struct _tagQ* pq;// input queue

/*0x0E0*/     struct _tagSMS* psmsSent;// send queue (sent)

/*0x0E4*/     struct _tagSMS* psmsCurrent;// send queue (current)

/*0x0E8*/     struct _tagSMS* psmsReceiveList;// send queue (received)

/*0x174*/     struct _tagMLIST mlPost;// post queue

} tagTHREADINFO, *PtagTHREADINFO;

消息得传递分为两种:1,异步方式,比如:PostMessage();直接把消息插入到mlPost消息队列中就返回了!2,同步的方式,比如:sendmessage,它的处理就比较麻烦了。今天我们主要看一下这个函数的执行流程。

Sendmessage由USER32.dll导出,经过简单处理之后通过SSDT shadow 进入内核的NtUserMessageCall  函数。

NTSTATUS NtUserMessageCall (

HWND hWnd,// target window

UINT Msg,// message type

WPARAM wParam,// param1

LPARAM lParam,// param2

ULONG_PTR ResultInfo,// param3

DWORD dwType,// window procedure

BOOL bAnsi )// ansi/unicode

NtUserMessageCall  首先判断hWnd是不是有效的窗口对象,然后判断Msg是否是系统定义的消息,如果是,就通过MessageTable得到索引。

kd> db win32k!MessageTable

822a42c8  00 c2 00 00 00 00 00 00-00 00 00 00 c3 c4 ec 00  ................

822a42d8  00 00 00 00 80 00 00 00-00 00 c3 c5 00 00 00 00  ................

822a42e8  00 00 00 00 86 00 00 80-00 00 00 87 88 89 00 4a  ...............J

822a42f8  00 80 00 00 00 00 00 00-8b 8c 29 00 a9 00 00 00  ..........).....

822a4308  00 00 00 00 00 00 8d 8e-00 cf 90 00 00 00 00 00  ................

822a4318  00 00 00 91 00 00 00 00-00 00 00 00 00 00 00 00  ................

822a4328  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

822a4338  a9 00 00 00 00 00 00 00-00 00 00 00 92 92 00 00  ................

再然后通过得到的索引调用代理函数,代理函数保存在一个名叫_gapfnMessageCall的数组中

.rdata:BF9F31C8 _gapfnMessageCall dd offset _NtUserfnNCDESTROY@28

.rdata:BF9F31CC                 dd offset _NtUserfnNCDESTROY@28

.rdata:BF9F31D0                 dd offset _NtUserfnINLPCREATESTRUCT@28

.rdata:BF9F31D4                 dd offset _NtUserfnINSTRINGNULL@28

.rdata:BF9F31D8                 dd offset _NtUserfnOUTSTRING@28

.rdata:BF9F31DC                 dd offset _NtUserfnINSTRING@28

.rdata:BF9F31E0                 dd offset _NtUserfnINOUTLPPOINT5@28

在代理函数中依次调用

                      xxxSendMessage

                            |

|

|

                               \  /

                        xxxSendMessageTimeout 

                            |

|

|

                               \  /

                           xxxInterSendMsgEx

在xxxInterSendMsgEx中创建发送消息结构体send message structure (win32k!tagSMS);利用AllocSMS()创建结构体之后通过MSGSQMAddMessage(x,x,x,x,x,x,x)添加到队列中。然后通知服务线程!

typedef struct _tagSMS                      // 15 elements, 0x3C bytes (sizeof)

{

/*0x000*/     struct _tagSMS* psmsNext;

/*0x004*/     struct _tagSMS* psmsReceiveNext;

/*0x008*/     struct _tagTHREADINFO* ptiSender;

/*0x00C*/     struct _tagTHREADINFO* ptiReceiver;

/*0x010*/     FUNCT_00A4_1106_lpResultCallBack* lpResultCallBack;

/*0x014*/     ULONG32      dwData;

/*0x018*/     struct _tagTHREADINFO* ptiCallBackSender;

/*0x01C*/     LONG32       lRet;

/*0x020*/     ULONG32      tSent;

/*0x024*/     UINT32       flags;

/*0x028*/     UINT32       wParam;

/*0x02C*/     LONG32       lParam;

/*0x030*/     UINT32       message;

/*0x034*/     struct _tagWND* spwnd;

/*0x038*/     VOID*        pvCapture;

} tagSMS, *PtagSMS;

客服端的分析就到这里结束了....我们想想现在把消息添加进去了,那系统又是如何取得并如何分发的呢?那就得通过getmessage这个入口点了。

Getmessage-->NtUserGetMessage----->xxxRealInternalGetMessage------>xxxReceiveMessage。

-------------------不写了-----------------------没心情了------------

好吧```由于昨天不在状态咯,,今天继续更新

主要来仔细分析一下xxxReceiveMessage函数的执行流程,和Sendmessge一样,此函数通过MessageTable得到一个索引值,然后根据这个值在_gapfnScSendMessage数组中找到一个代理函数

.rdata:BF9F31C8 _gapfnMessageCall dd offset _NtUserfnNCDESTROY@28

.rdata:BF9F31CC                 dd offset _NtUserfnNCDESTROY@28

.rdata:BF9F31D0                 dd offset _NtUserfnINLPCREATESTRUCT@28

.rdata:BF9F31D4                 dd offset _NtUserfnINSTRINGNULL@28

.rdata:BF9F31D8                 dd offset _NtUserfnOUTSTRING@28

.rdata:BF9F31DC                 dd offset _NtUserfnINSTRING@28

.rdata:BF9F31E0                 dd offset _NtUserfnINOUTLPPOINT5@28

在代理函数中通过KeUserModeCallback的力量回到RING3之上,进行一系列的数据操作,再然后回到内核中一路返回到xxxReceiveMessage,在该函数中填充MSG结构体并返回给调用者。(注:KeUserModeCallback的执行流程请看黑月教主的《KeUserModeCallback用法详解》地址:http://bbs.pediy.com/showthread.php?t=104918 ),然后告知发送者已经完成。

在同步的处理消息的时候,没有确认证实地址的可用性下就使用,将会带来DOs漏洞的攻击,比如MS11-012 ,MS10-073)以及最近爆出来的漏洞MS11-077另附MS11-077的一个BSOD代码

/*

* 【作者:KiDebug】

* 【E-Mail:Google¥pku.edu.cn】

*/

#include <Windows.h>

void main()

{

  SendNotifyMessage((HWND)-1,0x180,0,0);

蓝屏时的调用栈:

1 0: kd> kp

2 ChildEBP RetAddr  

3 ee0a3d08 bf80eed3 win32k!NtUserfnINLBOXSTRING+0x8

4 ee0a3d40 8054261c win32k!NtUserMessageCall+0xae

5 ee0a3d40 7c92e4f4 nt!KiFastCallEntry+0xfc

6 0012ff3c 77d194be ntdll!KiFastSystemCallRet

7 0012ff64 00401011 USER32!NtUserMessageCall+0xc

8 0012ff78 00401148 1!main(void)+0x11 [r:\temp\1\1.cpp @ 6]

9 0012ffc0 7c817067 1!__tmainCRTStartup(void)+0x10b [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 278]

10 0012fff0 00000000 kernel32!BaseProcessStart+0x23

由此可以看出是在处理NtUserfnINLBOXSTRING代理函数时出现的问题`````

我相信再WIN32K中还有很多这样的的漏洞~~~

--------------------------昏割线-----------------------------------------------------

Sendmessge和getmessge执行流程图:


本实例由VS2008开发,在提供了一套驱动开发框架的同时,又演示了如何获取Shadow SSDT表函数原始地址的办法。 主要函数:ULONG GetShadowSSDT_Function_OriAddr(ULONG index); 原理说明: 根据特征码搜索导出函数KeAddSystemServiceTable来获取Shadow SSDT基址,以及通过ZwQuerySystemInformation()函数获取win32k.sys基址,然后解析PE定位到Shadow SSDT在win32k.sys的偏移地址,并通过进一步计算来得到Shadow SSDT表函数的原始地址。 这里只测试了三个函数:(460)NtUserMessageCall、(475)NtUserPostMessage和(502)NtUserSendInput,具体使用时可以举一反三,网上完整的源代码实例并不太多,希望可以帮到真正有需要的朋友。 系统环境: 在WinXP SP3系统 + 瑞星杀毒软件 打印输出: [ LemonInfo : Loading Shadow SSDT Original Address Driver... ] [ LemonInfo : 创建“设备”值为:0 ] [ LemonInfo : 创建“设备”成功... ] [ LemonInfo : 创建“符号链接”状态值为:0 ] [ LemonInfo : 创建“符号链接”成功... ] [ LemonInfo : 驱动加载成功... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP 开始... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP Enter IRP_MJ_DEVICE_CONTROL... ] [ LemonInfo : 获取ShadowSSDT表 (460)NtUserMessageCall 函数的“当前地址”为:0xB83ECFC4,“起源地址”为:0xBF80EE6B ] [ LemonInfo : 获取ShadowSSDT表 (475)NtUserPostMessage 函数的“当前地址”为:0xB83ECFA3,“起源地址”为:0xBF8089B4 ] [ LemonInfo : 获取ShadowSSDT表 (502)NtUserSendInput 函数的“当前地址”为:0xBF8C31E7,“起源地址”为:0xBF8C31E7 ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP_MJ_DEVICE_CONTROL 成功执行... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP 结束... ] [ LemonInfo : UnLoading Shadow SSDT Original Address Driver... ] [ LemonInfo : 删除“符号链接”成功... ] [ LemonInfo : 删除“设备”成功... ] [ LemonInfo : 驱动卸载成功... ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值