众所周知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执行流程图: