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.