Part 1: Introduction and De-Obfuscating and Reversing the User-Mode Agent Dropper
Part 2: Reverse Engineering the Kernel-Mode Device Driver Stealth Rootkit
Part 3: Reverse Engineering the Kernel-Mode Device Driver Process Injection Rootkit
Part 4: Tracing the Crimeware Origins by Reversing the Injected Code
SUMMARY
This four part article series is a complete step-by-step tutorial on how to reverse engineer the ZeroAccess Rootkit. ZeroAcess is also known as the Smiscer or Max++ rootkit. You can either read along to gain an in-depth understand the thought process behind reverse engineering modern malware of this sophistication. The author prefers that you download the various tools mentioned within and reverse the rookit yourself as you read the article. If you would like to use the malware sample used in these articles, download it here: Max++ Malware. Note that this archive is password protected and the password is infected
InfoSec Institute would classify ZeroAccess as a sophisticated, advanced rootkit. It has 4 main components that we will reverse in great detail in this series of articles. ZeroAccess is a compartmentalized crimeware rootkit that serves as a platform for installing various malicious programs onto victim computers. It also supports features to make itself and the installed malicious programs impossible for power-users to remove and very difficult security experts to forensically analyze.
At the conclusion of the analysis, we will trace the criminal origins of the ZeroAccess rootkit. We will discover that the purpose of this rootkit is to set up a stealthy, undetectable and un-removable platform to deliver malicious software to victim computers. We will also see that ZeroAccess is being currently used to deliver FakeAntivirus crimeware applications that trick users into paying $70 to remove the “antivirus”. It could be used to deliver any malicious application, such as one that steals bank and credit card information in the future. Further analysis and network forensics supports that ZeroAccess is being hosted and originates from the Ecatel Network, which is controlled by the cybercrime syndicate RBN (Russian Business Network).
Symantec reports that 250,000+ computers have been infected with this rootkit. If 100% of users pay the $70 removal fee, it would net a total of $17,500,000. As it is not likely that 100% of users will pay the fee, assuming that perhaps 30% will, resulting $5,250,000 in revenue for the RBN cybercrime syndicate.
It has the following capabilities:
- Modern persistence hooks into the OS – Make it very difficult to remove without damaging the host OS
- Ability to use a low level API calls to carve out new disk volumes totally hidden from the infected victim, making traditional disk forensics impossible or difficult.
- Sophisticated and stealthy modification of resident system drivers to allow for kernel-mode delivery of malicious code
- Advanced Antivirus bypassing mechanisms.
- Anti Forensic Technology – ZeroAccess uses low level disk and filesystem calls to defeat popular disk and in-memory forensics tools
- Serves as a stealthy platform for the retrieval and installation of other malicious crimeware programs
- Kernel level monitoring via Asynchronous Procedure Calls of all user-space and kernel-space processes and images, and ability to seamlessly inject code into any monitored image
In this tutorial, our analysis will follow the natural execution flow for a new infection. This will result in a detailed chronology of the infection methodology and “workflow” that the rootkit uses to infect hosts. This conceptual workflow is repeated in many other advanced rootkit that have been analyzed, so it behooves you to understand this process and therefore be able to apply it to new malware reversing situations.
Usually, when a rootkit infects a host, the workflow is structured as follows:
- Infection vector allows for rootkit agent reaches victim’s system. (Drive-by-download, client side exploit or a dropper)
- User-mode agent execution
- Driver executable decryption and execution
- System hiding from Kernel-mode.
- Establishment on the host and Kernel-mode level monitoring/data-stealing.
- Sending of stolen data in a covert data channel.
Our analysis of ZeroAccess is split into a series of articles:
Part 1: Introduction and De-Obfuscating and Reversing the User-Mode Agent Dropper
Part 2: Reverse Engineering the Kernel-Mode Device Driver Stealth Rootkit
Part 3: Reverse Engineering the Kernel-Mode Device Driver Process Injection Rootkit
Part 4: Tracing the Crimeware Origins of ZeroAccess Rootkit by Reversing the Injected Code
Our analysis starts from analyzing the User-mode Agent and finishes at Kernel-mode where the rootkit drops two malicious device drivers.
Step-by-step Analysis
The ZeroAccess rootkit comes in the form of a malicious executable that delivered via infected Drive by Download Approach. Drive-by download means three things, each concerning the unintended download of computer software from the Internet:
- Downloads which a person authorized but without understanding the consequences (e.g. downloads which install an unknown or counterfeit executable program, ActiveX component, or Java applet).
- Any download that happens without a person’s knowledge.
- Download of spyware, a computer virus or any kind of malware that happens without a person’s knowledge.
Drive-by downloads may happen when visiting a website, viewing an e-mail message or by clicking on a deceptive pop-up window by clicking on the window in the mistaken belief that, for instance, an error report from the computer itself is being acknowledged, or that an innocuous advertisement pop-up is being dismissed. In such cases, the “supplier” may claim that the person “consented” to the download although actually unaware of having started an unwanted or malicious software download. Websites that exploit the Windows Metafile vulnerability may provide examples of drive-by downloads of this sort.
ZeroAccess has some powerful rootkit capabilities, such as:
- Anti FileSystem forensics by modifying and infecting critical system drivers (disk.sys, atapi.sys) as well as PIC driver object stealing and IRP Hooking.
- Infecting of System Drivers.
- User-mode Process Creation interception and DLL Injection, from KernelMode.
- DLL Hiding and Antivirus bypassing.
- Extremely resistant to Infection Removal.
Part 1: Reverse Engineering the User-Mode Agent/Dropper
The rootkit is obfuscated via a custom packed executable typically called ‘Max++ downloader install_2010.exe’. The hashes for this file are:
MD5: d8f6566c5f9caa795204a40b3aaaafa2
SHA1: d0b7cd496387883b265d649e811641f743502c41
SHA256: d22425d964751152471cca7e8166cc9e03c1a4a2e8846f18b665bb3d350873db
Basic analysis of this executable shows the following PE sections and imports:
Sections: .text .rdata .rsrc
Imports: COMCTL32.dll
The Import Table is left in a very poor condition for analysis. Typically this means that additional and necessary functions will be imported at Run Time. Let’s now check the Entry Point Code:
The start code is pretty standard, except for an interesting particular, as you can see at 00413BD5 we have an int 2Dh instruction.
The interrupt 2Dh instruction is mechanism used by Windows Kernel mode debugging support to access the debugging interface. When int 2Dh is called, system creates an EXCEPTION_RECORD structure with an exception code of STATUS_BREAKPOINT as well as other specific informations. This exeception is processed by calling KiDebugRoutine.
Int 2Dh is used by ntoskrnl.exe to interact with DebugServices but we can use it also in user-mode. If we try to use it in normal (not a debugged) application, we will get exception. However if we will attach debugger, there will be no exception.
(You can read more about this at the OpenRCE reference libraryhttp://www.openrce.org/reference_library/anti_reversing_view/34/INT%202D%20Debugger%20Detection/)
When int 2Dh is called we get our first taste of ZeroAccess anti-reversing and code obsfuction functionality. The system will skip one byte after the interrupt, leading to opcode scission. The actual instructions executed will differ from the apparent instructions that will be displayed in a dissasembler or debugger.
To continue further we need a mechanism to correctly handle int 2Dh call and mantain the jump-one-byte feature, and allow us to follow the opcode-splitted code. To do so, we are going to use StrongOD Olly plugin which can be downloaded here: http://reversengineering.wordpress.com/2010/07/26/strongod-0-3-4-639/
With StrongOD installed, after tracing over int 2Dh we are presenting with the following instructions:
The most interesting instruction for us here is the Call 00413bb4. Immediately after this instruction we have garbage code. Let’s enter into this call, and you are now presented with the following code block:
Again, we see int 2Dh, which will lead us one byte after the RETN instruction. The next piece of code will decrypt the adjacent routine, after tracing further, finally we land here:
This call will decrypt another block of code, at after that call execution jump here:
FS:[18] corresponds to TEB (Thread Environment Block) address, from TEB is obtained PEB (Process Environment Block) which is located at TEB Address + 30h.
PEB+0C corresponds to PPEB_LDR_DATA LdrData.
If you are using WinDBG, you can use this quick hint to uncover the link between structure -> offset ->involved member by issuing the following command:
0:004> dt nt!_PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0×000 Length : Uint4B
+0×004 Initialized : UChar
+0×008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0×014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
+0×024 EntryInProgress : Ptr32 Void
+0×028 ShutdownInProgress : UChar
+0x02c ShutdownThreadId : Ptr32 Void
As you can see, the malicious code refers to _PEB_LDR_DATA + 1Ch, by checking the output of WinDbg you can see that ECX now points to InInitializationOrderModuleList. The code that follows is responsible for locating Import Function addresses and then from this information building an ImportTable on the fly dynamically. Next there is a complex sequence of nested calls that have the principal aim of decrypting, layer by layer, the core routines of ZeroAccess. We will not describe the analysis of this piece of multi-layer code; it is left as an exercise for the reader. This section of code is quite long, repetitive, and frankly boring, and not relevant from a functionality point of view.
Imported Function addresses are successively protected and will be decrypted on fly only when they are called. Let’s take a look at how an API call actually looks:
Call 00401172 decrypts and return the API’s address in EAX. In the above code snippet, the API called is VirtualAlloc. Allocated memory will be used in future execution paths to decrypt a number of different blocks of instructions. These blocks will eventually constitute an executable dropped by the original infection agent.
Main executable ( the infection vector we are also referring to as the Agent) builds and drops various files into victim’s hard disk and as well as in memory. Whether on disk or in memory, the pattern used is always the same:
Next, let’s try to determine what is being decrypted in these blocks. We place a breakpoint at 0040162B, which is immediately after Next Block jump. The end of the Next Block corresponds to the end of decryption process, we will see in allocated memory the familiar ‘MZ’ signature, letting us know the executable is ready to be used. Before proceding we recommending dumping onto the the hard drive the full executable using the Backup functionality of Ollydbg.
The next block of code is protected with a VEH ( Vectored Exception Handler ) by using RtlAddVectoredExceptionHandler and RtlRemoveVectoredExceptionHandler. Inside this block we have a truly important piece of code. This block is loaded via the undocumented native API call, LdrLoadDll. A system DLL is called, lz32.dll, as well as the creation of a Section Object.
A Section Object represents a section of memory that can be shared. A process can use a section object to share parts of its memory address space (memory sections) with other processes. Section objects also provide the mechanism by which a process can map a file into its memory address space.
Take a look at the red rectangle, calling the value 003C24FB stored in EAX. As you can see this belongs to the previously loaded lz32.dll. Because of this call, execution flow jumps inside the lz32.dll, and which contains malicious code decrypted by the rootkit agent.
This is what the code of lz32.dll program looks like:
If we trace into the Call 003C23DB, we have a long routine that completes infection, and more precisely we have the kernel mode component installation phase. We will see a series of creative routines specifically written to elude classic Antivirus checks, such as the usage of Section Objects and Views placed into System Files.
Now, let’s take a look at the core routine of the Agent, which we will analyze piece by piece:
During the analysis of complex pieces of malware it’s a good practice to leave open the HandleView and ModuleView panes within OllyDbg. This will help you keep track of what is loaded/unloaded and what files/objects/threads/etc. are opened. Let’s see what happens in Call 003C1C2C at address 003C2461.
At first, we see the enumeration of Drivers placed into \system32\drivers, and next we have the following piece of code:
We have an interesting algorithm here, after driver enumeration a random number is generated, next fitted within a range of [0 - 0xFF] and used to randomly select from the driver list a file to be infected. Finally the string formatted as:
\._driver_name_
Now let’s watch what is going on in HandleView:
As you can see a Section Object is created according to the randomly selected driver file, and next will be opened as View inside this Section.
The access values for this section are set to 0xF001F. Let’s first talk about why this is important. During a malware analysis session, much like a forensic investigation, is fundamental to know what the access potential the various components have, so we can direct our investigation down the right path. This can be determined by checking the access rights assigned to various handles.
Let’s lookup what the access right of 0xF001F corresponds by looking in winnt.h:
#define SECTION_ALL_ACCESS 0xf001f
SECTION_ALL_ACCESS means the handle has the ability to Read, Write, Query and Execute. This is the optimal environment to place a malicious portion of code. Now, lets analyze further:
This block of code takes the driver previously selected and now registers it into:
\registry\MACHINE\SYSTEM\CurrentControlSet\services\
The \services entry under CurrentControlSet contains parameters for the device drivers, file system drivers, and Win32 service drivers. For each Service, there is a subkey with the name of the service itself. Our registry entry will be named \._driver_name_
Start Type has 0×3 value that means -> Load on Demand
Type: 0×1 -> Kernel Device Driver
Image Path -> \*
The same driver is always opened. Next, its handle used to send, via ZwFsControlCode, a FSCTL (File System Control Code). Taking a look at the API parameters at run time reveals that the FSCTL code is 9C040. This code corresponds to FSCTL_SET_COMPRESSION. It sets the compression state of a file or directory on a volume whose file system supports per-file and per-directory compression.
Next, a new executable will be built with the aforementioned decryption scheme and then loaded via ZwLoadDriver. This process will result in two device drivers:
- The first driver is unnamed and will perform IRP Hooking and Object and disk.sys/pci.sys Object Stealing (we will analyze this in greater detail later)
- The second driver, named B48DADF8.sys, is process creation aware and contains a novel DLL injection system (we will also analyze it greater detail later)
Once the driver infection is complete we land in an interesting piece of code:
Here, we see the loading of fmifs.dll. This DLL is the Format Manager for Installable File Systems, and it offers a set of functions for FileSystem Management.
In this case the exported function is FormatEx. A bit of documentation on FormatEx follows:
VOID
STDCALL
FormatEx(
PWCHAR DriveRoot,
DWORD MediaFlag,
PWCHAR Format,
PWCHAR Label,
BOOL QuickFormat,
DWORD ClusterSize,
PFMIFSCALLBACK Callback);
STDCALL
FormatEx(
PWCHAR DriveRoot,
DWORD MediaFlag,
PWCHAR Format,
PWCHAR Label,
BOOL QuickFormat,
DWORD ClusterSize,
PFMIFSCALLBACK Callback);
This function, as the name suggests is used to Format Volumes. In our case the DriverRoot is \\?\C2CAD972#4079#4fd3#A68D#AD34CC121074 and Format is NTFS. This is a remarkable feature unique to this rootkit. This call creates a hidden volume, and the volume will contain the driver and DLLs dropped by the ZeroAccess Agent. These files remain totally invisible to the victim (something we teach in our ethical hackingcourse).
The next step the Agent takes is to build, with the same decryption routine previously described, the remaining malicious executables that will be stored into the newly created hidden volume. These two files are:
- B48DADF8.sys
- max++.00,x86.dll
Both located into the hidden volume, \\?\C2CAD972#4079#4fd3#A68D#AD34CC121074\L\. We now we have a good knowledge of what user-mode side of ZeroAccess does, we can focus our attention to Kernel Mode side, by reversing the two drivers and dropped DLL.
Let’s continue to follow the workflow of the rootkit. If you are reversing along with us, analysis will logically follow the order of binaries dropped by the Agent. Our first driver to reverse will be the randomly named one, which will be in Part 2 of this tutorial.