Wednesday, September 26, 2012

MoVP 3.3 Analyzing USER Handles and the Win32k.sys Gahti

Month of Volatility Plugins

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:

'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 -f win7x64cmd.dd --profile=Win7SP1x64 gahti
Volatile Systems Volatility Framework 2.1_alpha
Session  Type                 Tag      fnDestroy          Flags
-------- -------------------- -------- ------------------ -----
       0 TYPE_FREE                     0x0000000000000000 
       0 TYPE_MENU                     0xfffff960001515ac OCF_DESKTOPHEAP, OCF_PROCESSOWNED
       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']

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:
  1. Determine the base address of win32k.sys as mapped into the session space
  2. 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.
  3. 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.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']]
'_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. 

The UserHandles Plugin

The userhandles plugin locates the shared information structure for each session, walks the handle table, and prints out the contents. 

$ python -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
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

This plugin has three additional command-line arguments:

$ python userhandles –-help
  -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. 


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