for proc in tasks.pslist(addr_space):
for thread in proc.ThreadListHead.list_of_type("_ETHREAD", "ThreadListEntry"):
# print thread.ExitTimeAs shown, in the inner for loop, you would reference members of _ETHREAD in an object-oriented manner, just like any other Python class. This gives you the ability to use or print the data in any way you want. For example, if you can't identify the owning driver for a thread's start address (thread.StartAddress), then it might be an orphan thread left by a rootkit. You may also want to know which threads are using an SSDT (thread.Tcb.ServiceTable) with hooked functions. Plugins for Volatility, which implement both of these checks, are described in Recipe 17.6 Detecting SSDT Hooks and Recipe 17.8 Finding Rootkits with Detached Kernel Threads of Malware Analyst's Cookbook.After careful consideration ;-) I decided that instead of creating an individual plugin for each heuristic, we could simply have one which performs all checks, can be easily extended with other checks, and that more or less breaths some life into the ability to report on threads in physical memory dumps. After all, there are lots of memory forensic tools that can enumerate threads, but output is typically limited to basic information about the thread (such as thread ID, owning process, and start/exit times). From an analysis perspective, after you've found a thread...what's next?
To help address these issues, I created a new plugin named threads. It uses whatever heuristics you give it and associates a descriptive tag to threads found in the memory dump. Then you filter by tag when performing an investigation. For example, you could ask it to only show threads hidden from a debugger. To see the list of possible arguments to the threads command, use it first with -h:
$ python vol.py -f test.vmem threads -h
Volatile Systems Volatility Framework 1.4_rc1
Usage: Volatility - A memory forensics analysis platform.
Options:
......
-F FILTER, --filter=FILTER
Tags to filter (comma-separated)
-A ALLOW_HOOK, --allow-hook=ALLOW_HOOK
Allow SSDT hooks from these mods (comma-separated)
-p PID, --pid=PID Operate on these Process IDs (comma-separated)
-L, --listtags List all available tagsTo list the existing tags and their descriptions, use the -L flag to the plugin:$ python vol.py -f test.vmem threads -L
Volatile Systems Volatility Framework 1.4_rc1
Tag Description
-------------- --------------
DkomExit Detect inconsistencies wrt exit times and termination
HwBreakpoints Detect threads with hardware breakpoints
ScannerOnly Detect threads no longer in a linked list
HideFromDebug Detect threads hidden from debuggers
OrphanThread Detect orphan threads
AttachedProcess Detect threads attached to another process
HookedSSDT Detect threads using a hooked SSDT
SystemThread Detect system threadsIf available , for the threads that meet your filtering criteria, the plugin will also print the contents of the thread's registers at the time of the memory dump and disassemble code at the thread's start address. Now let's discuss each of the heuristics and see some example output.The OrphanThread Tag
This tag is associated with system threads (see the SystemThread tag) whose StartAddress does not map back to a kernel driver in the PsLoadedModuleList. It can find threads created by a rootkit that unloads or unlinks its .sys in order to hide. Here is example output from the plugin on a memory dump infected with Tigger.
$ python vol.py -f tigger.vmem threads -F OrphanThread
Volatile Systems Volatility Framework 1.4_rc1
------
ETHREAD: 0xff1f92b0 Pid: 4 Tid: 1648
Tags: OrphanThread,SystemThread
Created: 2010-08-15 19:26:13
Exited: -
Owning Process: 0x810b1660 System
Attached Process: 0x810b1660 System
State: Waiting:DelayExecution
BasePriority: THREAD_PRIORITY_NORMAL
TEB: 0x00000000
StartAddress: 0xf2edd150
ServiceTable: 0x80552180
[0] 0x80501030
[1] -
[2] -
[3] -
Win32Thread: 0x00000000
CrossThreadFlags: PS_CROSS_THREAD_FLAGS_SYSTEM
f2edd150: 803d782aeff200 CMP BYTE [0xf2ef2a78], 0x0
f2edd157: 7437 JZ 0xf2edd190
f2edd159: 56 PUSH ESI
f2edd15a: bef0d0edf2 MOV ESI, 0xf2edd0f0
f2edd15f: ff35702aeff2 PUSH DWORD [0xf2ef2a70]There are several key points to note:* The start address is 0xf2edd150 and no module name is printed, since it can't be identified
* The thread is probably still running, because there is a create time but no exit time and the thread's state is waiting
* This is not a GUI thread, because the SSDT[1] (for the win32k.sys Shadow Table) is not initialized and the Win32Thread pointer is NULL
The SystemThread Tag
This tag is associated with threads created by calls to PsCreateSystemThread. You can distinguish these threads because they're owned by the System process, the PS_CROSS_THREAD_FLAGS_SYSTEM bit is set in _ETHREAD.CrossThreadFlags, and in most cases the _TEB (Thread Environment Block) pointer will also be NULL. An interesting thing to note about the PS_CROSS_THREAD_FLAGS_SYSTEM flag is that it can be set for non-system threads, which on some versions of Windows can help prevent the thread or owning process from being terminated. Naturally, some Chinese hacker forums (visit at your own risk) site 1 and site 2 has some source code regarding that fact.
The HookedSSDT Tag
This tag is associated with threads whose _ETHREAD.Tcb.ServiceTable points to one or more SSDTs with hooked functions. Depending on how a rootkit installs SSDT hooks, the hooks may not apply to all threads. To see which threads are affected and the names of the hooked functions, you can use the following filter (example is using BlackEnergy 2):
$ python vol.py -f be2.vmem threads -F HookedSSDT
Volatile Systems Volatility Framework 1.4_rc1
------
ETHREAD: 0xff20c6f8 Pid: 1028 Tid: 1284
Tags: HookedSSDT
Created: 2010-08-15 19:22:11
Exited: -
Owning Process: 0x80fbf910 svchost.exe
Attached Process: 0x80fbf910 svchost.exe
State: Waiting:WrLpcReceive
BasePriority: THREAD_PRIORITY_ABOVE_NORMAL
TEB: 0x7ff95000
StartAddress: 0x7c810856
ServiceTable: 0xff1ef008
[0] 0xff3aab90
[0x41] NtDeleteValueKey 0xff0d2487 00004A2A
[0x47] NtEnumerateKey 0xff0d216b 00004A2A
[0x49] NtEnumerateValueKey 0xff0d2267 00004A2A
[0x77] NtOpenKey 0xff0d20c3 00004A2A
[0x7a] NtOpenProcess 0xff0d1e93 00004A2A
[0x80] NtOpenThread 0xff0d1f0b 00004A2A
[0x89] NtProtectVirtualMemory 0xff0d2617 00004A2A
[0xad] NtQuerySystemInformation 0xff0d1da0 00004A2A
[0xba] NtReadVirtualMemory 0xff0d256b 00004A2A
[0xd5] NtSetContextThread 0xff0d2070 00004A2A
[0xf7] NtSetValueKey 0xff0d2397 00004A2A
[0xfe] NtSuspendThread 0xff0d201d 00004A2A
[0x102] NtTerminateThread 0xff0d1fca 00004A2A
[0x115] NtWriteVirtualMemory 0xff0d25c1 00004A2A
[1] -
[2] -
[3] -
Win32Thread: 0x00000000
CrossThreadFlags:
Eip: 0x7c90eb94
eax=0x7509b647 ebx=0x750a3db0 ecx=0x7c914d8f
edx=0x01f7faec esi=0x00000001 edi=0x00000000
eip=0x7c90eb94 esp=0x0168fde8 ebp=0x0168ff34 err=0x00000000
cs=0x1b ss=0x23 ds=0x23 es=0x23 gs=0x00 efl=0x00000246
dr0=0x00000000 dr1=0x00000000 dr2=0x00000000
dr3=0x00000000 dr6=0x00000000 dr7=0x00000000Notice in the output that the thread's service table is 0xff1ef008. This points to an array of 4 SSDTs. The first one at index [0] is the Native SSDT whose base is 0xff3aab90. It contains 14 hooked functions, all pointing to a driver named 00004A2A.sys. Therefore, when this thread calls any of the 14 functions, it will be routed through a malicious driver.The -A or --allow-hook parameter to the threads command allows you to specify the names of drivers that can legitimately set SSDT hooks. For example, if good.sys (an anti-virus driver) hooks the SSDT for all threads and bad.sys (a rootkit) hooks the SSDT for only a few threads, you may want to use the HookedSSDT filter and only focus on threads hooked by bad.sys. In that case just specify --allow-hook=good.sys.
The ScannerOnly Tag
This tag is associated with threads identified by the pool scanner only (not by list traversal). This does not mean that a rootkit maliciously unlinked a thread from the list or that the thread is hidden in some way. It most often means that the thread has simply exited (and thus has legitimately been unlinked) or the thread belongs to a process that isn't in the PsActiveProcessHead list. You typically won't set a filter specifically for ScannerOnly, but you will see it frequently paired with other tags - so its good to be aware of what it means.
The DkomExit Tag
This tag is associated with threads that are still running but whose ExitTime has been filled in. Of the existing memory analysis tools that print exit times, you'll typically see a date or an empty string (if the ExitTime is all zeros). If a rootkit uses Direct Kernel Object Manipulation (DKOM) and fills in a thread's ExitTime, it will appear as if the thread has exited even if it's still running. Thus relying on the ExitTime alone is something we want to avoid.
The purpose of the DkomExit check is to cross-reference the ExitTime with other fields in the _ETHREAD structure, that if forged, would probably cause serious problems for the thread. These other fields include the thread's state (see _KTHREAD_STATE) and the thread's flags (see the PS_CROSS_THREAD_FLAGS_* definitions). To evaluate if this technique will yield false positives, we have to consider when the kernel sets each of these fields during a thread's shutdown routine.
For now, let's assume that most threads terminate via NtTerminateThread or PsTerminateSystemThread. First, both functions call PspTerminateThreadByPointer, which sets the PS_CROSS_THREAD_FLAGS_TERMINATED bit in _ETHREAD.CrossThreadFlags. Second, PspExitThread is called, which uses KeQuerySystemTime to fill in the _ETHREAD.ExitTime. Third, KeTerminateThread is called, which sets the _ETHREAD.Tcb.State to Terminated. So theoretically we should never find an _ETHREAD whose State == Terminated but whose ExitTime is still all zeros and whose CrossThreadFlags does not have the PS_CROSS_THREAD_FLAGS_TERMINATED bit set.
Right.....? Wrong!
KeTerminateThread is exported by the NT module and can be called directly by a thread already running in kernel mode. In this case, the first and second steps will be skipped, but the third will be carried out. Here's a call flow diagram showing these possibilities:
Out of the first 15-20 memory dumps from an assortment of XP, 2003, Vista, 2008, and 7 machines that I used for testing, there weren't any false positives found for the DkomExit tag. So although its possible to encounter a false positive, it shouldn't happen frequently...and now you have an explanation to go along with it.The HideFromDebug Tag
As described in a Code Project article on Anti-Reverse Engineering (this may not be the original source), a thread can "hide" from a debugger by passing the HideThreadFromDebugger class to NtSetInformationThread. This doesn't exactly hide the thread, it just prevents debuggers from receiving events generated by the thread. Malware may do this to prevent analysts from catching breakpoints, for example. To detect threads with this special status, we check if the PS_CROSS_THREAD_FLAGS_HIDEFROMDBG bit is set in _ETHREAD.CrossThreadFlags. Here's an example created by running a compiled copy of the source code in the above article.
$ python vol.py -f Windows7.vmem --profile=Win7SP0x86 threads -F HideFromDebug
Volatile Systems Volatility Framework 1.4_rc1
------
ETHREAD: 0x84f5a158 Pid: 4052 Tid: 4080
Tags: HideFromDebug
Created: -
Exited: -
Owning Process: 0x84b17998 debugtest.exe
Attached Process: 0x84b17998 debugtest.exe
State: Waiting:DelayExecution
BasePriority: THREAD_PRIORITY_NORMAL
TEB: 0x7ffde000
StartAddress: 0x778764d8
ServiceTable: 0x829a99c0
[0] 0x828b06f0
[1] -
[2] -
[3] -
Win32Thread: 0x00000000
CrossThreadFlags: PS_CROSS_THREAD_FLAGS_HIDEFROMDBG
Eip: 0x778764f4
eax=0x00000000 ebx=0x7ffdf000 ecx=0x00000000
edx=0x778764f4 esi=0x0038fed4 edi=0x00000000
eip=0x778764f4 esp=0x0038fe90 ebp=0x0038fef8 err=0x00000000
cs=0x1b ss=0x23 ds=0x23 es=0x23 gs=0x00 efl=0x00000246
dr0=0x00000000 dr1=0x00000000 dr2=0x00000000
dr3=0x00000000 dr6=0x00000000 dr7=0x00000000The HwBreakpoints TagThis tag is associated with threads that have hardware breakpoints set. Hardware breakpoints are usually configured by attaching to a process with a debugger, as shown in the following screen shot.
Information about these breakpoints is stored in each thread's debug registers (Dr0 - Dr7) all of which are accessible by referencing _ETHREAD.Tcb.TrapFrame. User mode programs can't directly access these fields, but they can use the Win32 API function GetThreadContext. Once in kernel mode, this API copies values from the TrapFrame into a CONTEXT structure that the user mode programs can access. So now you know how to find EIP, EAX, ESP, EFLAGS, SS, DS, and all that good stuff...in case there is ever a reason to check those registers in your Volatility plugins ;-)As with some of the other topics we've discussed thus far, threads with hardware breakpoints set are not immediately suspicious. However, it's also not something you ordinarily see and its not unheard of to use hardware breakpoints as a stealthy alternative to Trampoline style API hooks. Instead of overwriting instructions in a function's prologue, a rootkit could set an On-Execute hardware breakpoint on the address of an API, and then set up an exception handler that gets called when the breakpoint triggers. Here's an example of using the filter:
$ python vol.py -f Windows7.vmem --profile=Win7SP0x86 threads -F HwBreakpoints
Volatile Systems Volatility Framework 1.4_rc1
------
ETHREAD: 0x8538d030 Pid: 2360 Tid: 1004
Tags: HwBreakpoints
Created: -
Exited: -
Owning Process: 0x84a488e0 calc.exe
Attached Process: 0x84a488e0 calc.exe
State: Waiting:UserRequest
BasePriority: THREAD_PRIORITY_NORMAL
TEB: 0x7ffdd000
StartAddress: 0x778764d8
ServiceTable: 0x829a99c0
[0] 0x828b06f0
[1] -
[2] -
[3] -
Win32Thread: 0x00000000
CrossThreadFlags: PS_CROSS_THREAD_FLAGS_DEADTHREAD
Eip: 0x778764f4
eax=0x00d9235b ebx=0x00000000 ecx=0x00000000
edx=0x00000000 esi=0x000000f4 edi=0x00000000
eip=0x778764f4 esp=0x02ebf7a0 ebp=0x02ebf80c err=0x00000000
cs=0x1b ss=0x23 ds=0x23 es=0x23 gs=0x00 efl=0x00000246
dr0=0x00d99768 dr1=0x00d9977e dr2=0x00000000
dr3=0x00000000 dr6=0xffff0ff0 dr7=0x00700505Notice the dr0 value is 0x00d99768 and dr1 is 0x00d9977e - both of the addresses shown in the OllyDbg screenshot.The breakpoints described in this section are thread-specific, since each thread has its own set of registers. However, there is another set of processor-specific (thus affecting all threads running on the processor...or affinity) registers where kernel breakpoints can be set. This is how the malware in Rachit Mathur's Memory forging attempt by a rootkit blog worked, and I will discuss how to detect that in a separate post later on.
The AttachedProcess Tag
This tag is associated with threads that are "attached" to another process's address space. Through the use of APIs such as KeAttachProcess and KeStackAttachProcess, kernel drivers can easily read/write data in any process's virtual memory. Although there are legitimate reasons why a thread may be executing in the context of a process other than the process that owns the thread, there are also malicious reasons. For example, the following screen shot shows a rootkit using KeAttachProcess so it can write a DLL into services.exe from kernel mode. Then it queues an APC to start executing at the DLL's entry point.
Before moving on, note that the rootkit calls KeDetachProcess when its finished writing to the memory of services.exe. For detection, you must acquire memory after a thread calls KeAttachProcess, but before it calls KeDetachProcess. Since all this can go down pretty quickly, there's a slim chance of catching this particular thread "in the act." Another well known malware family, TDL4 uses a very similar technique (see section 2.3 Injecting payload into processes). However, not all rootkits work the same way. Other malware may attach to a process, scan that process's memory for credit card numbers every 30 seconds, and only detach when the process terminates or the system shuts down. In that case, you'll catch it every time!$ python vol.py -f XPSP3.vmem threads -F AttachedProcessNotice there are actually 3 tags for this thread. It's a system thread, its currently attached to services.exe although the "System" process owns it, and the SSDT has hooked functions. The thread's start address points to a driver named windev-5e93-fd3.sys which is also the module which owns the SSDT hooks, meaning the rootkit's own threads are using the modified SSDT.
Volatile Systems Volatility Framework 1.4_rc1
------
ETHREAD: 0x81eda7a0 Pid: 4 Tid: 484
Tags: SystemThread,AttachedProcess,HookedSSDT
Created: 2011-04-18 16:03:38
Exited: -
Owning Process: 0x823c8830 System
Attached Process: 0x81e3c458 services.exe
State: Running
BasePriority: THREAD_PRIORITY_NORMAL
TEB: 0x00000000
StartAddress: 0xb1805f1a windev-5e93-fd3.sys
ServiceTable: 0x80553020
[0] 0x80501bbc
[0x47] NtEnumerateKey 0xb1805944 windev-5e93-fd3.sys
[0x49] NtEnumerateValueKey 0xb1805aca windev-5e93-fd3.sys
[0x91] NtQueryDirectoryFile 0xb18055ee windev-5e93-fd3.sys
[1] -
[2] -
[3] -
Win32Thread: 0x00000000
CrossThreadFlags: PS_CROSS_THREAD_FLAGS_SYSTEM
b1805f1a: 8bff MOV EDI, EDI
b1805f1c: 55 PUSH EBP
b1805f1d: 8bec MOV EBP, ESP
b1805f1f: 51 PUSH ECX
b1805f20: 51 PUSH ECX
Adding new heuristics
Adding new heuristics to the threads plugin is easy. All you do is enter your criteria in the checks dictionary, with your desired tag as the dictionary key and some function or equation (that evaluates to True/False) as the dictionary's value. Here are a few examples:
checks['MyNewTag'] = thread.ThisField == 0xdeadbeef
checks['MyOtherTag'] = do_something(thread)
Once you've done that, just filter for your new tag:
$ python vol.py -f memory.dmp threads -F MyNewTag
Using Volshell on ETHREAD and KTHREAD
If you need to explore thread-related data structures a little closer, don't forget about volshell. This let's you investigate values that aren't printed by existing plugins - without having to write your own plugin. Just break into a shell and use dt() as shown below.
$ python vol.py -f ds_fuzz_hidden_proc.img volshell
Volatile Systems Volatility Framework 1.4_rc1
Current context: process System, pid=4, ppid=0 DTB=0x319000
Welcome to volshell!
To get help, type 'hh()'
>>> dt("_ETHREAD", 0x8180b798)
[CType _ETHREAD] @ 0x8180B798
0x0 : Tcb 2172696472
0x1c0 : CreateTime 2008-11-26 07:38:25
0x1c8 : ExitTime 2008-11-26 07:40:25
0x1c8 : KeyedWaitChain 2172696928
0x1c8 : LpcReplyChain 2172696928
0x1d0 : ExitStatus 1
0x1d0 : OfsChain 1
......
>>> dt("_KTHREAD", 0x8180b798)
[CType _KTHREAD] @ 0x8180B798
0x0 : Header 2172696472
0x10 : MutantListHead 2172696488
0x18 : InitialStack 0
0x1c : StackLimit 4162211840
0x20 : Teb 0
0x24 : TlsArray 0
0x28 : KernelStack 4162223128
0x2c : DebugActive 0
0x2d : State 4
.......ConclusionAt the end of the day, we have a new plugin that combines the functionality of two older plugins, plus introduces several new heuristics that can help analyze threads in physical memory dumps. Not all heuristics indicate something malicious, and not all heuristics are fully safe from carefully planned DKOM attacks. Furthermore, this plugin's output requires a greater understanding of Windows internals than other plugins do (for example, it's not as self-explanatory as "connections" which prints IPs and ports). However, used in the correct manner, this plugin can be extremely useful for your malware related investigations.
0 comments:
Post a Comment