Monday, July 20, 2009

New and Updated Volatility Plug-ins

Here is a Volatility plug-in for printing the Interrupt Descriptor Table (IDT) addresses:

http://mhl-malware-scripts.googlecode.com/files/idt.py

It only prints the IDT for one processor, so if anyone wants to update it to handle multiple processors, then feel free. Also if the system's KPCR is located at an address other than 0xFFDFF000 (see Brendan Dolan Gavitt's blog or Edgar Barbosa's paper), then you'll need to add the correct address to the script.

In order to test the script, I used Greg Hoglund's basic_interrupt.zip from rootkit.com. Below you can see that interrupt #46 (0x2E) is pointing inside the basic_int.sys driver.

# python volatility idt -f ../hooked_int.bin
IDT# ISR unused_lo segment_type system_segment_flag DPL P
0 0008:804df350 0 0e 0 0 1
1 0008:804df4cb 0 0e 0 0 1
[...]
46 0008:f8bcd550 0 0e 0 3 1

# python volatility modules -f ../hooked_int.bin
File Base Size Name
\WINDOWS\system32\ntoskrnl.exe 0x00804d7000 0x216700 ntoskrnl.exe
\WINDOWS\system32\hal.dll 0x00806ee000 0x020300 hal.dll
\??\C:\BASIC_INT.sys 0x00f8bcd000 0x001000 BASIC_INT.sys


Here is a Volatility plug-in for printing driver IRP function addresses:

http://mhl-malware-scripts.googlecode.com/files/driverirp.py

The script requires Andreas Schuster's driverscan.py plug-in. It works by over-riding the object_action method of the PoolScanDriver class. Thanks to Andreas for providing the framework. Thanks to AAron Walters for showing me how to over-ride the scanner methods.

In order to test the script, I used Jamie Butler's TCPIRPHook.zip from rootkit.com. Below you can see that the IRP_MJ_DEVICE_CONTROL function for Tcpip.sys is hooked by irphook.sys.

# python volatility driverirp -f ../irphook.bin
0x0218a830 0x823b45b8 7 0 0xb2ef3000 361600 Tcpip Tcpip \Driver\Tcpip
[0] [IRP_MJ_CREATE] => 0xb2ef94f9
[1] [IRP_MJ_CREATE_NAMED_PIPE] => 0xb2ef94f9
[2] [IRP_MJ_CLOSE] => 0xb2ef94f9
[3] [IRP_MJ_READ] => 0xb2ef94f9
[4] [IRP_MJ_WRITE] => 0xb2ef94f9
[5] [IRP_MJ_QUERY_INFORMATION] => 0xb2ef94f9
[6] [IRP_MJ_SET_INFORMATION] => 0xb2ef94f9
[7] [IRP_MJ_QUERY_EA] => 0xb2ef94f9
[8] [IRP_MJ_SET_EA] => 0xb2ef94f9
[9] [IRP_MJ_FLUSH_BUFFERS] => 0xb2ef94f9
[10] [IRP_MJ_QUERY_VOLUME_INFORMATION] => 0xb2ef94f9
[11] [IRP_MJ_SET_VOLUME_INFORMATION] => 0xb2ef94f9
[12] [IRP_MJ_DIRECTORY_CONTROL] => 0xb2ef94f9
[13] [IRP_MJ_FILE_SYSTEM_CONTROL] => 0xb2ef94f9
[14] [IRP_MJ_DEVICE_CONTROL] => 0xf8b615d0

# python volatility modules -f ../irphook.bin
File Base Size Name
\WINDOWS\system32\ntoskrnl.exe 0x00804d7000 0x216700 ntoskrnl.exe
\WINDOWS\system32\hal.dll 0x00806ee000 0x020300 hal.dll
\??\c:\irphook.sys 0x00f8b61000 0x001000 irphook.sys


I updated the usermode hook detection plug-in so that it uses pydasm in a cleaner, more reasonable method. Instead of checking the instruction string against a regex, it checks the opcode type and operand type using constants declared in the libdasm source code. Here is a copy of the new version:

http://mhl-malware-scripts.googlecode.com/files/usermode_hooks2.py

Also, as I mentioned in my original post about usermode hooks, it can be (and has now been) ported to kernel mode. Here is a copy of the version that detects IAT, EAT, and in-line hooks in kernel drivers instead of usermode modules:

http://mhl-malware-scripts.googlecode.com/files/kernel_hooks.py

I tested the script against a sample of the Skynet trojan. Below you can see that kernel_hooks.py detects the in-line hooks in ntoskrnl.exe functions.

# python volatility kernel_hooks -f ../skynet.bin -d outdir
Type: INLINE, Hooked driver: ntoskrnl.exe (0x804d7000 - 0x806ed780), Hooked function: IofCallDriver => jmp 0x8217f20b, Hooking driver:
Type: INLINE, Hooked driver: ntoskrnl.exe (0x804d7000 - 0x806ed780), Hooked function: IofCompleteRequest => jmp 0x820babc3, Hooking driver:


According to the write-up by McAfee, Skynet hooks two other functions using the in-line technique: NtFlushInstructionCache and NtEnumerateKey. The kernel_hooks.py does not currently detect these two, because they are not actually exported by ntoskrnl.exe, and the plug-in finds functions by walking the EAT. The addresses of the two functions are exposed through their corresponding SSDT entries, however, so its possible to find them that way and use the same check_inline function from kernel_hooks.py to inspect.

I also updated the "threads without modules" plug-in. As described in the original post on my blog, it detects hidden system/kernel threads. Using the new version, you don't have to make any adjustments to files in the Volatility core distribution anymore. Its more user-friendly now that it just over-rides the object_action method of the PoolScanThreadFast2 class in order to change the scanner's behavior.

http://mhl-malware-scripts.googlecode.com/files/orphan_threads.py

The output of this script still looks the same as before (sample shown for a memory dump of Tigger trojan):

# python volatility orphan_threads -f ../tigger.vmem
PID TID Offset StartAddress
------ ------ --------- ------------
4 248 0x2029da8 0xf623f54e
4 996 0x206fb90 0xf6240393
4 1372 0x2095700 0xf623ea46
4 564 0x209d3f8 0xf6240150


Last but not least, I updated the malfind plug-in for detecting hidden/injected code in usermode processes. You can find the latest version here:

http://mhl-malware-scripts.googlecode.com/files/malfind2.py

The new version includes the following changes:
  1. If it finds a PE file in the suspicious memory segment, it fixes up the ImageBase to match the address of the start of the memory region (in case you plan on opening the dumped EXE/DLL in IDA)
  2. If it finds shell code or non-executable data in the suspicious memory segment, it also prints a hexdump (aside from just a disassembly)
  3. It no longer makes external calls to volatility using the commands.getoutput() method. Instead, it accesses the desired functions using the volatility API, which greatly enhances speed. It went from 1 minute and 48 seconds down to just 4 seconds!! The change also makes it easier to use on Windows, because no path adjustments are needed.
Here's an example of what to see when processing a memory dump infected with Zeus. Note that it extracts a PE file from the memory.

# python volatility malfind2 -f ../zeus.vmem -d outdir

#
# svchost.exe (Pid: 972)
#

[!] Range: 0x00890000 - 0x008b7fff
Memory extracted to outdir2/malfind.972.890000-8b7fff.dmp
PE sections: {.odkx, .itiz, .ryd, .rsrc, }

#
# svchost.exe (Pid: 1064)
#

[!] Range: 0x01f00000 - 0x01f27fff
Memory extracted to outdir2/malfind.1064.1f00000-1f27fff.dmp
PE sections: {.odkx, .itiz, .ryd, .rsrc, }


Here's an example of what to see when processing a memory dump infected with Silent Banker. In this case, the suspicious memory is a trampoline for one of the trojan's API function hooks.

[!] Range: 0x01650000 - 0x01650fff
Memory extracted to outdir3/malfind.1876.1650000-1650fff.dmp
Hexdump:
0x01650000 58 68 05 00 66 01 68 00 00 00 00 68 00 00 80 7c Xh..f.h....h...|
0x01650010 68 68 18 0b 10 50 68 8f 9f 0a 10 c3 00 00 00 00 hh...Ph.........
0x01650020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x01650030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

Disassembly:
0x01650000 pop eax
0x01650001 push dword 0x1660005
0x01650006 push dword 0x0
0x0165000b push dword 0x7c800000
0x01650010 push dword 0x100b1868
0x01650015 push eax
0x01650016 push dword 0x100a9f8f
0x0165001b ret


Here's an example of what to see when processing a memory dump infected with CoreFlood. The suspicious memory in this case is the start of the code segment of the PE file (as mentioned in the original blog, CoreFlood strips its PE header).

[!] Range: 0x7ff80000 - 0x7ffadfff
Memory extracted to outdir4/malfind.248.7ff80000-7ffadfff.dmp
Hexdump:
0x7ff80000 81 ec 20 01 00 00 53 8b 9c 24 30 01 00 00 8b c3 .. ...S..$0.....
0x7ff80010 24 04 55 f6 d8 56 57 8b bc 24 34 01 00 00 68 05 $.U..VW..$4...h.
0x7ff80020 01 00 00 8d 4c 24 2c 51 1b c0 25 27 0c 00 00 33 ....L$,Q..%'...3
0x7ff80030 f6 8b ef 89 44 24 18 89 74 24 1c ff 15 b0 e1 f9 ....D$..t$......

Disassembly:
0x7ff80000 sub esp,0x120
0x7ff80006 push ebx
0x7ff80007 mov ebx,[esp+0x130]
0x7ff8000e mov eax,ebx
0x7ff80010 and al,0x4
0x7ff80012 push ebp
0x7ff80013 neg al
0x7ff80015 push esi
0x7ff80016 push edi
0x7ff80017 mov edi,[esp+0x134]
0x7ff8001e push dword 0x105
0x7ff80023 lea ecx,[esp+0x2c]
0x7ff80027 push ecx
0x7ff80028 sbb eax,eax
0x7ff8002a and eax,0xc27
0x7ff8002f xor esi,esi
0x7ff80031 mov ebp,edi
0x7ff80033 mov [esp+0x18],eax
0x7ff80037 mov [esp+0x1c],esi
0x7ff8003b call [0x7ff9e1b0]


Here 's an example of what to see when processing a memory dump infected with the Skynet trojan mentioned above. In this case, the suspicious memory holds a command and control IP address and the name of one of the files that it hides on disk using the rootkit.

#
# svchost.exe (Pid: 876)
#

[!] Range: 0x02410000 - 0x02414fff
Memory extracted to outdir5/malfind.876.2410000-2414fff.dmp
Hexdump:
0x02410000 18 00 00 00 68 74 74 70 73 3a 2f 2f 32 31 33 2e ....https://213.
0x02410010 31 33 33 2e 31 31 30 2e 32 31 2f 00 06 00 00 00 133.110.21/.....
0x02410020 31 30 31 32 30 00 02 00 00 00 30 00 00 4e 00 00 10120.....0..N..
0x02410030 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 MZ..............

[!] Range: 0x02430000 - 0x02434fff
Memory extracted to outdir5/malfind.876.2430000-2434fff.dmp
Hexdump:
0x02430000 00 00 00 10 00 00 00 00 53 4b 59 4e 45 54 63 6d ........SKYNETcm
0x02430010 64 2e 64 6c 6c 00 00 00 00 00 00 00 00 00 00 00 d.dll...........
0x02430020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x02430030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................


Please let me know if you have questions or comments about these plug-ins, and if you're interested in helping to build several others.

2 comments:

Joel said...

I'm running Volatility 1.3beta + malfind2py + pefile-1.2.10-63 + libdasm-1.5 on a WinXP-SP3 analysis host

Using the Metasploit framework to run memdump and acquire memory from the suspect host (the dumpfile is 61032.img)

Volatility + malfind2 run through dumping .dmp files but the script crashes with the following:

[!] Range: 0x00740000 - 0x007fdfff
Memory extracted to C:\temp\malcode\61032/malfind.3516.740000-7fdfff.dmp
[!] Cannot open C:\temp\malcode\61032/malfind.3516.740000-7fdfff.dmp with pefile

[!] Range: 0x00680000 - 0x00736fff
Memory extracted to C:\temp\malcode\61032/malfind.3516.680000-736fff.dmp
[!] Error dumping PE file to C:\temp\malcode\61032/malfind.3516.680000-736fff.dmp
PE sections: {}
Traceback (most recent call last):
File "c:\Trunk\Volatility\volatility", line 219, in
main()
File "c:\Trunk\Volatility\volatility", line 215, in main
command.execute()
File "c:\Trunk\Volatility\memory_plugins/malfind2.py", line 204, in execute
pe.OPTIONAL_HEADER.ImageBase = StartingVpn
AttributeError: PE instance has no attribute 'OPTIONAL_HEADER'

Michael Hale Ligh said...

It sounds like there may be a few issues. One thing you can try is upgrading to Volatility 1.3.2:

$ svn checkout http://volatility.googlecode.com/svn/branches/Volatility-1.3.2 volatility-read-only

Another problem may be directory paths (some are / and some are \ in your output). That's probably my fault, the malfind2.py isn't optimized for a Windows analysis system. If you send me an email (michael dot ligh at mnin dot org), I'll get you a copy of a new version that does paths correctly on Windows.

Regarding the AttributeError, it seems there's a PE file at 0x00680000, but maybe some portion of the PE header is corrupt so pefile can't process it. You can always use vaddump on the process and then look for the segment for 0x00680000-0x00736fff.