Tuesday, May 29, 2012

Malware Analysis Tutorial 28: Break Max++ Rootkit Hidden Drive Protection

Learning Goals:
  1. Practice WinDbg for Intercepting Driver Loading
  2. Practice IMM for Modifying Binary Code
  3. Trace and Modify Control Flow Using IMM
Applicable to:
  1. Operating Systems
  2. Assembly Language
  3. Operating System Security
1. Introduction

One typical feature of Max++ is its ability to hide malicious files in a hidden drive. In this tutorial, we show you how to modify the malware itself to break its hidden drive protection. Our goal is to dig out the B48DADFD.sys from the hidden drive. Recall that B48DADFD.sys is used to load a series of malicious DLL files, introduced in Tutorial 27,

2. Preliminaries: Who Has Loaded B8DADFD.sys?
We need to figure out who loads B48DADFD.sys first. Simply follow the instructions (1) to (4) of Tutorial 19 to set up the lab experiment. Once the WinDbg is started, click Debug->Event Filter to enable the "Load Module" event (as shown in Figure 1). Then, after you hit the BP at 0x401417 in IMM, press SHIFT+F9 to let it go. The first module you capture will be "_" (the one which hacks lz32.dll) and the second hit will get you B48DADFD.sys.

Figure 1. Enable Event Filter on Module Loading

As shown in the following, when WinDbg hit DebugService2+0x10, it's the handler for module load event. If you list all current modules using "lm", you will notice that B48DADF8 is the module being loaded. We can further dump the stack contents to find out the caller.

kd> g
80506d3e cc              int     3
kd> lm
start    end        module name
804d7000 806ed680   nt         (pdb symbols)          c:\windows\symbols\ntoskrnl.pdb\47A5AC97343A4A7ABF14EFD9E99337722\ntoskrnl.pdb
fadfc000 fae01000   B48DADF8   (deferred)            
faed4000 faedc000   _          (deferred)            

Unloaded modules:
f77c4000 f77ef000   kmixer.sys
f7b6e000 f7b99000   kmixer.sys
fb206000 fb207000   drmkaud.sys
f7b99000 f7bbc000   aec.sys
f7c89000 f7c96000   DMusic.sys
facec000 facfa000   swmidi.sys
fb0f0000 fb0f2000   splitter.sys
fad6c000 fad77000   imapi.sys
fad5c000 fad6b000   redbook.sys
fae6c000 fae71000   Cdaudio.SYS
fb050000 fb053000   Sfloppy.SYS
fae64000 fae69000   Flpydisk.SYS
fae5c000 fae63000   Fdc.SYS
kd> kv
ChildEBP RetAddr  Args to Child             
f76a251c 80506d80 f76a25b4 f76a2530 00000003 nt!DebugService2+0x10 (FPO: [Non-Fpo])
f76a2540 805a3113 f76a25b4 fadfc000 ffffffff nt!DbgLoadImageSymbols+0x42 (FPO: [Non-Fpo])
f76a26e4 805a7c9d f76a2800 00000000 00000000 nt!MmLoadSystemImage+0xa80 (FPO: [Non-Fpo])
f76a2894 804de7ec 00000036 f76a2974 0000001c nt!NtSetSystemInformation+0x8a9 (FPO: [Non-Fpo])
f76a2894 804dd98d 00000036 f76a2974 0000001c nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f76a28a8)
*** ERROR: Module load completed but symbols could not be loaded for *
f76a2918 faed50c7 00000036 f76a2974 0000001c nt!ZwSetSystemInformation+0x11 (FPO: [3,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
f76a299c faed6b93 ffbd2478 ffb23550 003f005c _+0x10c7
f76a2a34 faed6bf8 ffbd2478 ffb23550 ffb97840 _+0x2b93
f76a2a4c 804e37f7 ffa337b0 ffbd2468 ffbd2468 _+0x2bf8
f76a2a5c 8056c712 ffa33798 ffaad86c f76a2c04 nt!IopfCallDriver+0x31 (FPO: [0,0,0])
f76a2b3c 80563fec ffa337b0 00000000 ffaad7c8 nt!IopParseDevice+0xa12 (FPO: [Non-Fpo])
f76a2bc4 805684da 00000000 f76a2c04 00000040 nt!ObpLookupObjectName+0x56a (FPO: [Non-Fpo])
f76a2c18 8056cbeb 00000000 00000000 160de801 nt!ObOpenObjectByName+0xeb (FPO: [Non-Fpo])
f76a2c94 8056ccba 0012d624 00100000 003d3150 nt!IopCreateFile+0x407 (FPO: [Non-Fpo])
f76a2cf0 8056cdf0 0012d624 00100000 003d3150 nt!IoCreateFile+0x8e (FPO: [Non-Fpo])
f76a2d30 804de7ec 0012d624 00100000 003d3150 nt!NtCreateFile+0x30 (FPO: [Non-Fpo])
f76a2d30 7c90e4f4 0012d624 00100000 003d3150 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f76a2d64)
0012d648 003c2507 00401166 003c0000 fffffffe 0x7c90e4f4
0012ffd0 8054b6b8 0012ffc8 81148da8 ffffffff 0x3c2507
00413a40 ec81ec8b 0000030c d98b5653 f4b58d57 nt!ExFreePoolWithTag+0x676 (FPO: [Non-Fpo])
00413a4c f4b58d57 8bfffffd f45589c3 0000c0e8 0xec81ec8b
00413a50 8bfffffd f45589c3 0000c0e8 10c38300 0xf4b58d57
00413a54 f45589c3 0000c0e8 10c38300 0cf85d89 0x8bfffffd
00413a58 00000000 10c38300 0cf85d89 f3b58dff 0xf45589c3

As shown by the stack dump, it's the lz32.dll code (one instruction before 0x3C2507) that eventually triggers the load of B48DADFD.sys. The chain of calls are:

one instruction before 0x3C2507 (lz32.dll) --> ... --> IoCreateFile --> ... --> IopfCallDriver ... ---> load B484DADFD.sys

Challenge 1. Use the analysis techniques presented in Tutorial 27 and analyze how B48DADFD is loaded.

3. Lab Configuration
Given that B48DADFD is loaded by one instruction before 3C2507, we can start our lab configuration to dig out the malicious driver file. The lab configuration is shown below:

First, follow the instructions (1) to (4) of  Tutorial 19.  When the breakpoint at 0x004017 is hit, jump to 0X3C2502 in the CPU pane and set a software breakpoint on it (by right clicking in CPU pane -> go to expression). You should see the binary code as shown in Figure 2 below.
Figure 2. Breakpoint Right Before Loading B48DADFD.sys

At this moment, we are now ready to take the B48DADFD out.

4. Surgery on Hidden Drive
As the hidden drive is not visible in Windows explorer, we have to call some functions available in the windows system library to get B48DADFD.sys out of the hidden drive. This is possible because Max++ hooks up the file system driver to handle special requests on the hidden drive. Our plan is to overwrite the instructions right after the instruction of 0x003C2502, and try to copy the file out.

Browsing the kernel32.dll (View -> Executable Modules -> ViewNames) gives us one good candidate, as shown in Figure 3.

Figure 3. kernel32.dll CopyFileA

By MSDN,the prototype of CopyFileA is defined as below: (where "A" stands for ASCII, i.e., how file name is encoded. You could see there are several variations in Figure 3, such as copyFileW).

  _In_  LPCTSTR lpExistingFileName,
  _In_  LPCTSTR lpNewFileName,
  _In_  BOOL bFailIfExists

So the following is our plan, starting at 0x003C250C, we can use IMM to modify the binary code and insert a instruction CALL 7C8286D6. Meanwhile, we will modify the stack contents so that the three parameters are arranged properly. We can first modify the instructions, as shown in Figure 4. Simply right click in CPU pane --> assemble (or press space bar), and type "CALL 7C8286D6") to modify the binary code.

Figure 4. Modify Binary Code in IMM

We now need to create the parameters for the CopyFileA call. We need to insert two strings in the data dump window, the general approach is shown in Figure 5. Right click -> Binary -> Edit. Make sure that only insert data when EIP is 0x003c2507 to avoid destructing the data for previous calls of Max++.

Figure 5. Modify Binary Code

The inserted data is shown in Figure 6. The file string "\\.\C2CAD...\B48DADF8.sys" is retrieved from the earlier analysis of the ntLoadImage in the stack dump in section 2. As shown in Figure 6, we inserted two pieces of strings, one is the "\\.\C2CAD...\B48DADF8.sys" and the other is "c:\bad.sys". Also the parameter setting is shown in the stack once we have entered the data in the memory dump pane. The third parameter (FailfExists) can be set to "false" (0).

Figure 6. The Input Parameters of CopyFileA

Execute one step in the CPU pane and we have successfully pulled out the malicious driver file out. See Figure 7.

Challenge 2. List all files in the hidden drive and take all of them out.

Figure 7. Success

No comments:

Post a Comment