Home
Forums
News
Software
Rootkit List
Articles
Links
Contact Us




Click Flag for Translation

Subscribe to the Antirootkit Newsletter
Enter Email Address

Subscribe
Unsubscribe

Home > Articles

A Journey to the Center of the Rustock.B Rootkit

Version: 1.0

Last Update: 20th January 2007

Author: Frank Boldewin / www.reconstructer.org



Table of Contents


1 ABSTRACT
2 INTRODUCTION
3 STAGE 1 - DROP FROM THE MOTHER SHIP
4 STAGE 2 – KERNEL CODE VS PE-TOOLS
5 STAGE 3 – NAKED LOOKS BEST
6 SPEEDDUMPING WITH SOFTICE+ICEEXT
6.1 PREPARATION
6.2 DEBUGGING AND DUMPING
7 CONCLUSION
8 REFERENCES

 

1 Abstract

"You try to look innocent, but what's behind the curtain? Whatever you hide or pretend will be detected - this is certain!" On 27th December 2006 I found a sample of the Rustock.B Rootkit at www.offensivecomputing.net, which was only sparsely analyzed at this time. I was keen to study its behaviour, as I’ve heard a lot of stories about this infamous Rootkit. Rustock included several techniques to obfuscate the driver which could be stumbling blocks for the researcher. Analyzing the binary was quite fun. Recalling the work I’ve done over the last few days, it is clear that Rustock is quite different from most other Rootkits I’ve seen in the past. It is not much because Rustock uses new techniques, but rather because it combines dozens of known tricks from other malware which makes it very effective.


2 Introduction

This paper is divided into two main parts. In the first part I wanted to extract the native Rootkit driver code but without the use of kernel debuggers or other ring0 tools. The second part covers the extraction over the last three stages but much faster and with lesser efforts using the SoftICE debugger. Each part shows various possibilities for solving the different problems facing the researcher when analyzing Rustock. The techniques can also be useful in future reversing sessions. All the tools I’ve used can be found in the references. Some of them are free and others again are commercial, like IDA Pro. Further all the binary dumps and IDA .idb files from each stage are included in the package with this paper. Use caution when reproducing the work described here. Consider employing a virtual machine like VMware or Virtual PC and perform the analysis on an isolated network to avoid the damage that could be caused by the Rootkit. Use at your own risk!

3 Stage 1 - Drop from the Mother ship

First thing we have to do is to browse into the directory stage1 and unzip the file Rustock-Rootkit.B-Password-infected.zip. The zipfile password is “infected”. Now we are ready to start.

Load the unpacked file rustock.exe into Ollydbg.

Right click and select:

Search for ---> All referenced text strings

The limited result may indicate that the binary is packed or obfuscated in some way. The best idea in this case is often to employ a tool like PEID or Protection-ID. Unfortunately, this time both tools cannot determine the Compiler/Packer/Protector. It could be that a proprietary obfuscation technique has been used. One of the indicators that a file is packed or
obfuscated often is some unrecognized data, thus we start scrolling down from the entry point at 0x401000 and strike a bonanza at address 0x401b82 (Figure 1). Place the cursor at this position and right click

Find references to ---> Selected address (or just CTRL+R)

Figure 2:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig1

The references window (Figure 2) should show three hits now. We choose the second one at address 0x40198d, because it’s the most likely reference that jumps directly to the unrecognized data (push 0x401b82/retn is the same as call 0x401b82). A good chance for us,
that this is the end of the obfuscation code.

Figure 2:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig2

So why not setting a breakpoint (F2 at cursor position) here and see what happens after we Run (F9) the code? (Figure 3).

Figure 3:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig3

After running the code, a breakpoint occurred at our address as expected. Now press Step into (F7) two times and we should be located at address 0x401b82 (Figure 4).

Figure 4:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig4

Hm, still doesn’t look like valid code, right?

No problem, right click

Analysis ---> Analyse code (or just CTRL+A)

and the result should look much better.

Use Step Over (F8) until you passed call 0x402092 at 0x401bac, which does some API importing stuff). Ollydbg now should be able to show you the API names to the relative addresses, e.g. CALL DWORD PTR:SS[EBP+8c3] = kernel32._lcreat

After reading some code, we notice that a file called “lzx32.sys” is created at 0x401c7c (Figure 5). It is fairly telltale that this is the kernel mode driver. So let us set another breakpoint at 0x401c7b (F2) and Run (F9) the code again.

Figure 5:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig5

After the breakpoint occurred select EDI in the Registers window, then right click Follow in Dump (Figure 6)

Figure 6:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig6

The register EDI points to the following string:

C:\windows\system32:lzx32.sys

Confused of the “:” instead of “\” in the pathname?

Is it a mistake? Surely not, the driver just is created as Alternative Data Stream (ADS). A nice method to hide the driver from easy detection, because neither Windows Explorer nor cmd.exe will show you ADS streams. This is only possible with special tools like Sysinternals streams.exe. To simplify our analysis it’s a good idea to let the code create a normal file. Therefore select the “: = 0x3a” in the memory map (Figure 7) and patch it to “\ = 0x5c” using right click

Binary ---> Edit (or just CTRL+E)

Figure 7:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig7


Lastly Step Over (F8) until the file was written (_lwrite) and closed (_lclose) at address 0x401cc7.

As the aim of this paper is to describe how to deobfuscate/unpack the driver, Ollydbg can be closed now and the first stage was mastered.

As a goody here is a short description for the folks, who are interested what else is going on in the dropper code of Rustock.

1. If API Import fails, connect to: http://208.66.194.158/index.php?page=main Delete lzx32.sys and Exit
2. Try to open the service control manager (if it fails go to 5)
3. Create Service PE386 (if it fails go to 5)
4. Start Service PE386 (if it fails go to 5 – if ok go to 7)
5. Create service registry entries by hand as lzx32
6. Invoke ZwLoadDriver
7. Inject Rustock.exe into the Explorer process, create a remote thread and Exit


4 Stage 2 – Kernel code vs PE-Tools

Welcome to Stage 2! After we have successfully detached the driver we load it into IDA and see what is going on there. You can find my detached driver code and .idb file in the directory “stage1”.
Hm, after running down some pages, it seems that there is more code obfuscation fun waiting for us. ;)
When we try to load this binary into Ollydbg now, a Message box pops up and tells us something like this:

File “original-dropped-lzx32_sys.sys” is a DLL. Windows can’t execute DLLs directly. Launch LOADDLL.EXE?

Usually after clicking ‘yes’ the DLL gets loaded by LOADDLL and stops at its entry point. But after selecting Run (F9) the next Message box appears and informs us about the following:

Entry Point Alert

Module “ntoskrnl” has entry point outside the code (as specified in the PE-Header). Maybe this file is selfextracting or self-modifying. Please keep it in mind when setting breakpoints!

And the same Message for HAL.DLL. No problem, but after clicking “Ok”
LOADDLL terminates with exit code 1001 and that’s it. :(

What now?

In these cases the best choice is to “fix” the PE-Files with PE-Tools.

So fire up PE-Tools and make the following modifications:

• Tools-->PE-Editor-->Select original-dropped lzx32_sys.sys
• Select “File Header”--->Characteristics--->unmark the “dll” bit---> click OK (Figure 8)
• Leave “Image File Header Editor”

Figure 8:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig8

Select “Optional Header” and change the “Subsystem” value to “2” (Windows GUI) ---> click OK (Figure 9)

Figure 9:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig9

Select “Directories”--->”Import Directory” and set its “RVA” and “Size” to “00000000”--->click Save and leave PE-Tools (Remember the old values 0x00010000 and 0x000001cb. We have to reset these later in order to have a working file!) (Figure 10)

Figure 10:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig10

So what have we done so far?

Before the settings were:
• A DLL
• A Native Executable
• Had Imports from Kernel Libraries NTOSKRNL.EXE and HAL.DLL

Now the settings are:
• No DLL
• A Windows GUI Application
• No Imports

Why can we do this?
The answer is easy. As long as the obfuscation code does not expect any special data returned by imported kernel library functions, it does not matter how we declare the PE-FILE. ;)

Therefore, after patching the PE-File behaviour we load up original dropped lzx32_sys.sys again and - Eureka!

After running down some pages again we notice some unrecognized data at address 0x116a4 again (Figure 11). Are the bells ringing?

Figure 11:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig11

Yep, we saw this stuff in the dropper code before. Why do not trying the same trick again? (Figure 12)

Figure 12:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig12

We select address 0x116a4, right click

Find references to--->Selected address (Figure 13)

As the first hit in the references looks best (push 0x116a4/retn) we choose this one (Figure 13), set a breakpoint using F2 (Figure 14) at address 0x110ce and Run (F9) the code.

Figure 13:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig13

Figure 14:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig14

When the breakpoint is reached press Step into (F7) two times. We should be arrived at address 0x116a4 (Figure 15).

Figure 15:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig15

Use hotkey CTRL+A to display some human readable code.
As the code looks clearly less obfuscated now (Figure 16), it’s time to dump the current state.

Figure 16:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig16

Click Plugins--->OllyDump--->Dump debugged process (Figure 17)

Figure 17:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig17

Unmark “Rebuild Import” and click “Dump” (Figure 18)

Figure 18:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig18

Cool, after saving the dump it is important to reset the old PE-File settings:
• Setting the “DLL” bit in the “Characteristics” area
• Setting the “Subsystem” to “Native” in the “Optional Header” area
• Setting the “RVA” and “Size” of the Import Directory field in the “Directories” area to 0x00010000 and 0x000001cb

Ok, that is it for the second stage.


5 Stage 3 – Naked looks best

For the last stage, load your dumped file from stage 2 into IDA or just use mine: stage2/lzx32-unobfuscated.idb

As you can see in Figure 19, you can find some comments within the .idb file. This allows a better understanding about the code.

Figure 19:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig19

Before I start explaining what the code between 0x116a4 and 0x11abc basically does, let’s have a short look at Figure 20.

Again, we have unrecognized data beginning at 0x11afc.

Figure 20:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig20

Unfortunately, there are no direct references in the code to this area, like in the two stages before. :(

Now we need a strategy.

We should start reading some code, to get a clue how to solve our problem.

Here’s a short description:
• Import some APIs from NTOSKRNL (ExAllocatePool, ExFreePool, ZwQuerySystemInformation, _stricmp)
• Query all running System modules using ZwQuerySystemInformation/Subfunction 0x0b
• Allocate Kernel memory
• Unpacking routine at 0x11788 ---> call sub_117d3 unpacks code to new allocated Kernel memory
• Move unpacked code over packed code area, grab imports from ntoskrnl.exe and hal.dll, destroy PE-Header (MZ, PE, e_lfanew) and rebase API calls
• Free the kernel memory, that is no longer used
• JMP EAX at address 0x117c8 ---> execute the real naked driver

So, how can we grab the real driver without any kernel debugging now?

Why not just ripping the unpacking code at address 0x117d3 and then dumping the whole data as a file?

A good idea especially before the PE-Header gets destroyed and the driver code rebased. ;)

My small C Program called lzx32-laststage-unpacker in directory stage3 exactly does this job (Figure 21).

Figure 21:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig21


Voila, last but not least we have a clean native driver that can be analyzed very easily now. As a whole analysis of the complete rootkit would go beyond the scope of this paper, here’s just a link to an analysis of Rustock.B:

http://www.sarc.com/avcenter/venc/data/backdoor.rustock.b.html#technicaldetails

Basically the paper may end here, but I thought it might be also of interest, how to do the same action like in all 3 stages with a kernel debugger, but faster.


6 Speeddumping with SoftICE+ICEEXT

6.1 Preparation

To fully understand what’s going on in the preparation, you need to know that a special function in NTOSKNRL.EXE called IopLoadDriver isn’t exported by default (next to others). If exported, this function could be a very useful breakpoint for us.

To solve this problem, we need the proper .pdb file of NTOSKRNL.EXE from the Microsoft server. Further, the downloaded .pdb file need conversion to the proprietary SoftICE format .nms. Normally not a big task, as SoftICE has its own Symbol retriever. The bad news is that this tool always sucks for me. :(

But why despair, if there’s another way!

The first thing we have to do is to leech the “Windows Debugging Tools” from the Microsoft website (Link can found in the references) and installing them.

Based on the fact that you have installed Driverstudio/SoftICE as well, edit the file %systemroot%\system32\drivers\winice.dat now.

Set NTSYMBOLS=ON
Set LOAD=SystemRoot\ntoskrnl.nms

Change value SYM=2048 (default is 512)
If you are not familiar with SoftICE you should read my paper:

The big SoftICE howto (see references)

Or if you are a WinDBG freak, use this one.

Do the following next steps:

md %systemroot%\symbols

cd %ProgramFiles%\Debugging Tools for Windows

symchk.exe %systemroot%\system32\ntoskrnl.exe /s
SRV*%systemroot%\symbols*http://msdl.microsoft.com/download/symbols

copy
%systemroot%\Symbols\ntoskrnl.pdb\<some_hash_value>\ntoskrnl.pdb
%systemroot%\system32

cd %ProgramFiles%\Compuware\DriverStudio\SoftICE

net start iceext

nmsym.exe %systemroot%\system32\ntoskrnl.exe
/OUTPUT:%systemroot%\system32\ntoskrnl.nms


So, what have we done so far?
• Created a symbol directory
• Switched to the Windows Debugging Tools directory
• Retrieved the proper NTOSKRNL.PDB from the Windows website with symchk.exe
• Copied the .pdb from the symbol directory into the Windows system32 directory
• Switched to the SoftICE directory
• Started the SoftICE extension ICEEXT and thus SoftICE too (which is quite tautological)
• Converted the .pdb file to a .nms file

Ok, we are now prepared to start the debugging session now.


6.2 Debugging and Dumping

Before we fire up the Rustock.exe we need to adjust two settings in the SoftICE window first. So enter SoftICE using CTRL+D and set (Figure 22)

!protect on
bpx ioplloaddriver

Figure 22:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig22

Leave the debugger using x and execute Rustock.exe
The debugger window should have been popped up now at the entry point of IopLoadDriver (Figure 23)

Figure 23:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig23

Next switch to the code window using F6 and scroll down until you see
code like this (Figure 24)

CALL MmLoadSystemImage
CMP EAX, EBX
MOV [EBP-54], EAX
JL somewhere
PUSH DWORD PTR [EBP-70]
CALL RtlImageNtHeader

On my machine IopLoadDriver+1c1 (Windows XP SP2 German) and address 0x805a0591.

Leave the code windows with F6 and set a breakpoint at the address were the following instruction is found and run the code using x.

PUSH DWORD PTR [EBP-70]

Figure 24:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig24

When the breakpoint occurred at PUSH DWORD PTR [EBP-70] enter:

d *(ebp-70)

As you can see in Figure 25, ebp-70 has a pointer to the start of the image to the Rootkit driver.

Let us grab some PE section info about the driver.

map32 system32:lzx32

So what do we know about the image now?
ImageStart ---> 0xf60ee000
Size: 0x110ce ---> (reloc_addr+size) - ImageStart
EntryPoint ---> ImageStart + (*(e_lfanew)+28h) = 0xf60e1000

Figure 25:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig25

As we know from the former debugging session with Ollydbg the end address of the first deobfuscation was at EntryPoint+0xce. So, it’s a good idea to set our next breakpoint at this address (Figure 26) and run the code again.

Figure 26:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig26
After the breakpoint is reached, use the trace option 2 times using t and you should see some well-known code (Figure 27).

Figure 27:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig27

The last breakpoint that is left, is the one behind the unpacking routine, we discussed in stage 3. And if you scroll down a little bit in the SoftICE code window you should find the unpacker at offset 0x1788, thus we set the breakpoint right behind it, on my machine at 0xf60e178d and run code the again.

So, after the breakpoint occurred and unpacking was done let’s see what we find at EDI now (Figure 28).

Yep, it points to the unpacked and untouched image we want to dump.

Figure 28:
A-Journey-to-the-Center-of-the-Rustock-B-Rootkit-Fig28

Therefore the last thing what’s left is using the dumping tool of IceExt.

!dump \??\c:\lzx32-native.sys 81967004 8880

That’s it folks! To clean your drive from Rustock.B again, just use the fine Rootkit-Detection-Tool called RkUnhooker (see References).


7 Conclusion

After studying this paper the reader now should have a better understanding what different approaches can lead to success when analyzing an obfuscated/packed driver. You may rest assured that reverse engineering Malware is getting harder in future. Therefore, being prepared with some armory and tricks is essential. I hope you enjoyed this paper a little and I would be glad about some constructive reviews.

Happy reversing!


8 References

PE-Tools 1.5.800.2006-RC7
http://neox.iatp.by/updates/pt_update_08_rc7.zip

Ollydbg v1.10
http://www.ollydbg.de/odbg110.zip

Ollydump v3.00.110
http://dd.x-eye.net/file/ollydump300110.zip

IDA Pro 5.0.0.879
http://www.datarescue.com/idabase/

Windows Debugging Tools v6.6.7.5
http://www.microsoft.com/whdc/devtools/debugging/default.mspx

Driverstudio v3.21
http://www.compuware.com/products/driverstudio/782_ENG_HTML.htm

IceExt v0.70
http://sourceforge.net/projects/iceext/

PEID v0.94
http://peid.has.it

ProtectionID 5.2b
http://protectionid.owns.it

The big SoftICE howto v1.1
http://www.reconstructer.org/papers/The%20big%20SoftICE%20howto.pdf

RkUnhooker v3.0
http://rku.xell.ru/?l=e&a=dl

Big thanks go to Val Smith, Marc Schönefeld, FX and Olli Koen for reviewing this paper!

 


Reproduced with kind permission of Frank Boldewin
www.reconstructer.org

This article is also available in PDF format along with the files mentioned from Frank's Site.

©2005 Antirootkit.com