In his blog, Rachit described a malware sample that prevented live anti-rootkit tools from detecting the malware's IRP hooks. To summarize the malware's technique, it did the following:
1) Hook KiDebugRoutine (see skape & Skywing's explanation) by overwriting the kernel global with a pointer to the rootkit code. For example code showing how to do this, see the DbgHookKiDebugRoutine function of the ngdbg project.
2) Set an on-read hardware breakpoint which triggers on attempts to read the memory of the hooked driver's dispatch table
3) Return real or fake data from the KiDebugRoutine hook...thus allowing the rootkit to mask its memory modifications when checked by anti-rootkit tools
The purpose of this blog is to discuss how you can detect the hardware breakpoints that allow this malware to "filter" access to the protected memory. Volatility's threads command can detect hardware breakpoints, but only thread-specific breakpoints using the set of registers located at _ETHREAD.Tcb.TrapFrame. The rootkit we're discussing uses a different set of processor (or affinity)-specific registers.
Take a look at the following function in the rootkit's driver that actually sets the breakpoints.
And here's a description of what its doing:1) Uses KeQueryActiveProcessors to find out how many processors the victim machine has
2) Enters a loop that executes once for each processor, up to 32 processors
3) Calls KeSetSystemAffinityThread to "switch" processors on which the current thread executes (so that the thread can make processor-specific changes)
4) Finds the processor's KPCR structure by referencing fs:[1Ch] which points to KPCR.SelfPcr (a self-referencing member of the KPCR)
5) Sets the KPCR.DebugActive member to 1
6) Move the debug flags 0F0703h into the dr7 control register (see mov dr7, ebx)
7) Move the address of the protected memory into the dr0 register (see mov dr0, ebx)
8) If OS MajorVersion is less than 6 (i.e. XP or earlier), then it also writes to the processor-specific set of special registers. Note: writing to dr0 and dr7 on XP will update the special registers anyway.
Based on the disassembly and these few minutes of rootkit reverse engineering, we can now build a proof-of-concept Volatility plugin that detects the breakpoints.
import volatility.commands as commands
import volatility.utils as utils
import volatility.obj as obj
class TestCommand(commands.command):
"""Check the kernel's debug registers"""
def calculate(self):
addr_space = utils.load_as(self._config)
volmagic = obj.Object('VOLATILITY_MAGIC', 0x0, addr_space)
kpcr = obj.Object("_KPCR", volmagic.KPCR.v(), addr_space)
if kpcr.DebugActive:
if kpcr.Prcb.ProcessorState.SpecialRegisters.KernelDr0 != 0:
print '[*] Infection Possible!'
def render_text(self, outfd, data):
passJust drop that code into a file such as volatility/plugins/testcommand.py and off you go. On an infected machine, you should see:$ python vol.py -f memory.dmp testcommand
[*] infection possible!
The code we've written here is simply proof of concept, just to show how easy it is to access members of different data structures using Volatility's object model. The main weaknesses in the current code, should you want to design a more useful one are:
1) It only checks one processor instead of the maximum 32. For proof of concept purposes, it doesn't really matter which processor is checked since the rootkit "infects" them all, but in real life you'd want to be more robust.
2) It only checks if dr0 is set, but dr1 dr2 and dr3 could be set by the rootkit instead. Maybe different variants of the same malware use different registers - you never know.
2 comments:
How does kpcr.Prcb.ProcessorState.SpecialRegisters.KernelDr0 get updated? Dr0 and dr7 are on the logical processor and not in main computer memory. kpcr.Prcb.ProcessorState.SpecialRegisters.KernelDr0 will not automatically be affected by the mov instruction (until the next context shift).
Hmm, well from the time the malware installs and when you dump memory, a context shift/switch would have occurred, so the memory dump would acquire the modified KernelDr0...right?
Post a Comment