In this tutorial we will explore a case in which crossing the border between the userspace and kernelspace code is necessary. For this purpose we will use a sample of ZeroAccess rootkit, analysed and described in this excellent article. You can download this sample from here. The password is "infected".
We will execute this analysis scenario using Windows 7 guest OS. Let's verify if the tprobe-volatility configuration is proper:
Make sure that the corresponding Window 7 profile is the only one uncommented. If you are unsure what is your installation directory, you can verify it with this command:
Now, boot the tprobe-qemu with a qemu drive of your choice containing Windows 7. After logging in, download and extract the sample. Shut down the system and create a disk snapshot so that you can easily reverse it to the pre-infection state.
If everything went smoothly, start the clean machine and after the booting process has finished, attach tprobe:
Upon succsesful attaching to the target machine, the UI will appear.
In order to explain the structures and code that we will explore, a short introduction into syscalls and drivers in in order.
In the software that runs your computer, there are multpiple programs running concurrently. A separation of responsibility among them is necessary. Word.exe is responsible for turning the text and formatting instructions you enter into rich text data, Steam.exe is responsible for downloading and starting your games, etc. But there is one special kind of a program that has an unique set of responsibilities - your operating system's kernel.
All modern operating system' kernels are responsible for several fundamental tasks. They decide which process will be executed in what period (e.g. who has the processor), which process can access a certain file or a device, and - how does this happen. So, the only way your program can interact with these (devices and files in filesystem) is through the kernel. It does so using syscalls - a special processor instructions.
In the diagram below, you can see a simplified illustration of a lifecycle of an syscall. Upon clicking "Save" in your Word.exe process, it makes a call to it's code libraries, which ends up in most cases in ntdll.dll library.
The ntdll.dll library prepares a syscall, that is, a request to the kernel to perform an oepration, that will enable the library to return to the callee with the information on completing.
In the course of servicing the syscall, kernel often makes use of various kernel modules and device drivers, special programs running within the kernel's realm (more details on the process of completing a syscall involving drivers will be described in subsequent parts of this tutorial).
This kernelspace realm is unreachable from the perspective of a simple user process. This decision has been made for security and stability reasons.
One of the main security rationale behind the separation is the following: whoever kontrols the kernel code, controls everything that userspace processes see. This means, whoever can tamper the data and code in kernelspace, has the ability to fool the processes attempting to diagnose it in userspace. The same goes for defensive software that does not possess a kernel component. (you can read more on this in many excellent books on the subject of rootkits).
This ability is a very attractive perk for the creators of malware. And they often reach for it, implementing capabilities for intruding and manipulating kernel data and code. These are called "rootkit capabilities" of malware.
Every now and then, in case of an incident involving such malware, an analyst has a need to inspect the selected regions of the kernelspace. Unfortunately, not many debuggers give him this ability. Fortunately thou, TProbe does.
For the time being, this explanation hsould suffice. We will delve into more details in subseqent blog entries.
Let's get back to crossing the userspace/kernelspace border with TProbe. We will do this by analysing one of the more advanced rootkit malwares - ZeroAccess. Specifically, we will examine the operation of deploying the malicious driver, that will be later used for fooling user's diagnostic tools and hide the malicious presence in the system.
For the sake of simplicity, we will skip a lot of the required analysis tasks for ZeroAcess sample. If you are interested in these, please refer to the beforementioned article. In this post we will focus solely on the driver deployment.
ZeroAccess's dropper searches through the driver filenames in system32/drivers directory. It picks a random name and prefixes it with a "." (dot) to create the name for his malicious driver. Then it tries loading it into the kernel with a ZwLoadDriver call.
In the prepared machine with the attached TProbe debugger, first, let's examine the SSDT table by entering in TProbe console simply: "ssdt"
In the resulting listing, let's find the entry corresponding to the ZwLoadDriver entry. it should look similar to this one:
Lets place a breakpoint on the routine address <address> with swb command (system-wide breakpoint):
The system-wide breakpoint stops the execution of code in the machine regardless of which thread belonging to which process has hit our breakpoint.
After we placed the breakpoint, we can continue by pressing F9 in the Code View.
As we can verify, the execution of the machine has been released and you can freely interact with it. Run the ZeroAccess sample by double-clicking on it. After some time, the execution should hit the ZwLoadDriver routine.
Now, we are inside the kernelspace. We can explore the threads that service the ZwLoadDriver syscall. First, let's extract names of exported functions of the kernel modules, so that we can have a better insight into what's going on:
Let's remove the breakpoint. Now we can execute the code in a step-by-step manner, delve into the subsequent calls (F7 in Code View) or step over them (F8), access kernel structures used in servicing calls, etc.
For now, we will observe the changes in the kernel module. Let's list all the modules that are currently loaded into the kernel
We will compare this list with the new one, after the loading of a driver has finished. Let's use a combination of stepping over and breakpoints in order to reach the end of the servicing routine.
After that, let's check the list again.
As we can see, there is a new module loaded into the kernelspace. This is one of the two malicious drivers deployed into kernel by ZeroAccess, that together provide it's rootkit capabilities. If we release the execution of the machine, we will see that the sample is gone from the system. Or at least that's what usermode programs tell us.
But don't be fooled by that. ZeroAccess deployed it's infection architecture in the system and will run whenever you restart your machine. And in some cases - even after the system reinstallation.
We will follow up on exploring kernel structures in the next part of this post.