cs373

Writeups for CS 373 Defense against the Dark Arts, Winter 2018

View on GitHub

Week 5: Rootkits

This week we dive into windows internals to learn about rootkits. This is going to be a shorter post this week because i’m burning myself out writing these.

Hooking

Once a piece of malware has gotten onto your system, as we discussed in week 3, one of its primary goals is to blend in or hide.

The malware we’ve seen so far has tried to blend in by choosing names which could plausibly sound like system process; or tried to hide its actions by deleting its installer files and trying to keep a low footprint on the machine.

But the other way to hide is to trick the operating system into lying about what’s on the system. If you can’t see the malware process running in process explorer, and you can’t see its files in on the filesystem, and you can’t see any of its registry keys, it might as well not be there.

We call these programs rootkits.

The primary method used by rootkits to hide themselves is called hooking, which is where the rootkit replaces some system call with its own code which filters itself out of the results. For example, one of the rootkits we analyzed this week hooked the NtQuerySystemInformation call to hide itself from the process list.

Rootkits primarily run in the OS kernel (at least partly), because the kernel is what’s responsible for giving other programs their view of the world; that the code they need to hook.

There are lots of potential places for hooks. Here are a few:

Tools

Before going any further, let’s take a break to talk about tools. The problem with a rootkit is that it runs in the kernel, so if you want to be able to poke at it and debug it, you have to be able to debug the kernel.

Agony

We explored SSDT hooking in great depth by analyzing a rootkit called Agony.

SSDT screenshot

Here’s a screenshot of the SSDT in Tuluka. The three red rows (er, including the highlighted blue one) have been modified to point and now point to code in C:\analyzer\wininit.sys.

If we look for C:\analyzer\wininit.sys in Explorer or the command prompt, it doesn’t seem to exist – that’s because Agony has hooked the NtQueryDirectoryFile call, which is responsible for enumerating the contents of a directory. Agony’s hook calls the original NtQueryDirectoryFile, filters out the wininit.sys entry, and returns the modified list.

This is a general pattern: pretty much any rootkit that doesn’t want to totally break the system has to eventually call back to the original system call. (Can you imagine how much chaos it would cause if all files on the system were invisible? I doubt you’d get past the boot screen.) This means that the rootkit has to remember the original value of the SSDT entry and store it somewhere in memory, which we can find by setting a breakpoint on the hook function address and tracing it until the rootkit makes a call back to the original function. Once we have that, we can patch the SSDT entry back to its original value and watch as wininit.sys mysteriously appears in file explorer.

Userspace hooking

Although I said rootkits usually infect the kernel, it’s actually possible to hook entirely in userspace (provided you’re running in kernel mode or with admin permissions so you can write to other processes’ address space).

How does that work? Well, when an executable is linked with a DLL – like, say, KERNEL32.DLL – any calls it makes to functions in KERNEL32.DLL are actually compiled into an indirect call like

mov esi, dword ptr [notepad!_imp__SendMessageW]
call esx

And somewhere in the exe there is a table of function pointers for _imp__SendMessageW and all the other calls it makes to that DLL.

00bd001234 notepad!_imp__SendMessageW dd 00000000
00bd001238 notepad!_imp__SetFocus     dd 00000000
and so on

When the program is loaded by the OS, the dynamic linker goes through and fixes all these function pointers to point to the actual addresses of the functions in KERNEL32.DLL (or whatever).

So! To hook these calls, all the rootkit has to do inject itself into the target program and overwrite these addresses to point to the rootkit’s malicious version of the function, similar to the SSDT table except it has to be done for every program instead of just once in the kernel.

Bootkits

Bootkits are even lower-level than rootkits. Rootkits attack the kernel, but bootkits go one step deeper and attack the bootloader. Since the bootloader runs even before the operating system kernel gets to run, this gives them even more control over the system.

It also helps them hide better, just by virtue of the fact that the malicious code is stored in the master boot record rather than a file in the filesystem, making it invisible to file-based antivirus scanners.

Boot sequence

  1. BIOS initializes the hardware
  2. BIOS calls code in Master Boot Record (MBR) sector of disk 0
  3. MBR loads bootloader from boot sector of the active partition
  4. bootloader run second stage bootloader from filesystem
  5. second bootloader does some stuff, presents a boot menu, whatever
  6. second bootloader hands control to the OS kernel

Bootkits can infect pretty much any one of these steps, but it’s pretty common to see them infecting the MBR.

The MBR consists of code, followed by the master partition table, followed by the marker FF AA. An MBR bootkit will typically copy sector 0 to some other unused sector and write its code to sector 0.

With VM support, WinDBG can actually attach early enough in the boot process to step through the MBR code, which is pretty cool.

Now that MBR is being phased out in favor of GPT, I’m not sure where that leaves bootkits. We’ll probably see more malware that infects the UEFI.

Homework

We were given a challenge this week to write a program that could enumerate all running processes, threads, modules (DLLs) in a process, and memory pages in a process.

I found two different Windows APIs to do that: PSAPI and “Tool Help”.

PSAPI is a windows library for manipulating processes. From what I can tell, it can enumerate processes and modules, but not threads.

Tool Help is a library which grew out of an earlier set of 16-bit APIs which provider helpers for debugging tools (hence “tool help”). It uses a snapshot-based approach in which you call Createtoolhelp32snapshot and then functions like Process32Next to traverse the snapshot. This offers a more coherent view of the system, but is also a lot slower.

There are also low-level calls like NtQuerySystemInformation, but these are either not documented, or documented as being unstable.

Acknowledgements

This post was based on lectures given by Aditya Kapoor of Intel Security.