Since the early days of memory forensics, tools have analyzed kernel/executive objects such as processes, threads, mutexes, open files, and registry keys. In fact, I would consider that a basic capability of any framework. One thing that sets Volatility apart from other tools, besides the 10 points on our wiki and the massive number of new features already discussed in MoVP posts, is the ability to enumerate and analyze USER objects and USER handle tables. Today we'll introduce a plugin named gahti that determines the various different types of USER objects on a system and another plugin named userhandles which traverses the handle table entries and associates them with the owning processes or threads. As you'll soon see, this is an important aspect of forensics that no other tools implement - not even those with pretty GUIs or Microsoft's own kernel debugger.
USER Objects
There are either 20 (Windows XP through Vista) or 22 (Windows 7) types of USER objects, including TYPE_FREE. From a malware and forensics perspective, the user handle tables are extremely valuable, because they provide an alternative method of finding evidence. For example, we already discussed how to find windows and message hooks (both by following pointers in the tagDESKTOP structures), but the same objects can be found by walking the USER handle table. Thus if an attacker tried to get creative and hide objects using DKOM, they’d have to hide in two places rather than one. Also, the handle tables can serve as a primary way of finding objects that we don’t locate in other ways, such as clipboard data (see a future MoVP post).
USER Object Types (Gahti)
The win32k!_gahti symbol is an array of tagHANDLETYPEINFO structures – one for each object type. These structures are similar in concept to nt!_OBJECT_TYPE for executive objects - they provide a reference of information about the various object types. In particular the handle type info structures tell you what pool tags (if any) are associated with each object, where the object is allocated (desktop heap, shared heap, session pool), and whether the object is thread owned or process owned. The structure for x64 is shown below:
>>> dt("tagHANDLETYPEINFO")
'tagHANDLETYPEINFO' (16 bytes)
0x0 : fnDestroy ['pointer', ['void']]
0x8 : dwAllocTag ['String', {'length': 4}]
0xc : bObjectCreateFlags ['Flags', {'target': 'unsigned char', 'bitmap': {'OCF_VARIABLESIZE': 7, 'OCF_DESKTOPHEAP': 4, 'OCF_THREADOWNED': 0, 'OCF_SHAREDHEAP': 6, 'OCF_USEPOOLIFNODESKTOP': 5, 'OCF_USEPOOLQUOTA': 3, 'OCF_MARKPROCESS': 2, 'OCF_PROCESSOWNED': 1}}]
Here are some explanations to the members:
- fnDestroy member points to the deallocation/cleanup function for the object type
- dwAllocTag is similar to a pool tag - a 32-bit value that can be used to find or identify the allocation in memory
- bObjectCreateFlags can be used to determine if an object is thread-owned or process-owned, if its allocated from the shared or desktop heaps, etc
Before moving on, let’s take a quick look at the output of the gahti plugin on an x64 Windows 7 system. The first structure in the gahti is always for TYPE_FREE, whose members are all 0. You can then see TYPE_WINDOW objects have a tag Uswd, they are thread-owned and they’re allocated from the desktop heap (or failing that, the session pool). TYPE_TIMER (GDI timers) objects are process owned. TYPE_CLIPDATA (clipboard objects) are neither thread nor process owned – which makes sense because data copied to the clipboard is freely accessible by any process (in the same session) that calls GetClipboardData.
$ python vol.py -f win7x64cmd.dd --profile=Win7SP1x64 gahti
Volatile Systems Volatility Framework 2.1_alpha
Session Type Tag fnDestroy Flags
-------- -------------------- -------- ------------------ -----
0 TYPE_FREE 0x0000000000000000
0 TYPE_WINDOW Uswd 0xfffff9600014f660 OCF_DESKTOPHEAP, OCF_THREADOWNED, OCF_USEPOOLIFNODESKTOP, OCF_USEPOOLQUOTA
0 TYPE_MENU 0xfffff960001515ac OCF_DESKTOPHEAP, OCF_PROCESSOWNED
0 TYPE_CURSOR Uscu 0xfffff960001541a0 OCF_MARKPROCESS, OCF_PROCESSOWNED, OCF_USEPOOLQUOTA
0 TYPE_SETWINDOWPOS Ussw 0xfffff960001192b4 OCF_THREADOWNED, OCF_USEPOOLQUOTA
0 TYPE_HOOK 0xfffff9600018e5c8 OCF_DESKTOPHEAP, OCF_THREADOWNED
0 TYPE_CLIPDATA Uscb 0xfffff9600017c5ac
0 TYPE_CALLPROC 0xfffff9600017c5cc OCF_DESKTOPHEAP, OCF_PROCESSOWNED
0 TYPE_ACCELTABLE Usac 0xfffff9600017c5cc OCF_PROCESSOWNED, OCF_USEPOOLQUOTA
0 TYPE_DDEACCESS Usd9 0xfffff9600017c5ac OCF_THREADOWNED, OCF_USEPOOLQUOTA
0 TYPE_DDECONV UsdA 0xfffff960001ba1fc OCF_THREADOWNED, OCF_USEPOOLQUOTA
0 TYPE_DDEXACT UsdB 0xfffff960001ba22c OCF_THREADOWNED, OCF_USEPOOLQUOTA
0 TYPE_MONITOR Usdi 0xfffff960001ca76c OCF_SHAREDHEAP
0 TYPE_KBDLAYOUT Uskb 0xfffff960001b7c28
0 TYPE_KBDFILE Uskf 0xfffff960001b77c8
0 TYPE_WINEVENTHOOK Uswe 0xfffff9600018f148 OCF_THREADOWNED
0 TYPE_TIMER Ustm 0xfffff960001046dc OCF_PROCESSOWNED
0 TYPE_INPUTCONTEXT Usim 0xfffff9600014c660 OCF_DESKTOPHEAP, OCF_THREADOWNED
0 TYPE_HIDDATA Usha 0xfffff960001d2a34 OCF_THREADOWNED
0 TYPE_DEVICEINFO UsDI 0xfffff960000d8cd4
0 TYPE_TOUCH Ustz 0xfffff9600017c5cc OCF_THREADOWNED
0 TYPE_GESTURE Usgi 0xfffff9600017c5cc OCF_THREADOWNED
The Shared Info Structure
Now that you know what type of USER objects exist, we should discuss the critical tagSHAREDINFO structure - which can be found at win32k!_gSharedInfo. This structure contains a pointer to the USER handle table, which is basically a map to all USER objects in use on the system. The challenging part is finding this symbol and structure in a 10 GB (or however large your RAM is) memory dump. We cannot rely on calling APIs from dbghelp.dll because Volatility must work on non-Windows analysis systems. Although there is a Python PDB parser written by Brendan Dolan-Gavitt (a Volatility developer), its not integrated yet into the core code base. Furthermore, the structure we're looking for isn't allocated dynamically, thus there are no pool tags to scan for. We must overcome these challenges to find a method that is both accurate (i.e. it only finds one possibility - the correct one) and reliable (i.e. it works across all Windows versions, service packs and hardware architectures).
To find the needle in the haystack, start with what you know - the symbol is somewhere in win32k.sys. Already that clue narrows our search down to 3-5 MB. Then do a little digging - open win32k.sys in IDA Pro and use the Names pane to locate _gSharedInfo. As shown below, the symbol exists in the .data section of the PE file. Depending on the build of win32k.sys being analyzed, now the search can be narrowed to about 100 - 150 KB.
At this point, we can use some basic pattern/member matching to find the structure. Yet before we do that, you should know a little bit about the structure whose members you're trying to match. On a Windows 7 x64 system, the tagSHAREDINFO looks like this:
>>> dt("tagSHAREDINFO")
'tagSHAREDINFO' (568 bytes)
0x0 : psi ['pointer64', ['tagSERVERINFO']]
0x8 : aheList ['pointer64', ['_HANDLEENTRY']]
0x10 : HeEntrySize ['unsigned long']
0x18 : pDispInfo ['pointer64', ['tagDISPLAYINFO']]
0x20 : ulSharedDelta ['unsigned long long']
0x28 : awmControl ['array', 31, ['_WNDMSG']]
0x218 : DefWindowMsgs ['_WNDMSG']
0x228 : DefWindowSpecMsgs ['_WNDMSG']
To find the needle in the haystack, start with what you know - the symbol is somewhere in win32k.sys. Already that clue narrows our search down to 3-5 MB. Then do a little digging - open win32k.sys in IDA Pro and use the Names pane to locate _gSharedInfo. As shown below, the symbol exists in the .data section of the PE file. Depending on the build of win32k.sys being analyzed, now the search can be narrowed to about 100 - 150 KB.
At this point, we can use some basic pattern/member matching to find the structure. Yet before we do that, you should know a little bit about the structure whose members you're trying to match. On a Windows 7 x64 system, the tagSHAREDINFO looks like this:
>>> dt("tagSHAREDINFO")
'tagSHAREDINFO' (568 bytes)
0x0 : psi ['pointer64', ['tagSERVERINFO']]
0x8 : aheList ['pointer64', ['_HANDLEENTRY']]
0x10 : HeEntrySize ['unsigned long']
0x18 : pDispInfo ['pointer64', ['tagDISPLAYINFO']]
0x20 : ulSharedDelta ['unsigned long long']
0x28 : awmControl ['array', 31, ['_WNDMSG']]
0x218 : DefWindowMsgs ['_WNDMSG']
0x228 : DefWindowSpecMsgs ['_WNDMSG']
The psi member should point to a valid tagSERVERINFO structure. The aheList should point to an array of _HANDLEENTRY – one for each handle in the table. To determine how many handles are currently in use, you can look at tagSHAREDINFO.psi.cHandleEntries. The HeEntrySize is equal to sizeof(_HANDLEENTRY) for the current platform. The ulSharedDelta is a delta that usermode processes can use to determine the location of USER objects in kernel memory. With the information we've gathered so far, its possible to write code for Volatility that can in fact find this needle in a haystack. The procedure is as follows:
- Determine the base address of win32k.sys as mapped into the session space
- Locate the .data PE section. If the PE header is corrupt or paged (we've seen this happen in very busy large memory systems like 80 GB RAM Hyper-V cluster servers), then we just brute force our search using the 3-5 MB full length of win32k.sys instead of just the .data section.
- Iterate over the data on a DWORD boundary and instantiate a tagSHAREDINFO object at the address - then determine if its valid using the class's is_valid method - this is where the sanity checks are performed.
Just to give you an idea of how that last step appears in Python within Volatility's architecture, here it is:
class tagSHAREDINFO(obj.CType):
"""A class for shared info blocks"""
def is_valid(self):
"""The sanity checks for tagSHAREDINFO structures"""
if not obj.CType.is_valid(self):
return False
# The kernel's version of tagSHAREDINFO should always have
# a zeroed-out shared delta member.
if self.ulSharedDelta != 0:
return False
# The pointer to our server information structure must be valid
if not self.psi.is_valid():
return False
# The pointer to the handle table must be valid
if not self.aheList.is_valid():
return False
# The final check is that the total size in bytes of the handle
# table is equal to the size of a _HANDLEENTRY multiplied by the
# number of _HANDLEENTRY structures.
return (self.psi.cbHandleTable /
self.obj_vm.profile.get_obj_size("_HANDLEENTRY")
== self.psi.cHandleEntries)
Now you should be able to find the tagSHAREDINFO structure on all versions of Windows without any problem. Tarjei Mandt’s Windows Hook of Death: Kernel Attacks through Usermode Callbacks describes two additional ways of finding tagSHAREDINFO, which can also be useful depending on your context. For example, you can use user32!_gSharedInfo – a symbol exported on Windows 7 (but only Windows 7); or on a live system you can call the CsrClientConnectToServer API function.
The Handle Table Entries
A handle table entry on Windows 7 x64 looks like this:
>>> dt("_HANDLEENTRY")
'_HANDLEENTRY' (24 bytes)
0x0 : phead ['pointer64', ['_HEAD']]
0x8 : pOwner ['pointer64', ['void']]
0x10 : bType ['Enumeration', {'target': 'unsigned char', 'choices': {0: 'TYPE_FREE', 1: 'TYPE_WINDOW', 2: 'TYPE_MENU', 3: 'TYPE_CURSOR', 4: 'TYPE_SETWINDOWPOS', 5: 'TYPE_HOOK', 6: 'TYPE_CLIPDATA', 7: 'TYPE_CALLPROC', 8: 'TYPE_ACCELTABLE', 9: 'TYPE_DDEACCESS', 10: 'TYPE_DDECONV', 11: 'TYPE_DDEXACT', 12: 'TYPE_MONITOR', 13: 'TYPE_KBDLAYOUT', 14: 'TYPE_KBDFILE', 15: 'TYPE_WINEVENTHOOK', 16: 'TYPE_TIMER', 17: 'TYPE_INPUTCONTEXT', 18: 'TYPE_CTYPES', 19: 'TYPE_GENERIC'}}]
0x11 : bFlags ['unsigned char']
0x12 : wUniq ['unsigned short']
All USER objects start with one of the common headers, which are pointed to by the _HANDLEENTRY.phead member. The bType tells you what type of object the handle is for, and based on the information previously dumped from win32k!_gahti, you know which objects are thread-owned (they’ll begin with a _THRDESKHEAD) and which are process-owned (they’ll begin with a _PROCDESKHEAD). Objects like TYPE_CLIPBOARD begin with the generic _HEAD. Here are the three possibilities:
>>> dt("_HEAD")
'_HEAD' (16 bytes)
0x0 : h ['pointer64', ['void']]
0x8 : cLockObj ['unsigned long']
>>> dt("_THRDESKHEAD")
'_THRDESKHEAD' (40 bytes)
0x0 : h ['pointer64', ['void']]
0x8 : cLockObj ['unsigned long']
0x10 : pti ['pointer64', ['tagTHREADINFO']]
0x18 : rpdesk ['pointer64', ['tagDESKTOP']]
0x20 : pSelf ['pointer64', ['unsigned char']]
>>> dt("_PROCDESKHEAD")
'_PROCDESKHEAD' (40 bytes)
0x0 : h ['pointer64', ['void']]
0x8 : cLockObj ['unsigned long']
0x10 : hTaskWow ['unsigned long']
0x18 : rpdesk ['pointer64', ['tagDESKTOP']]
0x20 : pSelf ['pointer64', ['unsigned char']]
>>> dt("_HEAD")
'_HEAD' (16 bytes)
0x0 : h ['pointer64', ['void']]
0x8 : cLockObj ['unsigned long']
>>> dt("_THRDESKHEAD")
'_THRDESKHEAD' (40 bytes)
0x0 : h ['pointer64', ['void']]
0x8 : cLockObj ['unsigned long']
0x10 : pti ['pointer64', ['tagTHREADINFO']]
0x18 : rpdesk ['pointer64', ['tagDESKTOP']]
0x20 : pSelf ['pointer64', ['unsigned char']]
>>> dt("_PROCDESKHEAD")
'_PROCDESKHEAD' (40 bytes)
0x0 : h ['pointer64', ['void']]
0x8 : cLockObj ['unsigned long']
0x10 : hTaskWow ['unsigned long']
0x18 : rpdesk ['pointer64', ['tagDESKTOP']]
0x20 : pSelf ['pointer64', ['unsigned char']]
If an object in the handle table is thread-owned, you can identify the object’s owning thread using _THRDESKHEAD.pti, which is a pointer to tagTHREADINFO. From there, you can use the tagTHREADINFO.pEThread member which points to the execute thread object (_ETHREAD).
On the other hand, if an object is process-owned, the _HANDLEENTRY.pOwner field is a pointer to the owning tagPROCESSINFO. From there, tagPROCESSINFO.Process identifies the _EPROCESS.
Now, regardless of whether USER objects are thread- or process-owned, you can associate them with the executive _ETHREAD or _EPROCESS objects that everyone is familiar with. Bravo!
Guess what else this information can be useful for? Cross-referencing thread and process objects for rootkit detection ala psxview. You've heard of processes hiding by unlinking from PsActiveProcessHead. You've heard of them mangling their pool tags and non-critical members to hide from scanning techniques. I would be surprised if any rootkits exist, even in a proof-of-concept state that hide using both of those techniques, the 5-6 others that psxview already detects and removes all of the processes handles from the USER handle table.
On the other hand, if an object is process-owned, the _HANDLEENTRY.pOwner field is a pointer to the owning tagPROCESSINFO. From there, tagPROCESSINFO.Process identifies the _EPROCESS.
Now, regardless of whether USER objects are thread- or process-owned, you can associate them with the executive _ETHREAD or _EPROCESS objects that everyone is familiar with. Bravo!
Guess what else this information can be useful for? Cross-referencing thread and process objects for rootkit detection ala psxview. You've heard of processes hiding by unlinking from PsActiveProcessHead. You've heard of them mangling their pool tags and non-critical members to hide from scanning techniques. I would be surprised if any rootkits exist, even in a proof-of-concept state that hide using both of those techniques, the 5-6 others that psxview already detects and removes all of the processes handles from the USER handle table.
The UserHandles Plugin
The userhandles plugin locates the shared information structure for each session, walks the handle table, and prints out the contents.
$ python vol.py -f win7x64.dd --profile=Win7SP1x64 userhandles
Volatile Systems Volatility Framework 2.1_alpha
**************************************************
SharedInfo: 0xfffff9600035d300, SessionId: 0
aheList: 0xfffff900c0400000, Table size: 0x2000, Entry size: 0x18
Object(V) Handle bType Flags Thread Process
------------------ ------------ --------------- -------- -------- -------
0xfffff900c05824b0 0x10001 TYPE_MONITOR 0 -------- -
0xfffff900c01bad20 0x10002 TYPE_WINDOW 64 432 316
0xfffff900c00b6730 0x10003 TYPE_CURSOR 0 -------- 316
0xfffff900c0390b90 0x10004 TYPE_WINDOW 0 432 316
0xfffff900c00d7ab0 0x10005 TYPE_CURSOR 0 -------- 316
0xfffff900c0390e60 0x10006 TYPE_WINDOW 0 432 316
0xfffff900c00d7640 0x10007 TYPE_CURSOR 0 -------- 316
[snip]
0xfffff900c0630bf0 0x467c054b TYPE_HOOK 0 2368 2348
0xfffff900c0616d60 0x72055f TYPE_MENU 0 -------- 880
0xfffff900c0654610 0x494c0581 TYPE_MENU 0 -------- 880
0xfffff900c1a14b10 0x539f0583 TYPE_CURSOR 0 -------- 880
[snip]
This plugin has three additional command-line arguments:
$ python vol.py userhandles –-help
[snip]
-p PID, --pid=PID Pid filter
-t TYPE, --type=TYPE Handle type
-F, --free Include free handles
To only show USER objects owned by a particular process use --pid=PID. To filter by object type, use something like --type=TYPE_HOOK. Finally, if you want to include information on handles marked as freed, then use --free.
Although the output of this plugin
is not very verbose, it gives you an overview of the types of objects being
used by a particular thread or process. Furthermore, it can be used as an API
for other plugins to find specific object types and then go into detail about them. In fact this is exactly how the previously described eventhooks plugin works, and several other plugins which will be introduced in future MoVP posts.
Conclusion
What you've been learning over the past few weeks of MoVP posts is that there's entirely new and unexplored areas of kernel memory where malware lurks; and the evidence begs to be found. We enjoy focusing on challenging, innovative, and technical problems. Just because Microsoft doesn't document it, doesn't mean you can't analyze it with Volatility!
More information on the gahti and userhandles plugins and their usages in forensic investigations will be presented at Open Memory Forensics Workshop (OMFW) 2012.
What you've been learning over the past few weeks of MoVP posts is that there's entirely new and unexplored areas of kernel memory where malware lurks; and the evidence begs to be found. We enjoy focusing on challenging, innovative, and technical problems. Just because Microsoft doesn't document it, doesn't mean you can't analyze it with Volatility!
More information on the gahti and userhandles plugins and their usages in forensic investigations will be presented at Open Memory Forensics Workshop (OMFW) 2012.
No comments:
Post a Comment