Window Messages as a Forensic Resource

In which window messages are explored, a new plugin is created, and we learn the importance of reading messages sent to you regularly.

In Windows, the GUI system is event-driven–actions occur in response to various events generated by the system. When you press a key on the keyboard, Windows generates a window message (specifically, WM_KEYDOWN) and sends it to the thread that owns the window that's in focus. That thread then call's the window's event handling procedure (the so-called WindowProc) to process the message. There are many such messages, covering input events such as keyboard and mouse actions, system-level events such as notification of a time change or a change in the system's power state, and events related to the windowing system, such as resizing or moving a window.

How can these be forensically relevant? Well, as it turns out, some threads in buggy applications aren't always good at processing their messages, and messages they receive pile up in the queue. This means that by looking at the message queues on a system, we can get some information about its past state. To make this more concrete, let's look at the message queue for a thread belonging to a certain full-disk encryption vendor on one of the images in my collection:
0:03:42.812,WM_WTSSESSION_CHANGE,WTS_CONSOLE_CONNECT
0:03:42,WM_WTSSESSION_CHANGE,WTS_SESSION_LOGON
1:45:05,WM_WTSSESSION_CHANGE,WTS_CONSOLE_DISCONNECT
1:45:06,WM_WTSSESSION_CHANGE,WTS_REMOTE_CONNECT
1:45:34,WM_WTSSESSION_CHANGE,WTS_REMOTE_DISCONNECT
1:46:19,WM_WTSSESSION_CHANGE,WTS_CONSOLE_CONNECT
2:28:50,WM_WTSSESSION_CHANGE,WTS_SESSION_LOCK
18:18:07,WM_WTSSESSION_CHANGE,WTS_SESSION_UNLOCK
22:05:51,WM_WTSSESSION_CHANGE,WTS_SESSION_LOCK
22:23:47,WM_WTSSESSION_CHANGE,WTS_SESSION_UNLOCK
23:32:34,WM_WTSSESSION_CHANGE,WTS_SESSION_LOCK
23:34:22,WM_WTSSESSION_CHANGE,WTS_SESSION_UNLOCK
1 day, 0:07:54.468,WM_WTSSESSION_CHANGE,WTS_SESSION_LOCK
[...]
8 days, 0:18:13.046,WM_WTSSESSION_CHANGE,WTS_SESSION_UNLOCK
8 days, 0:32:02.640,WM_WTSSESSION_CHANGE,WTS_SESSION_LOCK
8 days, 0:36:01.500,WM_WTSSESSION_CHANGE,WTS_SESSION_UNLOCK
(I've omitted many of the details here to save space. Window messages also include things like the handle of the window the message is for and cursor position at the time the message was sent.)

If we look up WM_WTSSESSION_CHANGE on MSDN, we find that it's a message related to fast user switching; one of these is sent whenever someone logs in, locks the screen, or connects remotely (i.e. via remote desktop). The message is sent to all applications that have called WTSRegisterSessionNotification. However, in this case, despite the fact that the application asked to be notified of such changes, it didn't bother to process the messages it received! This means that we can now look through its queue and find out when the user logged in and out, when the screen was locked, and so on. (The times given are relative to the time the system was booted, and are only good up to 49.7 days–this is because the timestamp comes from the GetTickCount function). It should be clear why such information might be useful in a forensic investigation.

I want to stress that we simply got lucky in this case by finding such a wealth of information in the message queue. It would be unwise to rely on every system having a misbehaving application like the one in this example; on the NIST images, for example, and on my own (clean) test VMs, there were no messages found on the system at all–the applications involved had processed them, and so they were no longer queued. Still, on real-world systems, buggy applications may be more common, so this trick could come in handy.

To look at the message queues in a memory dump, you can use a small plugin for Volatility that I've created. It enumerates all threads on the system and lists any messages it finds. It has an internal table mapping numeric IDs to names for a large number of standard window messages; however, it is very likely that the list is not complete. In addition, applications can define their own message types; in these cases, interpreting the message is impossible without analyzing the program. Caveats aside, I'm happy to offer threadqueues.py. As usual, drop it into your "memory_plugins/" directory, and then run it with:
$ python volatility thread_queues -f [IMAGE]
As with the SSDT plugin, thread_queues requires my list-walking library, which should be placed in the "forensics/win32/" directory.

If you want to know how the plugin works internally, read on!

Each thread in Windows (represented by the _ETHREAD strucutre) has a field in its Thread Control Block (or Tcb, which is a _KTHREAD) called Win32Thread. This field points to a data structure, _W32THREAD, which is defined in the kernel-mode portion of the Windows graphical subsystem, win32k.sys. You can actually examine the _W32THREAD structure by issuing "dt win32k!_W32THREAD" in WinDbg; however, if you start reverse engineering win32k.sys, you'll quickly find that the information given there is far from complete. In fact, _W32THREAD is a much larger data structure, which includes information about the current desktop, keyboard layout, installed window hooks, and, most importantly for us, the input message queue. You can read more about the internals of win32k in Alex Ionescu's BlackHat talk from this year; however, most of the details are still unknown, and the best way to find out about them is to reverse engineer win32k.sys.

In my case, I found the answers I wanted in the PostMessage() function. To make a (very) long story short, PostMessage is defined as:

PostMessage(PWND window, ulong msg, ulong wParam, ulong lParam)

When called, it eventually allocates a new entry in the thread queue using AllocQEntry, places the message information into it with StoreQMessage (which also adds the timestamp and current cursor position), and finally tells the thread that it should wake up because there's a message pending (using SetWakeBit). In Windows XP SP2, the message queue is found at offset 0xD0 of _W32THREAD, and looks like:
typedef struct _MSG_QUEUE {
PMSG_QUEUE_ENTRY Head;
PMSG_QUEUE_ENTRY Tail;
unsigned long NumberOfMessages;
} MSG_QUEUE;
Each queue entry, in turn, looks like:
typedef struct _MSG_QUEUE_ENTRY {
PMSG_QUEUE_ENTRY pNext;
PMSG_QUEUE_ENTRY pPrev;
struct _MSG {
unsigned long hWnd;
unsigned long ulMsg;
unsigned long wParam;
unsigned long lParam;
unsigned long time; // In milliseconds
POINT pt; // Mouse cursor position
unsigned long dwUnknown;
unsigned long dwExtraInfo;
} Msg;
} MSG_QUEUE_ENTRY, * PMSG_QUEUE_ENTRY;
So for each thread, we can just start at the head of the queue, and keep following pNext until we get to the end, at which point pNext will be NULL.

One quick note of warning, though: when you're exploring these structures, you may initially think that all of the information is paged out, because none of the memory addresses seem valid. As it turns out, although Win32Thread and its friends all live in kernel space, this portion of kernel space is not visible from all threads. This flies in the face of a very common assumption in Windows memory analysis–that the kernel portion of memory looks the same to every process. In fact, the portions related to the GUI subsystem are only visible from processes that have at least one GUI thread! So, in particular, the System process, which is what's most commonly used in Volatility to access kernel memory, can't see any of the structures we're interested in. In my plugin, I make sure to use the address space of the process that owns the threads we're examining, so that the GUI structures will be accessible if the thread is a GUI thread.

There's a lot more cool stuff you can do once you start digging into the kernel mode portion of the Windows graphics system. In a future post, I'll describe how we can enumerate all the windows on the system, and list their titles and position, and (if I can figure out how) possibly even how to extract a screenshot at the time the memory was captured. Though this last bit doesn't have much forensic value, it would be pretty slick to be able to see exactly what was on the desktop for public images like the DFRWS challenge images or the NIST reference images.

Comments

H. Carvey said…
Moyix, this is awesome stuff! A great deal of the post-mortem user activity tracking analysis that one would do relies heavily on what the shell "records" when a user interacts with the desktop.

Great job! I can't wait to try it out!
H. Carvey said…
Moyix,

I was working with some other modules this morning (w/ AAron's help) and decided to grab your modules, as well, and take a look.

Very cool! I didn't find anything particularly useful, but it's exciting just to have this capability. One of the things I'd love to see in an analysis process is to have this as part of the in-processing of all memory dumps.

Thanks again!

Popular posts from this blog

Someone’s Been Messing With My Subnormals!

Decrypting LSA Secrets

SysKey and the SAM