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:
Usually, when a rootkit infects a host, the workflow is structured as follows:
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:
ZeroAccess has some powerful rootkit capabilities, such as:
The rootkit is obfuscated via a custom packed executable typically called ‘Max++ downloader install_2010.exe’. The hashes for this file are:
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 library http://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
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
+0x024 EntryInProgress : Ptr32 Void
+0x028 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:

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:

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 0x3 value that means -> Load on Demand
Type: 0x1 -> 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:

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:
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:
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
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.
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.
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.
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 library http://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
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
+0x024 EntryInProgress : Ptr32 Void
+0x028 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 0x3 value that means -> Load on Demand
Type: 0x1 -> 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)
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
);
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
hacking course).STDCALL
FormatEx(
PWCHAR DriveRoot,
DWORD MediaFlag,
PWCHAR Format,
PWCHAR Label,
BOOL QuickFormat,
DWORD ClusterSize,
PFMIFSCALLBACK Callback
);
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
No comments:
Post a Comment