"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:

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:

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:

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:

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:

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

Figure 6:

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:

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:

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:

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:

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

Figure 12:

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:

Figure 14:

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

Figure 15:

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:

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

Figure 17:

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

Figure 18:

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:

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:

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.

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:

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:

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:

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:

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:

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

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:
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:

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:

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.