Kernel Object Hooking Rootkits (KOH Rootkits)
By: Greg Hoglund
Summary
Rootkits and Rootkit-detectors are locked in co-evolution. Anti-rootkit technology can now easily detect common techniques such as call hooking and detours. In this article I present yet another evolutionary step for offensive rootkits called ‘Kernel Object Hooking’ (KOH). KOH is an outgrowth of DKOM, but unlike DKOM which relies only on data-state changes, KOH modifies control flow using call-hooks. The KOH technique is easy to use and evades all current rootkit detection software. The techniques described herein will reveal current attack trends and assist those who defend networks to take the next step in rootkit detection.
General approaches for Subversive Code Execution (SCE)
Rootkits require subversive code to execute. Even purely data-based rootkit approaches (see DKOM) still require some subversive code to run at least once. Most rootkit approaches require subversive code to execute many times over, usually in response to a system event (such as the listing of the process table).
Methods to perform SCE:
There are two approaches to executing subversive code:
- redirection of existing code paths, and
- creation of new code paths.
Redirection of code paths requires techniques such as “Call hooking” and “Detours”. Creation of new control paths usually means the loading of new device drivers or the injection and execution of new DLL’s. In either case, subversive code is allowed to execute because the operating system has followed a code path with the CPU.
Redirection SCE:
There are two general approaches for redirection of existing code paths:
- Call hooking, and
- Detours.
Call hooking refers to the overwrite of existing function points in tables, such as the SSDT or IAT, which then results in a subverted pointer being dereferenced into a rootkit code block. Detours are slightly different in that they overwrite code with actual jump instructions which branch into a rootkit code block. In both cases, the result is the same – code that otherwise would have executed properly is now caused to branch into a subversive region.
New control paths SCE:
When creating a driver or new DLL, no redirection is required because the rootkit simply tells the operating system to begin executing a new code block. The rootkit’s code block doesn’t require a branch out-of-scope to execute – the rootkit’s code block itself is considered a legitimate operational part of the operating system.
Detection of SCE:
Detection of these techniques has been fairly easy for rootkit-defense programs. In the case of call hooks, the tables that are being subverted are easy to integrity check. In the case of detours, the code itself clearly branches in an unusual way, and usually to a location that is out of scope for the current module.
Kernel Objects and Control Flows
Kernel objects are simply data structures maintained by the kernel. They are usually in linked lists, arrays, or referenced from arrays of pointers. At any rate, there are many types of kernel structures and many instantiations of these structures. They exist to manage everything from IRPs (I/O) to device driver registrations, to callbacks and callchains. Many of these objects are directly involved in control flow – the objects themselves contain function pointers. Call hooking a kernel object is almost as easy as hooking the SSDT or IAT.
Examples include:
-IO Completion routines
-Kernel or User APC Queues, found via the thread structure
-- APC’s KernelRoutine pointer
-- APC’s NormalRoutine pointer
-- APC’s RundownRoutine pointer
-Threads saved context
-Protocol Characteristics Structure (managed by NDIS)
-Driver Object callback pointers
-- Driver Unload routine
-- IOCTL handler
-Callbacks registered for when an object is deleted (driver framework)
-Timers
-DPC Kernel Objects (found in non paged memory)
-DPC’s scheduled from ISR’s
For example, a DPC looks like this:
typedef struct {
SHORT Type;
UCHAR Number;
UCHAR Importance;
LIST_ENTRY DpcListEntry;
PKDEFERRED_ROUTINE DeferredRoutine;
PVOID DeferredContext;
PVOID SystemArgument1;
PVOID SystemArgument2;
PULONG Lock;
} KDPC, *PKDPC;
The pointer you want to subvert is DeferredRoutine.
Note: each processor has it’s own DPC queue. The Processor Region Control Block for each processor has a pointer to the DPC Queue’s head. It is worth noting that DPC’s can be scheduled to run on particular processors. In fact, this is one way to ensure multi-processor safe IDT hooking. See KeSetTargetProcessorDPC().
-The IP Filter driver hook (set via PF_SET_EXTENSION_HOOK_INFO )
-Exception handler callback functions
-Data buffer callback routines (for example, see 1394 drivers)
-TLS callback routines (see AddressOfCallBacks)
-All kinds of WDM driver stuff
-Plug and Play notifications
The list goes on and on….
All of these objects (and more) can be modified to cause a subversive control flow branch. The compelling thing for rootkit authors is that already existing (and easy to use) techniques for call hooking can be applied successfully against any of these instantiated objects.
The barrier to entry
If the call-hooking technique is so easy to apply against kernel objects, why aren’t more people hooking these things? The short answer is: some people are. The point I would like to make in this paper is that most if not all rootkit detection solutions are completely overlooking these objects. For rootkit authors, this is good news – we can once again dance across the kernel in glee and song.
The barrier to entry for KOH is learning to parse these instantiated objects. This problem exists for both rootkit authors and the anti-rootkit crowd. Many of these kernel objects are undocumented. It can be difficult to find them in memory to begin with. Scanning techniques can be used successfully, and looking for code that dereferences an object of interest is a good start. But, rootkit authors only need to find one or two dependable yet obscure places to hook – while the anti-rootkiters have to develop broad general support for integrity checking the operating system. This is a major problem for the anti-rootkit author – to properly integrity check the Windows OS would require complete understanding of the OS. In other words you have to be Microsoft, or one of their trusted partners.
Where does this leave the anti-rootkit crowd? Well, pretty much where they have always been – waiting for someone to post their rootkit on rootkit.com or elsewhere, and only then developing a “one shot” fix to detect that one rootkit. Again, in a constant state of catch-up.
Conclusion
The threat of KOH rootkits isn’t altogether new – the technique of call hooking is the oldest in the book – and KOH-like modifications have been made in the past and distributed in firewall-bypassing rootkits. KOH underscores a deeper problem – that an existing once easy-to-spot technique is made very obscure by virtue of being applied elsewhere in the kernel. And, the kernel is a big place. For those “true believers” in rootkit detection, I’m sorry to tell you this – the hardline conclusion here is that rootkit detection just isn’t going to work. To stop rootkits, you need to keep untrusted code out of the kernel to begin with. This ongoing game of cat-and-mouse detection is a Big Lie – and it’s really really funny when your the one on the offensive side of the game.
-Greg