-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Command Reference Mal
Table of Contents
- malfind
- yarascan
- svcscan
- ldrmodules
- impscan
- apihooks
- idt
- gdt
- threads
- callbacks
- driverirp
- devicetree
- psxview
- timers
Although all Volatility commands can help you hunt malware in one way or another, there are a few designed specifically for hunting rootkits and malicious code. The most comprehensive documentation for these commands can be found in the Malware Analyst's Cookbook and DVD: Tools and Techniques For Fighting Malicious Code.
The malfind command helps find hidden or injected code/DLLs in user mode memory, based on characteristics such as VAD tag and page permissions.
Note: malfind does not detect DLLs injected into a process using CreateRemoteThread->LoadLibrary. DLLs injected with this technique are not hidden and thus you can view them with dlllist. The purpose of malfind is to locate DLLs that standard methods/tools do not see. For more information see Issue #178.
Here is an example of using it to detect the presence of Zeus. The first memory segment (starting at 0x01600000) was detected because its executable, marked as private (not shared between processes) and has a VadS tag...which means there is no memory mapped file already occupying the space. Based on a disassembly of the data found at this address, it seems to contain some API hook trampoline stubs.
The second memory segment (starting at 0x015D0000) was detected because it contained an executable that isn't listed in the PEB's module lists.
If you want to save extracted copies of the memory segments identified by malfind, just supply an output directory with -D or --dump-dir=DIR. In this case, an unpacked copy of the Zeus binary that was injected into explorer.exe would be written to disk.
$ python vol.py -f zeus.vmem malfind -p 1724
Volatility Foundation Volatility Framework 2.4
Process: explorer.exe Pid: 1724 Address: 0x1600000
Vad Tag: VadS Protection: PAGE_EXECUTE_READWRITE
Flags: CommitCharge: 1, MemCommit: 1, PrivateMemory: 1, Protection: 6
0x01600000 b8 35 00 00 00 e9 cd d7 30 7b b8 91 00 00 00 e9 .5......0{......
0x01600010 4f df 30 7b 8b ff 55 8b ec e9 ef 17 c1 75 8b ff O.0{..U......u..
0x01600020 55 8b ec e9 95 76 bc 75 8b ff 55 8b ec e9 be 53 U....v.u..U....S
0x01600030 bd 75 8b ff 55 8b ec e9 d6 18 c1 75 8b ff 55 8b .u..U......u..U.
0x1600000 b835000000 MOV EAX, 0x35
0x1600005 e9cdd7307b JMP 0x7c90d7d7
0x160000a b891000000 MOV EAX, 0x91
0x160000f e94fdf307b JMP 0x7c90df63
0x1600014 8bff MOV EDI, EDI
0x1600016 55 PUSH EBP
Process: explorer.exe Pid: 1724 Address: 0x15d0000
Vad Tag: VadS Protection: PAGE_EXECUTE_READWRITE
Flags: CommitCharge: 38, MemCommit: 1, PrivateMemory: 1, Protection: 6
0x015d0000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 MZ..............
0x015d0010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
0x015d0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x015d0030 00 00 00 00 00 00 00 00 00 00 00 00 d0 00 00 00 ................
0x15d0000 4d DEC EBP
0x15d0001 5a POP EDX
0x15d0002 90 NOP
0x15d0003 0003 ADD [EBX], AL
0x15d0005 0000 ADD [EAX], AL
0x15d0007 000400 ADD [EAX+EAX], AL
0x15d000a 0000 ADD [EAX], AL
Volatility has several built-in scanning engines to help you find simple patterns like pool tags in physical or virtual address spaces. However, if you need to scan for more complex things like regular expressions or compound rules (i.e. search for "this" and not "that"), you can use the yarascan command. This plugin can help you locate any sequence of bytes (like assembly instructions with wild cards), regular expressions, ANSI strings, or Unicode strings in user mode or kernel memory.
You can create a YARA rules file and specify it as --yara-file=RULESFILE. Or, if you're just looking for something simple, and only plan to do the search a few times, then you can specify the criteria like --yara-rules=RULESTEXT.
To search for signatures defined in the file rules.yar, in any process, and simply display the results on screen:
$ python vol.py -f zeus.vmem yarascan --yara-file=/path/to/rules.yar
To search for a simple string in any process and dump the memory segments containing a match:
$ python vol.py -f zeus.vmem yarascan -D dump_files --yara-rules="simpleStringToFind"
To search for a byte pattern in kernel memory, use the following technique. This searches through memory in 1MB chunks, in all sessions. The TDL3 malware applies a hard-patch to SCSI adaptors on disk (sometimes atapi.sys or vmscsi.sys). In particular, it adds some shell code to the .rsrc section of the file, and then modifies the AddressOfEntryPoint so that it points at the shell code. This is TDL3's main persistence method. One of the unique instructions in the shell code is cmp dword ptr ['3LDT']
so I made a YARA signature from those opcodes:
$ python vol.py -f tdl3.vmem yarascan --yara-rules="{8B 00 81 38 54 44 4C 33 75 5A}" -K
Volatility Foundation Volatility Framework 2.4
Rule: r1
Owner: (Unknown Kernel Memory)
0x8138dcc0 8b 00 81 38 54 44 4c 33 75 5a 8b 45 f4 05 fd 29 ...8TDL3uZ.E...)
0x8138dcd0 b7 f0 50 b8 08 03 00 00 8b 80 00 00 df ff ff b0 ..P.............
0x8138dce0 00 01 00 00 b8 08 03 00 00 8b 80 00 00 df ff 8b ................
0x8138dcf0 40 04 8b 4d ec 03 41 20 ff d0 ff 75 e0 b8 08 03 @..M..A....u....
Rule: r1
Owner: dump_vmscsi.sys
0xf94bb4c3 8b 00 81 38 54 44 4c 33 75 5a 8b 45 f4 05 fd 29 ...8TDL3uZ.E...)
0xf94bb4d3 b7 f0 50 b8 08 03 00 00 8b 80 00 00 df ff ff b0 ..P.............
0xf94bb4e3 00 01 00 00 b8 08 03 00 00 8b 80 00 00 df ff 8b ................
0xf94bb4f3 40 04 8b 4d ec 03 41 20 ff d0 ff 75 e0 b8 08 03 @..M..A....u....
Rule: r1
Owner: vmscsi.sys
0xf9dba4c3 8b 00 81 38 54 44 4c 33 75 5a 8b 45 f4 05 fd 29 ...8TDL3uZ.E...)
0xf9dba4d3 b7 f0 50 b8 08 03 00 00 8b 80 00 00 df ff ff b0 ..P.............
0xf9dba4e3 00 01 00 00 b8 08 03 00 00 8b 80 00 00 df ff 8b ................
0xf9dba4f3 40 04 8b 4d ec 03 41 20 ff d0 ff 75 e0 b8 08 03 @..M..A....u....
Search for a given byte pattern in a particular process:
$ python vol.py -f zeus.vmem yarascan --yara-rules="{eb 90 ff e4 88 32 0d}" --pid=624
Search for a regular expression in a particular process:
$ python vol.py -f zeus.vmem yarascan --yara-rules="/my(regular|expression{0,2})/" --pid=624
Volatility is the only memory forensics framework with the ability to list services without using the Windows API on a live machine. To see which services are registered on your memory image, use the svcscan command. The output shows the process ID of each service (if its active and pertains to a usermode process), the service name, service display name, service type, and current status. It also shows the binary path for the registered service - which will be an EXE for usermode services and a driver name for services that run from kernel mode.
$ python vol.py -f ~/Desktop/win7_trial_64bit.raw --profile=Win7SP0x64 svcscan
Volatility Foundation Volatility Framework 2.4
Offset: 0xa26e70
Order: 71
Process ID: 1104
Service Name: DPS
Display Name: Diagnostic Policy Service
Service Type: SERVICE_WIN32_SHARE_PROCESS
Service State: SERVICE_RUNNING
Binary Path: C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork
Offset: 0xa25620
Order: 70
Process ID: -
Service Name: dot3svc
Display Name: Wired AutoConfig
Service Type: SERVICE_WIN32_SHARE_PROCESS
Service State: SERVICE_STOPPED
Binary Path: -
Offset: 0xa25440
Order: 68
Process ID: -
Service Name: Disk
Display Name: Disk Driver
Service Type: SERVICE_KERNEL_DRIVER
Service State: SERVICE_RUNNING
Binary Path: \Driver\Disk
A new option (--verbose) is available starting with Volatility 2.3. This option checks the ServiceDll registry key and reports which DLL is hosting the service. This is a critical capability since malware very commonly installs services using svchost.exe (the shared host service process) and implements the actual malicious code in a DLL.
$ python vol.py -f win7_trial_64bit.raw svcscan --verbose --profile=Win7SP0x64
Volatility Foundation Volatility Framework 2.4
Offset: 0xa26e70
Order: 71
Process ID: 1104
Service Name: DPS
Display Name: Diagnostic Policy Service
Service Type: SERVICE_WIN32_SHARE_PROCESS
Service State: SERVICE_RUNNING
Binary Path: C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork
ServiceDll: %SystemRoot%\system32\dps.dll <----- This is the component you recover from disk
There are many ways to hide a DLL. One of the ways involves unlinking the DLL from one (or all) of the linked lists in the PEB. However, when this is done, there is still information contained within the VAD (Virtual Address Descriptor) which identifies the base address of the DLL and its full path on disk. To cross-reference this information (known as memory mapped files) with the 3 PEB lists, use the ldrmodules command.
For each memory mapped PE file, the ldrmodules command prints True or False if the PE exists in the PEB lists.
$ python vol.py -f ~/Desktop/win7_trial_64bit.raw --profile=Win7SP0x64 ldrmodules
Volatility Foundation Volatility Framework 2.4
Pid Process Base InLoad InInit InMem MappedPath
-------- -------------------- ------------------ ------ ------ ----- ----------
208 smss.exe 0x0000000047a90000 True False True \Windows\System32\smss.exe
296 csrss.exe 0x0000000049700000 True False True \Windows\System32\csrss.exe
344 csrss.exe 0x0000000000390000 False False False \Windows\Fonts\vgasys.fon
344 csrss.exe 0x00000000007a0000 False False False \Windows\Fonts\vgaoem.fon
344 csrss.exe 0x00000000020e0000 False False False \Windows\Fonts\ega40woa.fon
344 csrss.exe 0x0000000000a60000 False False False \Windows\Fonts\dosapp.fon
344 csrss.exe 0x0000000000a70000 False False False \Windows\Fonts\cga40woa.fon
344 csrss.exe 0x00000000020d0000 False False False \Windows\Fonts\cga80woa.fon
428 services.exe 0x0000000000020000 False False False \Windows\System32\en-US\services.exe.mui
428 services.exe 0x00000000ff670000 True False True \Windows\System32\services.exe
444 lsass.exe 0x0000000000180000 False False False \Windows\System32\en-US\crypt32.dll.mui
444 lsass.exe 0x0000000076b20000 True True True \Windows\System32\kernel32.dll
444 lsass.exe 0x0000000076c40000 True True True \Windows\System32\user32.dll
444 lsass.exe 0x0000000074a70000 True True True \Windows\System32\msprivs.dll
444 lsass.exe 0x0000000076d40000 True True True \Windows\System32\ntdll.dll
568 svchost.exe 0x00000000001e0000 False False False \Windows\System32\en-US\umpnpmgr.dll.mui
Since the PEB and the DLL lists that it contains all exist in user mode, its also possible for malware to hide (or obscure) a DLL by simply overwriting the path. Tools that only look for unlinked entries may miss the fact that malware could overwrite C:\bad.dll to show C:\windows\system32\kernel32.dll. So you can also pass -v or --verbose to ldrmodules to see the full path of all entries.
For concrete examples, see http://blogs.mcafee.com/mcafee-labs/zeroaccess-misleads-memory-file-link ZeroAccess Misleads Memory-File Link and QuickPost: Flame & Volatility.
$ python vol.py -f ~/Desktop/win7_trial_64bit.raw --profile=Win7SP0x64 ldrmodules -v
Volatility Foundation Volatility Framework 2.4
Pid Process Base InLoad InInit InMem MappedPath
-------- -------------------- ------------------ ------ ------ ----- ----------
208 smss.exe 0x0000000047a90000 True False True \Windows\System32\smss.exe
Load Path: \SystemRoot\System32\smss.exe : smss.exe
Mem Path: \SystemRoot\System32\smss.exe : smss.exe
296 csrss.exe 0x0000000049700000 True False True \Windows\System32\csrss.exe
Load Path: C:\Windows\system32\csrss.exe : csrss.exe
Mem Path: C:\Windows\system32\csrss.exe : csrss.exe
344 csrss.exe 0x0000000000390000 False False False \Windows\Fonts\vgasys.fon
344 csrss.exe 0x00000000007a0000 False False False \Windows\Fonts\vgaoem.fon
344 csrss.exe 0x00000000020e0000 False False False \Windows\Fonts\ega40woa.fon
344 csrss.exe 0x0000000000a60000 False False False \Windows\Fonts\dosapp.fon
344 csrss.exe 0x0000000000a70000 False False False \Windows\Fonts\cga40woa.fon
344 csrss.exe 0x00000000020d0000 False False False \Windows\Fonts\cga80woa.fon
428 services.exe 0x0000000000020000 False False False \Windows\System32\en-US\services.exe.mui
428 services.exe 0x00000000ff670000 True False True \Windows\System32\services.exe
Load Path: C:\Windows\system32\services.exe : services.exe
Mem Path: C:\Windows\system32\services.exe : services.exe
444 lsass.exe 0x0000000000180000 False False False \Windows\System32\en-US\crypt32.dll.mui
444 lsass.exe 0x0000000076b20000 True True True \Windows\System32\kernel32.dll
Load Path: C:\Windows\system32\kernel32.dll : kernel32.dll
Init Path: C:\Windows\system32\kernel32.dll : kernel32.dll
Mem Path: C:\Windows\system32\kernel32.dll : kernel32.dll
[snip]
In order to fully reverse engineer code that you find in memory dumps, its necessary to see which functions the code imports. In other words, which API functions it calls. When you dump binaries with dlldump, moddump, or procdump, the IAT (Import Address Table) may not properly be reconstructed due to the high likelihood that one or more pages in the PE header or IAT are not memory resident (paged). Thus, we created impscan. Impscan identifies calls to APIs without parsing a PE's IAT. It even works if malware completely erases the PE header, and it works on kernel drivers.
Previous versions of impscan automatically created a labeled IDB for use with IDA Pro. This functionality has temporarily been disabled, but will return sometime in the future when other similar functionality is introduced.
Take Coreflood for example. This malware deleted its PE header once it loaded in the target process (by calling VirtualFree on the injected DLL's ImageBase). You can use malfind to detect the presence of Coreflood based on the typical criteria (page permissions, VAD tags, etc). Notice how the PE's base address doesn't contain the usual 'MZ' header:
$ python vol.py -f coreflood.vmem -p 2044 malfind
Volatility Foundation Volatility Framework 2.4
Process: IEXPLORE.EXE Pid: 2044 Address: 0x7ff80000
Vad Tag: VadS Protection: PAGE_EXECUTE_READWRITE
Flags: CommitCharge: 45, PrivateMemory: 1, Protection: 6
0x7ff80000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7ff80010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7ff80020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7ff80030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7ff80000 0000 ADD [EAX], AL
0x7ff80002 0000 ADD [EAX], AL
0x7ff80004 0000 ADD [EAX], AL
0x7ff80006 0000 ADD [EAX], AL
Let's assume you want to extract the unpacked copy of Coreflood and see its imported APIs. Use impscan by specifying the base address provided to you by malfind. In this case, we fixup the base address by 0x1000 to account for the missing page at the real ImageBase.
$ python vol.py -f coreflood.vmem -p 2044 impscan -b 0x7ff81000
Volatility Foundation Volatility Framework 2.4
IAT Call Module Function
---------- ---------- -------------------- --------
0x7ff9e000 0x77dd77b3 ADVAPI32.dll SetSecurityDescriptorDacl
0x7ff9e004 0x77dfd4c9 ADVAPI32.dll GetUserNameA
0x7ff9e008 0x77dd6bf0 ADVAPI32.dll RegCloseKey
0x7ff9e00c 0x77ddeaf4 ADVAPI32.dll RegCreateKeyExA
0x7ff9e010 0x77dfc123 ADVAPI32.dll RegDeleteKeyA
0x7ff9e014 0x77ddede5 ADVAPI32.dll RegDeleteValueA
0x7ff9e018 0x77ddd966 ADVAPI32.dll RegNotifyChangeKeyValue
0x7ff9e01c 0x77dd761b ADVAPI32.dll RegOpenKeyExA
0x7ff9e020 0x77dd7883 ADVAPI32.dll RegQueryValueExA
0x7ff9e024 0x77ddebe7 ADVAPI32.dll RegSetValueExA
0x7ff9e028 0x77dfc534 ADVAPI32.dll AdjustTokenPrivileges
0x7ff9e02c 0x77e34c3f ADVAPI32.dll InitiateSystemShutdownA
0x7ff9e030 0x77dfd11b ADVAPI32.dll LookupPrivilegeValueA
0x7ff9e034 0x77dd7753 ADVAPI32.dll OpenProcessToken
0x7ff9e038 0x77dfc8c1 ADVAPI32.dll RegEnumKeyExA
[snip]
If you don't specify a base address with -b or --base, then you'll end up scanning the process's main module (i.e. IEXPLORE.EXE since that's -p 2044) for imported functions. You can also specify the base address of a kernel driver to scan the driver for imported kernel-mode functions.
Laqma loads a kernel driver named lanmandrv.sys. If you extract it with moddump, the IAT will be corrupt. So use impscan to rebuild it:
$ python vol.py -f laqma.vmem impscan -b 0xfca29000
Volatility Foundation Volatility Framework 2.4
IAT Call Module Function
---------- ---------- -------------------- --------
0xfca2a080 0x804ede90 ntoskrnl.exe IofCompleteRequest
0xfca2a084 0x804f058c ntoskrnl.exe IoDeleteDevice
0xfca2a088 0x80568140 ntoskrnl.exe IoDeleteSymbolicLink
0xfca2a08c 0x80567dcc ntoskrnl.exe IoCreateSymbolicLink
0xfca2a090 0x805a2130 ntoskrnl.exe MmGetSystemRoutineAddress
0xfca2a094 0x805699e0 ntoskrnl.exe IoCreateDevice
0xfca2a098 0x80544080 ntoskrnl.exe ExAllocatePoolWithTag
0xfca2a09c 0x80536dc3 ntoskrnl.exe wcscmp
0xfca2a0a0 0x804fdbc0 ntoskrnl.exe ZwOpenKey
0xfca2a0a4 0x80535010 ntoskrnl.exe _except_handler3
0xfca2a3ac 0x8056df44 ntoskrnl.exe NtQueryDirectoryFile
0xfca2a3b4 0x8060633e ntoskrnl.exe NtQuerySystemInformation
0xfca2a3bc 0x805bfb78 ntoskrnl.exe NtOpenProcess
The next example shows impscan on an x64 driver and using the render_idc output format. This gives you an IDC file you can import into IDA Pro to apply labels to the function calls.
$ python vol.py -f ~/Desktop/win7_trial_64bit.raw --profile=Win7SP0x64 impscan -b 0xfffff88003980000 --output=idc --output-file=imps.idc
Volatility Foundation Volatility Framework 2.4
$ cat imps.idc
#include <idc.idc>
static main(void) {
MakeDword(0xFFFFF8800398A000);
MakeName(0xFFFFF8800398A000, "KeSetEvent");
MakeDword(0xFFFFF8800398A008);
MakeName(0xFFFFF8800398A008, "PsTerminateSystemThread");
MakeDword(0xFFFFF8800398A010);
MakeName(0xFFFFF8800398A010, "KeInitializeEvent");
MakeDword(0xFFFFF8800398A018);
MakeName(0xFFFFF8800398A018, "PsCreateSystemThread");
MakeDword(0xFFFFF8800398A020);
MakeName(0xFFFFF8800398A020, "KeWaitForSingleObject");
MakeDword(0xFFFFF8800398A028);
MakeName(0xFFFFF8800398A028, "ZwClose");
MakeDword(0xFFFFF8800398A030);
MakeName(0xFFFFF8800398A030, "RtlInitUnicodeString");
[snip]
MakeDword(0xFFFFF8800398A220);
MakeName(0xFFFFF8800398A220, "RtlAnsiCharToUnicodeChar");
MakeDword(0xFFFFF8800398A228);
MakeName(0xFFFFF8800398A228, "__C_specific_handler");
Exit(0);}
To find API hooks in user mode or kernel mode, use the apihooks plugin. This finds IAT, EAT, Inline style hooks, and several special types of hooks. For Inline hooks, it detects CALLs and JMPs to direct and indirect locations, and it detects PUSH/RET instruction sequences. It also detects CALLs or JMPs to registers after an immediate value (address) is moved into the register. The special types of hooks that it detects include syscall hooking in ntdll.dll and calls to unknown code pages in kernel memory.
As of Volatility 2.1, apihooks also detects hooked winsock procedure tables, includes an easier to read output format, supports multiple hop disassembly, and can optionally scan quicker through memory by ignoring non-critical processes and DLLs.
Here is an example of detecting IAT hooks installed by Coreflood. The hooking module is unknown because there is no module (DLL) associated with the memory in which the rootkit code exists. If you want to extract the code containing the hooks, you have a few options:
-
See if malfind can automatically find and extract it.
-
Use volshell dd/db commands to scan backwards and look for an MZ header. Then pass that address to dlldump as the --base value.
-
Use vaddump to extract all code segments to individual files (named according to start and end address), then find the file that contains the 0x7ff82 ranges.
$ python vol.py -f coreflood.vmem -p 2044 apihooks
Volatility Foundation Volatility Framework 2.4
************************************************************************
Hook mode: Usermode
Hook type: Import Address Table (IAT)
Process: 2044 (IEXPLORE.EXE)
Victim module: iexplore.exe (0x400000 - 0x419000)
Function: kernel32.dllGetProcAddress at 0x7ff82360
Hook address: 0x7ff82360
Hooking module: <unknown>
Disassembly(0):
0x7ff82360 e8fbf5ffff CALL 0x7ff81960
0x7ff82365 84c0 TEST AL, AL
0x7ff82367 740b JZ 0x7ff82374
0x7ff82369 8b150054fa7f MOV EDX, [0x7ffa5400]
0x7ff8236f 8b4250 MOV EAX, [EDX+0x50]
0x7ff82372 ffe0 JMP EAX
0x7ff82374 8b4c2408 MOV ECX, [ESP+0x8]
************************************************************************
Hook mode: Usermode
Hook type: Import Address Table (IAT)
Process: 2044 (IEXPLORE.EXE)
Victim module: iexplore.exe (0x400000 - 0x419000)
Function: kernel32.dllLoadLibraryA at 0x7ff82a50
Hook address: 0x7ff82a50
Hooking module: <unknown>
Disassembly(0):
0x7ff82a50 51 PUSH ECX
0x7ff82a51 e80aefffff CALL 0x7ff81960
0x7ff82a56 84c0 TEST AL, AL
0x7ff82a58 7414 JZ 0x7ff82a6e
0x7ff82a5a 8b442408 MOV EAX, [ESP+0x8]
0x7ff82a5e 8b0d0054fa7f MOV ECX, [0x7ffa5400]
0x7ff82a64 8b512c MOV EDX, [ECX+0x2c]
0x7ff82a67 50 PUSH EAX
[snip]
Here is an example of detecting the Inline hooks installed by Silentbanker. Note the multiple hop disassembly which is new in 2.1. It shows the first hop of the hook at 0x7c81caa2 jumps to 0xe50000. Then you also see a disassembly of the code at 0xe50000 which executes the rest of the trampoline.
$ python vol.py -f silentbanker.vmem -p 1884 apihooks
Volatility Foundation Volatility Framework 2.4
************************************************************************
Hook mode: Usermode
Hook type: Inline/Trampoline
Process: 1884 (IEXPLORE.EXE)
Victim module: kernel32.dll (0x7c800000 - 0x7c8f4000)
Function: kernel32.dllExitProcess at 0x7c81caa2
Hook address: 0xe50000
Hooking module: <unknown>
Disassembly(0):
0x7c81caa2 e959356384 JMP 0xe50000
0x7c81caa7 6aff PUSH -0x1
0x7c81caa9 68b0f3e877 PUSH DWORD 0x77e8f3b0
0x7c81caae ff7508 PUSH DWORD [EBP+0x8]
0x7c81cab1 e846ffffff CALL 0x7c81c9fc
Disassembly(1):
0xe50000 58 POP EAX
0xe50001 680500e600 PUSH DWORD 0xe60005
0xe50006 6800000000 PUSH DWORD 0x0
0xe5000b 680000807c PUSH DWORD 0x7c800000
0xe50010 6828180310 PUSH DWORD 0x10031828
0xe50015 50 PUSH EAX
[snip]
Here is an example of detecting the PUSH/RET Inline hooks installed by Laqma:
$ python vol.py -f laqma.vmem -p 1624 apihooks
Volatility Foundation Volatility Framework 2.4
************************************************************************
Hook mode: Usermode
Hook type: Inline/Trampoline
Process: 1624 (explorer.exe)
Victim module: USER32.dll (0x7e410000 - 0x7e4a0000)
Function: USER32.dllMessageBoxA at 0x7e45058a
Hook address: 0xac10aa
Hooking module: Dll.dll
Disassembly(0):
0x7e45058a 68aa10ac00 PUSH DWORD 0xac10aa
0x7e45058f c3 RET
0x7e450590 3dbc04477e CMP EAX, 0x7e4704bc
0x7e450595 00742464 ADD [ESP+0x64], DH
0x7e450599 a118000000 MOV EAX, [0x18]
0x7e45059e 6a00 PUSH 0x0
0x7e4505a0 ff DB 0xff
0x7e4505a1 70 DB 0x70
Disassembly(1):
0xac10aa 53 PUSH EBX
0xac10ab 56 PUSH ESI
0xac10ac 57 PUSH EDI
0xac10ad 90 NOP
0xac10ae 90 NOP
[snip]
Here is an example of the duqu style API hooks which moves an immediate value into a register and then JMPs to it.
************************************************************************
Hook mode: Usermode
Hook type: Inline/Trampoline
Process: 1176 (lsass.exe)
Victim module: ntdll.dll (0x7c900000 - 0x7c9af000)
Function: ntdll.dllZwQuerySection at 0x7c90d8b0
Hook address: 0x980a02
Hooking module: <unknown>
Disassembly(0):
0x7c90d8b0 b8020a9800 MOV EAX, 0x980a02
0x7c90d8b5 ffe0 JMP EAX
0x7c90d8b7 03fe ADD EDI, ESI
0x7c90d8b9 7fff JG 0x7c90d8ba
0x7c90d8bb 12c2 ADC AL, DL
0x7c90d8bd 1400 ADC AL, 0x0
0x7c90d8bf 90 NOP
0x7c90d8c0 b8a8000000 MOV EAX, 0xa8
0x7c90d8c5 ba DB 0xba
0x7c90d8c6 0003 ADD [EBX], AL
Disassembly(1):
0x980a02 55 PUSH EBP
0x980a03 8bec MOV EBP, ESP
0x980a05 51 PUSH ECX
0x980a06 51 PUSH ECX
0x980a07 e8f1fdffff CALL 0x9807fd
0x980a0c 8945fc MOV [EBP-0x4], EAX
0x980a0f e872feffff CALL 0x980886
0x980a14 8945f8 MOV [EBP-0x8], EAX
0x980a17 83 DB 0x83
0x980a18 7df8 JGE 0x980a12
Here is an example of using apihooks to detect the syscall patches in ntdll.dll (using a Carberp sample):
$ python vol.py -f carberp.vmem -p 1004 apihooks
Volatility Foundation Volatility Framework 2.4
************************************************************************
Hook mode: Usermode
Hook type: NT Syscall
Process: 1004 (explorer.exe)
Victim module: ntdll.dll (0x7c900000 - 0x7c9af000)
Function: NtQueryDirectoryFile
Hook address: 0x1da658f
Hooking module: <unknown>
Disassembly(0):
0x7c90d750 b891000000 MOV EAX, 0x91
0x7c90d755 ba84ddda01 MOV EDX, 0x1dadd84
0x7c90d75a ff12 CALL DWORD [EDX]
0x7c90d75c c22c00 RET 0x2c
0x7c90d75f 90 NOP
0x7c90d760 b892000000 MOV EAX, 0x92
0x7c90d765 ba DB 0xba
0x7c90d766 0003 ADD [EBX], AL
Disassembly(1):
0x1da658f 58 POP EAX
0x1da6590 8d056663da01 LEA EAX, [0x1da6366]
0x1da6596 ffe0 JMP EAX
0x1da6598 c3 RET
0x1da6599 55 PUSH EBP
0x1da659a 8bec MOV EBP, ESP
0x1da659c 51 PUSH ECX
0x1da659d 8365fc00 AND DWORD [EBP+0xfffffffc], 0x0
0x1da65a1 688f88d69b PUSH DWORD 0x9bd6888f
[snip]
Here is an example of using apihooks to detect the Inline hook of a kernel mode function:
$ python vol.py apihooks -f rustock.vmem
************************************************************************
Hook mode: Kernelmode
Hook type: Inline/Trampoline
Victim module: ntoskrnl.exe (0x804d7000 - 0x806cf980)
Function: ntoskrnl.exeIofCallDriver at 0x804ee130
Hook address: 0xb17a189d
Hooking module: <unknown>
Disassembly(0):
0x804ee130 ff2580c25480 JMP DWORD [0x8054c280]
0x804ee136 cc INT 3
0x804ee137 cc INT 3
0x804ee138 cc INT 3
0x804ee139 cc INT 3
0x804ee13a cc INT 3
0x804ee13b cc INT 3
0x804ee13c 8bff MOV EDI, EDI
0x804ee13e 55 PUSH EBP
0x804ee13f 8bec MOV EBP, ESP
0x804ee141 8b4d08 MOV ECX, [EBP+0x8]
0x804ee144 83f929 CMP ECX, 0x29
0x804ee147 72 DB 0x72
Disassembly(1):
0xb17a189d 56 PUSH ESI
0xb17a189e 57 PUSH EDI
0xb17a189f 8bf9 MOV EDI, ECX
0xb17a18a1 8b7708 MOV ESI, [EDI+0x8]
0xb17a18a4 3b35ab6d7ab1 CMP ESI, [0xb17a6dab]
0xb17a18aa 7509 JNZ 0xb17a18b5
0xb17a18ac 52 PUSH EDX
0xb17a18ad 57 PUSH EDI
0xb17a18ae e8c6430000 CALL 0xb17a5c79
0xb17a18b3 eb6a JMP 0xb17a191f
Here is an example of using apihooks to detect the calls to an unknown code page from a kernel driver. In this case, malware has patched tcpip.sys with some malicious redirections.
$ python vol.py -f rustock-c.vmem apihooks
Volatility Foundation Volatility Framework 2.4
************************************************************************
Hook mode: Kernelmode
Hook type: Unknown Code Page Call
Victim module: tcpip.sys (0xf7bac000 - 0xf7c04000)
Function: <unknown>
Hook address: 0x81ecd0c0
Hooking module: <unknown>
Disassembly(0):
0xf7be2514 ff15bcd0ec81 CALL DWORD [0x81ecd0bc]
0xf7be251a 817dfc03010000 CMP DWORD [EBP+0xfffffffc], 0x103
0xf7be2521 7506 JNZ 0xf7be2529
0xf7be2523 57 PUSH EDI
0xf7be2524 e8de860000 CALL 0xf7beac07
0xf7be2529 83 DB 0x83
0xf7be252a 66 DB 0x66
0xf7be252b 10 DB 0x10
Disassembly(1):
0x81ecd0c0 0e PUSH CS
0x81ecd0c1 90 NOP
0x81ecd0c2 83ec04 SUB ESP, 0x4
0x81ecd0c5 c704246119c481 MOV DWORD [ESP], 0x81c41961
0x81ecd0cc cb RETF
[snip]
To print the system's IDT (Interrupt Descriptor Table), use the idt command. If there are multiple processors on the system, the IDT for each individual CPU is displayed. You'll see the CPU number, the GDT selector, the current address and owning module, and the name of the PE section in which the IDT function resides. If you supply the --verbose parameter, a disassembly of the IDT function will be shown.
Some rootkits hook the IDT entry for KiSystemService, but point it at a routine inside the NT module (where KiSystemService should point). However, at that address, there is an Inline hook. The following output shows an example of how Volatility can point this out for you. Notice how the 0x2E entry for KiSystemService is in the .rsrc section of ntoskrnl.exe instead of .text like all others.
$ python vol.py -f rustock.vmem idt
Volatility Foundation Volatility Framework 2.4
CPU Index Selector Value Module Section
------ ------ -------- ---------- -------------------- ------------
0 0 8 0x8053e1cc ntoskrnl.exe .text
0 1 8 0x8053e344 ntoskrnl.exe .text
0 2 88 0x00000000 ntoskrnl.exe
0 3 8 0x8053e714 ntoskrnl.exe .text
0 4 8 0x8053e894 ntoskrnl.exe .text
0 5 8 0x8053e9f0 ntoskrnl.exe .text
0 6 8 0x8053eb64 ntoskrnl.exe .text
0 7 8 0x8053f1cc ntoskrnl.exe .text
0 8 80 0x00000000 ntoskrnl.exe
[snip]
0 2B 8 0x8053db10 ntoskrnl.exe .text
0 2C 8 0x8053dcb0 ntoskrnl.exe .text
0 2D 8 0x8053e5f0 ntoskrnl.exe .text
0 2E 8 0x806b01b8 ntoskrnl.exe .rsrc
[snip]
To get more details about the possible IDT modification, use --verbose:
$ python vol.py -f rustock.vmem idt --verbose
Volatility Foundation Volatility Framework 2.4
CPU Index Selector Value Module Section
------ ------ -------- ---------- -------------------- ------------
[snip]
0 2E 8 0x806b01b8 ntoskrnl.exe .rsrc
0x806b01b8 e95c2c0f31 JMP 0xb17a2e19
0x806b01bd e9832c0f31 JMP 0xb17a2e45
0x806b01c2 4e DEC ESI
0x806b01c3 44 INC ESP
0x806b01c4 4c DEC ESP
0x806b01c5 45 INC EBP
0x806b01c6 44 INC ESP
0x806b01c7 5f POP EDI
To print the system's GDT (Global Descriptor Table), use the gdt command. This is useful for detecting rootkits like Alipop that install a call gate so that user mode programs can call directly into kernel mode (using a CALL FAR instruction).
If your system has multiple CPUs, the GDT for each processor is shown.
In the output below, you can see that selector 0x3e0 has been infected and used for the purposes of a 32-bit call gate. The call gate address is 0x8003f000, which is where execution continues.
$ python vol.py -f alipop.vmem gdt
Volatility Foundation Volatility Framework 2.4
CPU Sel Base Limit Type DPL Gr Pr
------ ---------- ---------- ---------- -------------- ------ ---- ----
0 0x0 0x00ffdf0a 0xdbbb TSS16 Busy 2 By P
0 0x8 0x00000000 0xffffffff Code RE Ac 0 Pg P
0 0x10 0x00000000 0xffffffff Data RW Ac 0 Pg P
0 0x18 0x00000000 0xffffffff Code RE Ac 3 Pg P
0 0x20 0x00000000 0xffffffff Data RW Ac 3 Pg P
0 0x28 0x80042000 0x20ab TSS32 Busy 0 By P
0 0x30 0xffdff000 0x1fff Data RW Ac 0 Pg P
0 0x38 0x00000000 0xfff Data RW Ac 3 By P
0 0x40 0x00000400 0xffff Data RW 3 By P
0 0x48 0x00000000 0x0 <Reserved> 0 By Np
[snip]
0 0x3d0 0x00008003 0xf3d8 <Reserved> 0 By Np
0 0x3d8 0x00008003 0xf3e0 <Reserved> 0 By Np
0 0x3e0 0x8003f000 0x0 CallGate32 3 - P
0 0x3e8 0x00000000 0xffffffff Code RE Ac 0 Pg P
0 0x3f0 0x00008003 0xf3f8 <Reserved> 0 By Np
0 0x3f8 0x00000000 0x0 <Reserved> 0 By Np
If you want to further investigate the infection, you can break into a volshell as shown below. Then disassemble code at the call gate address.
$ python vol.py -f alipop.vmem volshell
Volatility Foundation Volatility Framework 2.4
Current context: process System, pid=4, ppid=0 DTB=0x320000
Welcome to volshell Current memory image is:
file:///Users/Michael/Desktop/alipop.vmem
To get help, type 'hh()'
>>> dis(0xffdf0adb, length=32)
0xffdf0adb c8000000 ENTER 0x0, 0x0
0xffdf0adf 31c0 XOR EAX, EAX
0xffdf0ae1 60 PUSHA
0xffdf0ae2 8b5508 MOV EDX, [EBP+0x8]
0xffdf0ae5 bb00704d80 MOV EBX, 0x804d7000
0xffdf0aea 8b4b3c MOV ECX, [EBX+0x3c]
0xffdf0aed 8b6c0b78 MOV EBP, [EBX+ECX+0x78]
>>> db(0xffdf0adb + 75, length = 512)
ffdf0b26 d9 03 1c 81 89 5c 24 1c 61 c9 c2 04 00 7e 00 80 ......$.a....~..
ffdf0b36 00 3b 0b df ff 5c 00 52 00 65 00 67 00 69 00 73 .;.....R.e.g.i.s
ffdf0b46 00 74 00 72 00 79 00 5c 00 4d 00 61 00 63 00 68 .t.r.y...M.a.c.h
ffdf0b56 00 69 00 6e 00 65 00 5c 00 53 00 4f 00 46 00 54 .i.n.e...S.O.F.T
ffdf0b66 00 57 00 41 00 52 00 45 00 5c 00 4d 00 69 00 63 .W.A.R.E...M.i.c
ffdf0b76 00 72 00 6f 00 73 00 6f 00 66 00 74 00 5c 00 57 .r.o.s.o.f.t...W
ffdf0b86 00 69 00 6e 00 64 00 6f 00 77 00 73 00 5c 00 43 .i.n.d.o.w.s...C
ffdf0b96 00 75 00 72 00 72 00 65 00 6e 00 74 00 56 00 65 .u.r.r.e.n.t.V.e
ffdf0ba6 00 72 00 73 00 69 00 6f 00 6e 00 5c 00 52 00 75 .r.s.i.o.n...R.u
ffdf0bb6 00 6e 00 00 00 06 00 08 00 c3 0b df ff 71 00 51 .n...........q.Q
ffdf0bc6 00 00 00 43 00 3a 00 5c 00 57 00 49 00 4e 00 44 ...C.:...W.I.N.D
ffdf0bd6 00 4f 00 57 00 53 00 5c 00 61 00 6c 00 69 00 2e .O.W.S...a.l.i..
ffdf0be6 00 65 00 78 00 65 00 00 00 26 00 28 00 f7 0b df .e.x.e...&.(....
ffdf0bf6 ff 5c 00 53 00 79 00 73 00 74 00 65 00 6d 00 52 ...S.y.s.t.e.m.R
ffdf0c06 00 6f 00 6f 00 74 00 5c 00 61 00 6c 00 69 00 2e .o.o.t...a.l.i..
ffdf0c16 00 65 00 78 00 65 00 00 00 00 00 e8 03 00 ec 00 .e.x.e..........
ffdf0c26 00 ff ff 00 00 00 9a cf 00 4d 5a 90 00 03 00 00 .........MZ.....
ffdf0c36 00 04 00 00 00 ff ff 00 00 b8 00 00 00 00 00 00 ................
ffdf0c46 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .@..............
ffdf0c56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
ffdf0c66 00 00 00 00 00 e0 00 00 00 0e 1f ba 0e 00 b4 09 ................
ffdf0c76 cd 21 b8 01 4c cd 21 54 68 69 73 20 70 72 6f 67 ...L.This prog
ffdf0c86 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 20 72 75 ram cannot be ru
ffdf0c96 6e 20 69 6e 20 44 4f 53 20 6d 6f 64 65 2e 0d 0d n in DOS mode...
The command gives you extensive details on threads, including the contents of each thread's registers (if available), a disassembly of code at the thread's start address, and various other fields that may be relevant to an investigation. Since any given system has hundreds of threads, making it difficult to sort through, this command associates descriptive tags to the threads it finds - and then you can filter by tag name with the -F or --filter parameter.
To see a list of available tags/filters, use -L like this:
$ python vol.py -f test.vmem threads -L
Volatility Foundation Volatility Framework 2.4
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 threads
If you don't specify any filters, then the command will output information on all threads. Otherwise, you can specify a single filter or multiple filters separated by commas. Here is an example of hunting for threads that are currently executing in the context of a process other than the process which owns the thread:
$ python vol.py -f XPSP3.vmem threads -F AttachedProcess
Volatility Foundation Volatility Framework 2.4
------
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
First, you see the virtual address of the ETHREAD object along with the process ID and thread ID. Next you see all tags associated with the thread (SystemThread, AttachedProcess, HookedSSDT), the creation/exit times, state, priority, start address, etc. It shows the SSDT base along with the address of each service table and any hooked functions in the tables. Finally, you see a disassembly of the thread's start address.
For a detailed description of each tag/filter and instructions on how to add your own heuristics to the threads command, see Investigating Windows Threads with Volatility.
Note: with the introduction of this command, two older commands (orphan_threads and ssdt_by_threads) have been deprecated.
Volatility is the only memory forensics platform with the ability to print an assortment of important notification routines and kernel callbacks. Rootkits, anti-virus suites, dynamic analysis tools (such as Sysinternals' Process Monitor and Tcpview), and many components of the Windows kernel use of these callbacks to monitor and/or react to events. We detect the following:
- PsSetCreateProcessNotifyRoutine (process creation).
- PsSetCreateThreadNotifyRoutine (thread creation).
- PsSetImageLoadNotifyRoutine (DLL/image load).
- IoRegisterFsRegistrationChange (file system registration).
- KeRegisterBugCheck and KeRegisterBugCheckReasonCallback.
- CmRegisterCallback (registry callbacks on XP).
- CmRegisterCallbackEx (registry callbacks on Vista and 7).
- IoRegisterShutdownNotification (shutdown callbacks).
- DbgSetDebugPrintCallback (debug print callbacks on Vista and 7).
- DbgkLkmdRegisterCallback (debug callbacks on 7).
Here's an example of detecting the thread creation callback installed by the BlackEnergy 2 malware. You can spot the malicious callback because the owner is 00004A2A - and BlackEnergy 2 uses a module name composed of eight hex characters.
$ python vol.py -f be2.vmem callbacks
Volatility Foundation Volatility Framework 2.4
Type Callback Owner
PsSetCreateThreadNotifyRoutine 0xff0d2ea7 00004A2A
PsSetCreateProcessNotifyRoutine 0xfc58e194 vmci.sys
KeBugCheckCallbackListHead 0xfc1e85ed NDIS.sys (Ndis miniport)
KeBugCheckCallbackListHead 0x806d57ca hal.dll (ACPI 1.0 - APIC platform UP)
KeRegisterBugCheckReasonCallback 0xfc967ac0 mssmbios.sys (SMBiosData)
KeRegisterBugCheckReasonCallback 0xfc967a78 mssmbios.sys (SMBiosRegistry)
[snip]
Here is an example of detecting the malicious process creation callback installed by the Rustock rootkit (points to memory owned by \Driver\pe386).
$ python vol.py -f rustock.vmem callbacks
Volatility Foundation Volatility Framework 2.4
Type Callback Owner
PsSetCreateProcessNotifyRoutine 0xf88bd194 vmci.sys
PsSetCreateProcessNotifyRoutine 0xb17a27ed '\\Driver\\pe386'
KeBugCheckCallbackListHead 0xf83e65ef NDIS.sys (Ndis miniport)
KeBugCheckCallbackListHead 0x806d77cc hal.dll (ACPI 1.0 - APIC platform UP)
KeRegisterBugCheckReasonCallback 0xf8b7aab8 mssmbios.sys (SMBiosData)
KeRegisterBugCheckReasonCallback 0xf8b7aa70 mssmbios.sys (SMBiosRegistry)
KeRegisterBugCheckReasonCallback 0xf8b7aa28 mssmbios.sys (SMBiosDataACPI)
KeRegisterBugCheckReasonCallback 0xf76201be USBPORT.SYS (USBPORT)
KeRegisterBugCheckReasonCallback 0xf762011e USBPORT.SYS (USBPORT)
KeRegisterBugCheckReasonCallback 0xf7637522 VIDEOPRT.SYS (Videoprt)
[snip]
Here is an example of detecting the malicious registry change callback installed by the Ascesso rootkit. There is one CmRegisterCallback pointing to 0x8216628f which does not have an owner. You also see two GenericKernelCallback with the same address. This is because notifyroutines finds callbacks in multiple ways. It combines list traversal and pool tag scanning. This way, if the list traversal fails, we can still find information with pool tag scanning. However, the Windows kernel uses the same types of pool tags for various callbacks, so we label those as generic.
$ python vol.py -f ascesso.vmem callbacks
Volatility Foundation Volatility Framework 2.4
Type Callback Owner
IoRegisterShutdownNotification 0xf853c2be ftdisk.sys (\Driver\Ftdisk)
IoRegisterShutdownNotification 0x805f5d66 ntoskrnl.exe (\Driver\WMIxWDM)
IoRegisterShutdownNotification 0xf83d98f1 Mup.sys (\FileSystem\Mup)
IoRegisterShutdownNotification 0xf86aa73a MountMgr.sys (\Driver\MountMgr)
IoRegisterShutdownNotification 0x805cdef4 ntoskrnl.exe (\FileSystem\RAW)
CmRegisterCallback 0x8216628f UNKNOWN (--)
GenericKernelCallback 0xf888d194 vmci.sys
GenericKernelCallback 0x8216628f UNKNOWN
GenericKernelCallback 0x8216628f UNKNOWN
To print a driver's IRP (Major Function) table, use the driverirp command. This command inherits from driverscan so that its able to locate DRIVER_OBJECTs. Then it cycles through the function table, printing the purpose of each function, the function's address, and the owning module of the address.
Many drivers forward their IRP functions to other drivers for legitimate purposes, so detecting hooked IRP functions based on containing modules is not a good method. Instead, we print everything and let you be the judge. The command also checks for Inline hooks of IRP functions and optionally prints a disassembly of the instructions at the IRP address (pass -v or --verbose to enable this).
This command outputs information for all drivers, unless you specify a regular expression filter.
$ python vol.py -f tdl3.vmem driverirp -r vmscsi
Volatility Foundation Volatility Framework 2.4
--------------------------------------------------
DriverName: vmscsi
DriverStart: 0xf9db8000
DriverSize: 0x2c00
DriverStartIo: 0xf97ea40e
0 IRP_MJ_CREATE 0xf9db9cbd vmscsi.sys
1 IRP_MJ_CREATE_NAMED_PIPE 0xf9db9cbd vmscsi.sys
2 IRP_MJ_CLOSE 0xf9db9cbd vmscsi.sys
3 IRP_MJ_READ 0xf9db9cbd vmscsi.sys
4 IRP_MJ_WRITE 0xf9db9cbd vmscsi.sys
5 IRP_MJ_QUERY_INFORMATION 0xf9db9cbd vmscsi.sys
6 IRP_MJ_SET_INFORMATION 0xf9db9cbd vmscsi.sys
7 IRP_MJ_QUERY_EA 0xf9db9cbd vmscsi.sys
[snip]
In the output, it is not apparent that the vmscsi.sys driver has been infected by the TDL3 rootkit. Although all IRPs point back into vmscsi.sys, they point at a stub staged in that region by TDL3 for the exact purpose of bypassing rootkit detection tools. To get extended information, use --verbose:
$ python vol.py -f tdl3.vmem driverirp -r vmscsi --verbose
Volatility Foundation Volatility Framework 2.4
--------------------------------------------------
DriverName: vmscsi
DriverStart: 0xf9db8000
DriverSize: 0x2c00
DriverStartIo: 0xf97ea40e
0 IRP_MJ_CREATE 0xf9db9cbd vmscsi.sys
0xf9db9cbd a10803dfff MOV EAX, [0xffdf0308]
0xf9db9cc2 ffa0fc000000 JMP DWORD [EAX+0xfc]
0xf9db9cc8 0000 ADD [EAX], AL
0xf9db9cca 0000 ADD [EAX], AL
1 IRP_MJ_CREATE_NAMED_PIPE 0xf9db9cbd vmscsi.sys
0xf9db9cbd a10803dfff MOV EAX, [0xffdf0308]
0xf9db9cc2 ffa0fc000000 JMP DWORD [EAX+0xfc]
0xf9db9cc8 0000 ADD [EAX], AL
0xf9db9cca 0000 ADD [EAX], AL
[snip]
Now you can see that TDL3 redirects all IRPs to its own stub in the vmscsi.sys driver. That code jumps to whichever address is pointed to by 0xffdf0308 - a location in the KUSER_SHARED_DATA region.
Windows uses a layered driver architecture, or driver chain so that multiple drivers can inspect or respond to an IRP. Rootkits often insert drivers (or devices) into this chain for filtering purposes (to hide files, hide network connections, steal keystrokes or mouse movements). The devicetree plugin shows the relationship of a driver object to its devices (by walking _DRIVER_OBJECT.DeviceObject.NextDevice
) and any attached devices (_DRIVER_OBJECT.DeviceObject.AttachedDevice
).
In the example below, Stuxnet has infected \FileSystem\Ntfs
by attaching a malicious unnamed device. Although the device itself is unnamed, the device object identifies its driver (\Driver\MRxNet).
$ python vol.py -f stuxnet.vmem devicetree
Volatility Foundation Volatility Framework 2.4
[snip]
DRV 0x0253d180 '\\FileSystem\\Ntfs'
---| DEV 0x82166020 (unnamed) FILE_DEVICE_DISK_FILE_SYSTEM
------| ATT 0x8228c6b0 (unnamed) - '\\FileSystem\\sr' FILE_DEVICE_DISK_FILE_SYSTEM
---------| ATT 0x81f47020 (unnamed) - '\\FileSystem\\FltMgr' FILE_DEVICE_DISK_FILE_SYSTEM
------------| ATT 0x81fb9680 (unnamed) - '\\Driver\\MRxNet' FILE_DEVICE_DISK_FILE_SYSTEM
---| DEV 0x8224f790 Ntfs FILE_DEVICE_DISK_FILE_SYSTEM
------| ATT 0x81eecdd0 (unnamed) - '\\FileSystem\\sr' FILE_DEVICE_DISK_FILE_SYSTEM
---------| ATT 0x81e859c8 (unnamed) - '\\FileSystem\\FltMgr' FILE_DEVICE_DISK_FILE_SYSTEM
------------| ATT 0x81f0ab90 (unnamed) - '\\Driver\\MRxNet' FILE_DEVICE_DISK_FILE_SYSTEM
[snip]
The devicetree plugin uses "DRV" to indicate drivers, "DEV" to indicate devices, and "ATT" to indicate attached devices (just like OSR's DeviceTree utility).
The x64 version looks very similar:
$ /usr/bin/python2.6 vol.py -f ~/Desktop/win7_trial_64bit.raw --profile=Win7SP0x64 devicetree
Volatility Foundation Volatility Framework 2.4
DRV 0x174c6350 \Driver\mouhid
---| DEV 0xfffffa8000dfbc90 FILE_DEVICE_MOUSE
------| ATT 0xfffffa8000ec7060 - \Driver\mouclass FILE_DEVICE_MOUSE
DRV 0x17660cb0 \Driver\rspndr
---| DEV 0xfffffa80005a1c20 rspndr FILE_DEVICE_NETWORK
DRV 0x17663e70 \Driver\lltdio
---| DEV 0xfffffa8000c78b70 lltdio FILE_DEVICE_NETWORK
DRV 0x17691d70 \Driver\cdrom
---| DEV 0xfffffa8000d00060 CdRom0 FILE_DEVICE_CD_ROM
DRV 0x176a7280 \FileSystem\Msfs
---| DEV 0xfffffa8000cac060 Mailslot FILE_DEVICE_MAILSLOT
DRV 0x176ac6f0 \FileSystem\Npfs
---| DEV 0xfffffa8000cac320 NamedPipe FILE_DEVICE_NAMED_PIPE
DRV 0x176ade70 \Driver\tdx
---| DEV 0xfffffa8000cb8c00 RawIp6 FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000cb8e30 RawIp FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000cb7510 Udp6 FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000cb7740 Udp FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000cb7060 Tcp6 FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000cad140 Tcp FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000cadbb0 Tdx FILE_DEVICE_TRANSPORT
DRV 0x176ae350 \Driver\Psched
---| DEV 0xfffffa8000cc4590 Psched FILE_DEVICE_NETWORK
DRV 0x176aee70 \Driver\WfpLwf
DRV 0x176b93a0 \Driver\AFD
---| DEV 0xfffffa8000cb9180 Afd FILE_DEVICE_NAMED_PIPE
DRV 0x176c28a0 \Driver\NetBT
---| DEV 0xfffffa8000db4060 NetBT_Tcpip_{EE0434CC-82D1-47EA-B5EB-AF721863ACC2} FILE_DEVICE_NETWORK
---| DEV 0xfffffa8000c1d8f0 NetBt_Wins_Export FILE_DEVICE_NETWORK
DRV 0x176c3930 \FileSystem\NetBIOS
---| DEV 0xfffffa8000cc3680 Netbios FILE_DEVICE_TRANSPORT
[snip]
This plugin helps you detect hidden processes by comparing what PsActiveProcessHead contains with what is reported by various other sources of process listings. It compares the following:
- PsActiveProcessHead linked list
- EPROCESS pool scanning
- ETHREAD pool scanning (then it references the owning EPROCESS)
- PspCidTable
- Csrss.exe handle table
- Csrss.exe internal linked list
On Windows Vista and Windows 7 the internal list of processes in csrss.exe is not available. It also may not be available in some XP images where certain pages are not memory resident.
Here is an example of detecting the Prolaco malware with psxview. A "False" in any column indicates that the respective process is missing. You can tell "1_doc_RCData_61" is suspicious since it shows up in every column except pslist (PsActiveProcessHead).
$ python vol.py -f prolaco.vmem psxview
Volatility Foundation Volatility Framework 2.4
Offset(P) Name PID pslist psscan thrdproc pspcid csrss session deskthrd
---------- -------------------- ------ ------ ------ -------- ------ ----- ------- --------
0x06499b80 svchost.exe 1148 True True True True True True True
0x04b5a980 VMwareUser.exe 452 True True True True True True True
0x05f027e0 alg.exe 216 True True True True True True True
0x010f7588 wuauclt.exe 468 True True True True True True True
0x04c2b310 wscntfy.exe 888 True True True True True True True
0x061ef558 svchost.exe 1088 True True True True True True True
0x06015020 services.exe 676 True True True True True True True
0x06384230 vmacthlp.exe 844 True True True True True True True
0x069d5b28 vmtoolsd.exe 1668 True True True True True True True
0x04a544b0 ImmunityDebugge 1136 True True True True True True True
0x0655fc88 VMUpgradeHelper 1788 True True True True True True True
0x06945da0 spoolsv.exe 1432 True True True True True True True
0x05f47020 lsass.exe 688 True True True True True True True
0x0113f648 1_doc_RCData_61 1336 False True True True True True True
0x04a065d0 explorer.exe 1724 True True True True True True True
0x066f0978 winlogon.exe 632 True True True True True True True
0x0115b8d8 svchost.exe 856 True True True True True True True
0x063c5560 svchost.exe 936 True True True True True True True
0x01122910 svchost.exe 1028 True True True True True True True
0x04be97e8 VMwareTray.exe 432 True True True True True True True
0x0211ab28 TPAutoConnSvc.e 1968 True True True True True True True
0x049c15f8 TPAutoConnect.e 1084 True True True True True True True
0x05471020 smss.exe 544 True True True True False False False
0x066f0da0 csrss.exe 608 True True True True False True True
0x01214660 System 4 True True True True False False False
0x0640ac10 msiexec.exe 1144 False True False False False False False
0x005f23a0 rundll32.exe 1260 False True False False False False False
The output looks similar for x64 systems:
$ python vol.py -f ~/Desktop/win7_trial_64bit.raw --profile=Win7SP0x64 psxview
Volatility Foundation Volatility Framework 2.4
Offset(P) Name PID pslist psscan thrdproc pspcid csrss session deskthrd
------------------ -------------------- ------ ------ ------ -------- ------ ----- ------- --------
0x0000000017692300 wininit.exe 332 True True True True True True True
0x00000000173c5700 lsass.exe 444 True True True True True True False
0x0000000017803b30 rundll32.exe 2016 True True True True True True False
0x0000000017486690 spoolsv.exe 1076 True True True True True True True
0x0000000017db7960 svchost.exe 856 True True True True True True False
0x0000000017dd09e0 svchost.exe 916 True True True True True True False
[snip]
This command prints installed kernel timers (KTIMER) and any associated DPCs (Deferred Procedure Calls). Rootkits such as Zero Access, Rustock, and Stuxnet register timers with a DPC. Although the malware tries to be stealthy and hide in kernel space in a number of different ways, by finding the KTIMERs and looking at the address of the DPC, you can quickly find the malicious code ranges.
Here's an example. Notice how one of the timers has an UNKNOWN module (the DPC points to an unknown region of kernel memory). This is ultimately where the rootkit is hiding.
$ python vol.py -f rustock-c.vmem timers
Volatility Foundation Volatility Framework 2.4
Offset(V) DueTime Period(ms) Signaled Routine Module
---------- ------------------------ ---------- ---------- ---------- ------
0xf730a790 0x00000000:0x6db0f0b4 0 - 0xf72fb385 srv.sys
0x80558a40 0x00000000:0x68f10168 1000 Yes 0x80523026 ntoskrnl.exe
0x821cb240 0x00000000:0x68fa8ad0 0 - 0xf84b392e sr.sys
0x8054f288 0x00000000:0x69067692 0 - 0x804e5aec ntoskrnl.exe
0xf7c13fa0 0x00000000:0x74f6fd46 60000 Yes 0xf7c044d3 ipsec.sys
0xf7c13b08 0x00000000:0x74f6fd46 0 - 0xf7c04449 ipsec.sys
0x8055a300 0x00000008:0x61e82b46 0 - 0x80533bf8 ntoskrnl.exe
0xf7c13b70 0x00000008:0x6b719346 0 - 0xf7c04449 ipsec.sys
0xf7befbf0 0x00000000:0x690d9da0 0 - 0xf89aa3f0 TDI.SYS
0x81ea5ee8 0x00000000:0x7036f590 0 - 0x80534016 ntoskrnl.exe
0x81d69180 0x80000000:0x3ae334ee 0 - 0x80534016 ntoskrnl.exe
0xf70d0040 0x00000000:0x703bd2ae 0 - 0xf70c3ae8 HTTP.sys
0xf7a74260 0x00000000:0x75113724 60000 Yes 0xf7a6cf98 ipnat.sys
0x82012e08 0x00000000:0x8a87d2d2 0 - 0xf832653c ks.sys
0x81f01358 0x00000008:0x6b97b8e6 0 - 0xf7b8448a netbt.sys
0x81f41218 0x00000000:0x6933c340 0 - 0xf7b8448a netbt.sys
0x805508d0 0x00000000:0x6ba6cdb6 60000 Yes 0x804f3b72 ntoskrnl.exe
0x80559160 0x00000000:0x695c4b3a 0 - 0x80526bac ntoskrnl.exe
0x820822e4 0x00000000:0xa2a56bb0 150000 Yes 0x81c1642f UNKNOWN
0xf842f150 0x00000000:0xb5cb4e80 0 - 0xf841473e Ntfs.sys
0x821811b0 0x00000131:0x34c6cb8e 0 - 0xf83fafdf NDIS.sys
...
Please note: the timers are enumerated in different ways depending on the target operating system. Windows stores the timers in global variables for XP, 2003, 2008, and Vista. Since Windows 7, the timers are are in processor-specific regions off of KPCR (Kernel Processor Control Region). As of Volatility 2.1, if there are multiple CPUs, the timers plugin finds all KPCRs and prints the timers associated with each CPU.
For more information on timer objects, see Ain't Nuthin But a K(Timer) Thing, Baby.
Volatility Foundation
Getting Started
- FAQ
- Installation
- Linux
- Mac
- Android
- Basic Usage
- 2.6 Win Profiles
- Encrypted KDBG
- Pyinstaller Builds
- Unified Output
Command References
Development
Miscellaneous
Physical Address Spaces