Thread Desynchronization Issues in Windows Message Handling

This week, Microsoft issued MS11-012 to resolve yet another batch of vulnerabilities in win32k.sys. The bulletin addressed three elevation of privilege vulnerabilities in window class data handling (somewhat related to those patched in MS10-073) and an additional two in window message handling. The latter were quite interesting as they were not your typical vulnerability class, but rather subtle issues caused by the desynchronization of threads engaged in synchronous messaging (using SendMessage APIs). In this post, we’ll review some of the internals concerning window messages and detail the issues at hand.

Past Vulnerabilities

Besides additions such as User Interface Privilege Isolation (UIPI) in Vista [1] and touch and gesture support in Windows 7, the core messaging components have undergone little change over the years. In spite of this, vulnerabilities continue to be discovered. In MS08-025, several data validation vulnerabilities related to user-mode callbacks in the system message handlers were addressed [2]. MS10-098 also fixed a validation issue introduced in Windows 7 upon processing WM_GETTEXT messages that could be leveraged to corrupt the memory of a privileged process. Moreover, additional denial of service vulnerabilities have been reported in system message handlers such as SfnINSTRING and SfnLOGONNOTIFY.

Windows Messages

Windows-based applications are event driven and act upon messages sent to them. Thus, messages and the mechanisms that support them have always played an integral role in the user interface component of the Windows operating system. Each window, owned by a thread, has a window procedure (function) for processing input messages and dispatching them to the operating system. If a thread accesses any of the user interface or GDI system calls (handled by win32k.sys), the kernel creates a THREADINFO structure which holds three message queues used to process input. These are the input queue, the post queue, and the send queue. The input queue is primarily used for mouse and keyboard messages, while the send and post queues are used for synchronous (send) and asynchronous (post) window messages respectively.

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


Asynchronous Messages

Asynchronous messages are used in one-way communication between window threads and are typically used to notify a window to perform a specific task. Asynchronous messages are handled by the PostMessage APIs and are sent to the post queue of the receiving thread. The sender does not wait for the processing to complete in the receiving thread and thus returns immediately. For this reason, asynchronous messages cannot be used with pointers and handles as there is no guarantee that the sender will exist by the time the receiver processes the data.

Synchronous Messages

Synchronous messages differ from asynchronous messages as the sender typically waits for a response to be provided or a timeout to occur before continuing execution. Thus, they require mechanisms to ensure that the threads are properly synchronized and in the expected state. Synchronous messages use the SendMessage APIs which in turn direct execution to the NtUserMessageCall system call in win32k.sys.

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

The message type (Msg) is identified by a unique WM code such as WM_SETFOCUS, WM_CREATE, WM_ENABLE, etc. Applications may define their own message codes, but those less than WM_USER (0x400) are reserved by the operating system. Each reserved code denotes an index into win32k!MessageTable (byte array) in which the lower 6 bits of each byte entry defines the identifier (array index) of the associated system message handlers (discussed in the next section).

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  ................

Inter-thread messages are stored in a send message structure (win32k!tagSMS) and appended to the send queue of the receiving thread. The buffer for this structure is allocated in win32k!AllocSMS upon initiating inter-thread messaging in win32k!xxxInterSendMsgEx. The SMS structure holds all the message parameters and information about the sending and receiving threads. The structure is defined as follows (from the Windows 7 public symbols).

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;

Client and Server Functions

The function id stored in the message table denotes an index into a client (gapfnMessageCall) or server (gapfnScSendMessage) function table. The client functions are used by threads sending a message while the server functions are used by threads processing a message. The client functions (prefixed by NtUserfn) are mostly used for probing and caching (copying to kernel memory) of user-mode data. Server functions (prefixed by Sfn) parse and process the content of the message and, if necessary, return data to the sending thread. The following tables show the first few functions of the client and server function tables as found in the data section of win32k.sys.

.rdata:BF9F30C8 _gapfnScSendMessage dd offset _SfnDWORD@32
.rdata:BF9F30CC                 dd offset _SfnNCDESTROY@32
.rdata:BF9F30D0                 dd offset _SfnINLPCREATESTRUCT@32
.rdata:BF9F30D4                 dd offset _SfnINSTRINGNULL@32
.rdata:BF9F30D8                 dd offset _SfnOUTSTRING@32
.rdata:BF9F30DC                 dd offset _SfnINSTRING@32
.rdata:BF9F30E0                 dd offset _SfnINOUTLPPOINT5@32
.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

In sending a WM_CREATE (0x1), the sending thread looks up the associated function id in win32k!MessageTable and calls gapfnMessageCall[0x2] = NtUserfnINLPCREATESTRUCT. This function probes and caches the user-mode values before it is sent off in win32k!xxxInterSendMessage and the receiving thread processes the message in gapfnScSendMessage[0x2] = SfnINLPCREATESTRUCT.

SendMessage/GetMessage Execution Flow

In order for send messages to function as intended, the kernel must implement mechanisms to ensure that the sending and receiving threads are synchronized. This is important as message parameters/variables can be stored on the kernel stack of the sending thread, and updates made to this thread stack must be done in a controlled manner. To ensure that either thread has not unexpectedly terminated, the send message kernel structure employs a flags field to keep track of the state of both threads. For instance, if a thread terminates, win32k!xxxDestroyThreadInfo calls win32k!SendMsgCleanup to update the flags field of the SMS structure accordingly. This field must be checked explicitly before updating any values in the stack of the opposite thread.

Thread Desynchronization Vulnerabilities

Several server functions (win32k!Sfn*) did not properly validate the state of the sender thread (client) before returning data to the sender’s thread stack. Consequently, the receiver thread could write to memory that had been freed or even worse, write to a desynchronized kernel thread stack. The latter could result in arbitrary kernel code execution, for instance in overwriting the return pointer of a stack frame. In win32k!SfnINLPDRAWITEMSTRUCT, we see that the device context value on the client thread stack (ESI) is updated without performing the necessary state checks.

.text:BF93A9FC    mov     edi, [ebp+hdcOriginal]
.text:BF93A9FF    test    edi, edi
.text:BF93AA01    jz      short loc_BF93AA11
.text:BF93AA03    mov     esi, [ebp+pDrawItemStruct]		// client stack
.text:BF93AA06    push    [esi+tagDRAWITEMSTRUCT.hDC]
.text:BF93AA09    call    __ReleaseDC@4   ; _ReleaseDC(x)
.text:BF93AA0E    mov     [esi+tagDRAWITEMSTRUCT.hDC], edi	// restore hDC
.text:BF93AA11    mov     eax, [ebp+var_2C]
.text:BF93AA14    jmp     short loc_BF93AA4C
.text:BF93AA16    xor     eax, eax
.text:BF93AA18    inc     eax
.text:BF93AA19    retn


  1. Edgar Barbosa – Windows Vista UIPI
  2. mxatone – Analyzing local privilege escalations in win32k

Comments are closed.