Just Let It Flow

November 16, 2009

Jumping the Queues

Filed under: Windows — adeyblue @ 6:39 am

Last time out we had a list of the WM_ messages and that got me thinking. Is it possible to see into the future and see what messages will be delivered? The best offered by Windows is the Spy++ model. WH_GETMESSAGE hooks can monitor messages returned by a call to GetMessage or PeekMessage, and WH_CALLWNDPROC(RET) can monitor calls to the window procedure, but that’s the extent of their powers.

Scanning a message queue is nothing new. Get/PeekMessage may do it when filtering messages, and GetRawInputBuffer certainly does it to collate pending WM_INPUT messages. Beyond that however, there’s no way to get any of this foresight back to usermode for processing. Reading the memory where the queue resides is no problem, the stumbling block is finding its location. The structs that comprise the path from HWND to pending messages change between every major release, between checked and release versions and even between architectures. While this makes doing it reliably a pain, it’s hardly an insurmountable obstacle.

Despite these little incongruities, the method is the same. Firstly, ValidateHWND from win32k is used to translate the hwnd into its more developed sibling, a WND structure. The first member of this, a THRDESKHEAD struct, is common to all per-desktop resources:

UserDefinedType: _THRDESKHEAD
Data           :   this+0x0, Member, Type: void *, h
Data           :   this+0x4, Member, Type: unsigned long, cLockObj
Data           :   this+0x8, Member, Type: struct tagTHREADINFO *, pti
Data           :   this+0xC, Member, Type: struct tagDESKTOP *, rpdesk
Data           :   this+0x10, Member, Type: unsigned char *, pSelf

From our point of view, the most important here is the pti member. This tagTHREADINFO structure (the first part of which is also known as _W32THREAD), is the starting point of all gui , input, and message related work involving win32k [1]. As of Win7 32-bit the struct is 208 bytes long, 20 of which are directly related to actual message information:

UserDefinedType: tagTHREADINFO
...
Data           :   this+0xBC, Member, Type: struct tagQ *, pq
...
Data           :   this+0xE8, Member, Type: struct tagSMS *, psmsReceiveList
...
Data           :   this+0x174, Member, Type: struct tagMLIST, mlPost

‘mlPost’ is a counted linked list of messages posted to the thread, ‘pq’ is a container of input related data including a counted list of mouse and keyboard input messages, and ‘psmsRecieveList’ is the head of a linked list of sent messages.

UserDefinedType: tagMLIST
Data           :   this+0x0, Member, Type: struct tagQMSG *, pqmsgRead
Data           :   this+0x4, Member, Type: struct tagQMSG *, pqmsgWriteLast
Data           :   this+0x8, Member, Type: unsigned long, cMsgs
 
UserDefinedType: tagQMSG
Data           :   this+0x0, Member, Type: struct tagQMSG *, pqmsgNext
Data           :   this+0x4, Member, Type: struct tagQMSG *, pqmsgPrev
Data           :   this+0x8, Member, Type: struct tagMSG, msg
Data           :   this+0x24, Member, Type: long, ExtraInfo
Data           :   this+0x28, Member, Type: struct tagPOINT, ptMouseReal
Data           :   this(bf)+0x30:0x0 len(0x1E), Member, Type: unsigned long, dwQEvent
Data           :   this(bf)+0x30:0x1E len(0x2), Member, Type: unsigned long, Padding
Data           :   this(bf)+0x34:0x0 len(0x1), Member, Type: int, Wow64Message
Data           :   this(bf)+0x34:0x1 len(0x1), Member, Type: int, NoCoalesce
Data           :   this(bf)+0x34:0x2 len(0x1), Member, Type: int, FromTouch
Data           :   this(bf)+0x34:0x3 len(0x1), Member, Type: int, FromPen
Data           :   this+0x38, Member, Type: struct tagTHREADINFO *, pti
Data           :   this+0x3C, Member, Type: struct tagMSGPPINFO, MsgPPInfo
 
UserDefinedType: tagQ
Data           :   this+0x0, Member, Type: struct tagMLIST, mlInput
Data           :   this+0xC, Member, Type: struct tagTHREADINFO *, ptiSysLock
Data           :   this+0x10, Member, Type: unsigned long, idSysLock
Data           :   this+0x14, Member, Type: unsigned long, idSysPeek
Data           :   this+0x18, Member, Type: struct tagTHREADINFO *, ptiMouse
Data           :   this+0x1C, Member, Type: struct tagTHREADINFO *, ptiKeyboard
Data           :   this+0x20, Member, Type: struct tagWND *, spwndCapture
Data           :   this+0x24, Member, Type: struct tagWND *, spwndFocus
Data           :   this+0x28, Member, Type: struct tagWND *, spwndActive
Data           :   this+0x2C, Member, Type: struct tagWND *, spwndActivePrev
Data           :   this+0x30, Member, Type: unsigned int, codeCapture
Data           :   this+0x34, Member, Type: unsigned int, msgDblClk
Data           :   this+0x38, Member, Type: unsigned short, xbtnDblClk
Data           :   this+0x3C, Member, Type: unsigned long, timeDblClk
Data           :   this+0x40, Member, Type: struct HWND__ *, hwndDblClk
Data           :   this+0x44, Member, Type: struct tagPOINT, ptDblClk
Data           :   this+0x4C, Member, Type: struct tagPOINT, ptMouseMove
Data           :   this+0x54, Member, Type: unsigned char[0x20], afKeyRecentDown
Data           :   this+0x74, Member, Type: unsigned char[0x40], afKeyState
Data           :   this+0xB4, Member, Type: struct tagCARET, caret
Data           :   this+0xEC, Member, Type: struct tagCURSOR *, spcurCurrent
Data           :   this+0xF0, Member, Type: int, iCursorLevel
Data           :   this+0xF4, Member, Type: unsigned long, QF_flags
Data           :   this+0xF8, Member, Type: unsigned short, cThreads
Data           :   this+0xFA, Member, Type: unsigned short, cLockCount
Data           :   this+0xFC, Member, Type: unsigned int, msgJournal
Data           :   this+0x100, Member, Type: long, ExtraInfo
Data           :   this+0x104, Member, Type: unsigned long, ulEtwReserved1
 
UserDefinedType: tagSMS
Data           :   this+0x0, Member, Type: struct tagSMS *, psmsNext
Data           :   this+0x4, Member, Type: struct tagSMS *, psmsReceiveNext
Data           :   this+0x8, Member, Type: struct tagTHREADINFO *, ptiSender
Data           :   this+0xC, Member, Type: struct tagTHREADINFO *, ptiReceiver
Data           :   this+0x10, Member, Type: function  *, lpResultCallBack
Data           :   this+0x14, Member, Type: unsigned long, dwData
Data           :   this+0x18, Member, Type: struct tagTHREADINFO *, ptiCallBackSender
Data           :   this+0x1C, Member, Type: long, lRet
Data           :   this+0x20, Member, Type: unsigned long, tSent
Data           :   this+0x24, Member, Type: unsigned int, flags
Data           :   this+0x28, Member, Type: unsigned int, wParam
Data           :   this+0x2C, Member, Type: long, lParam
Data           :   this+0x30, Member, Type: unsigned int, message
Data           :   this+0x34, Member, Type: struct tagWND *, spwnd
Data           :   this+0x38, Member, Type: void *, pvCapture

All that’s required to build a list of pending messages is to travel the links of the mlPost.pqmsgRead and pq->mlInput.pqmsgRead structures copying the msg member, and the psmsmsgRecieveList constructing a MSG structure from its’ various members. So that’s what I did. Enter MsgLister, a dumper of said message stores for any Window on the calling threads desktop. It also retrives other relevant data from the thread including messages to be generated (WM_PAINT, WM_TIMER, WM_QUIT), the SetMessageExtraInfo() value, who sent the sent messages and via which of the functions, etc.

Due to the aforementioned changes, it will work on RTM and above versions of Vista, Server 2008, and 7, but it won’t work on 7 RC unless you grab the win32k symbols and modify the structs in structs.h accordingly. Only x86 and AMD64 platforms are supported but who’s using Itanium anyway?

Binaries and source can be found here (1.29 MB). Happy spying.

[1] This pointer can be retrieved for an arbitrary ETHREAD by use of PsGetThreadWin32Thread(ethread), and is helpfully returned (for win32k anyway) for the current thread by ExEnterPriorityRegionAndAcquireResourceShared(pEresource) and its Exclusive counterpart.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress