Ring3 / Ring0 Rootkit Hook Detection 2/2


This article was actually planned to be posted the day after the first, however; I've not had much sleep the past few weeks, then I got sick, so it was very delayed. I'm pleased with how popular the previous article was, so in the future I plan to write more like this.  

In this part of the article i will be explaining some of the different ways to detect, bypass and remove hooks placed by malware.

Part 1

If you haven't read part 1 of this article, here it is: http://malwaretech.com/2013/09/ring3-ring0-rootkit-hook-detection-12.html

IAT Hooks

The Import Address Table (IAT) is a table of jumps "jmp dword ptr ds:[address]". Because functions in dlls change address, instead of calling a dll function directly, the application will make a call to the relevant jmp in its own jump table. When the application is executed the loader will place a pointer to each required dll function at the appropriate address in the IAT. 

Import Address Table
I'm not sure this actually helps
if a rootkit were to inject itself inside the application and modify the addresses in the IAT, it would be able to receive control every time a target function is called. 

Because the Export Address Table (EAT) of each dll remains intact, an application could easily bypass IAT hooks by just calling GetProcAddress to get the real address of each dll function. In order to prevent such a simple bypass, a rootkit would likely hook GetProcAddress/LdrGetProcedureAddress and use it to return fake addresses. These hooks can be bypassed by writing your own GetProcAddress implementation and using it to get the real function addresses.

Inline Hooks

Inline hooking is a method of receiving control when a function is called, but before the function has done its job. The flow of execution is redirected by modifying the first few (usually 5) bytes of a target function. A standard way to do this is to overwrite the first 5 bytes of the function with a jump to malicious code, the malicious code can then read the function arguments and do whatever it needs. If the malicious code requires results from the original function (the one it hooked): it may call the function by executing the 5 bytes that were overwritten then jump 5 bytes into the original function, which will miss the malicious jump/call to avoid infinite recursion/redirection.

inline function hooking
Example of an inline hook jumping to malicious code then executing the original function
Bypass / Detection (Ring 3)
In usermode inline hooks are usually place inside functions that are exported by a dll. The most accurate way to detect and bypass these hooks would be to compare each dll against the original code. First a program would need to get a list of each dll that is loaded, find the original file and read it, align and load the sections into memory then perform base relocation. Once the new copy of the dll is loaded into memory, the application can walk the export address table and compare each function vs that in the original dll. In order to bypass hooks, an application can then either replace the overwritten code using the code from the newly loaded dll, alternatively, it could resolve imports in the newly loaded dll and use it instead (be aware that some dlls will not work if more than 1 instance is loaded). 

This method of bypassing dll hooks practically involves writing your own implementation of LoadLibrary, it's really not for the beginners or faint-hearted. As much as I would like to post the code to do this, I won't, because it can (and will) be used by scriptkiddies to bypass usermode antivirus sandboxes or fight with other rootkits. 
(We can also use manual dll loading to detect / fix EAT hooks, I won't go into this in detail as EAT hooks are very uncommon). 

Bypass / Detection (Ring 0)
In kernel mode, inter-modular jumps are a lot more rare. Hooks in ntoskrnl can usually be detected by disassembling each instruction in each function, then looking for jumps or calls that point outside of ntoskrnl (into driver bodies, etc). It is also possible to use the same method explained for usermode hook detection: a driver could read each ntoskrnl module from disk, load it into memory and compare the instructions against the original. 

For inline hooks within drivers, scanning for jmp / call instructions that point outside of the driver body is much more likely to result in false positives, however, non-standard drivers that are the target of jumps / calls inside standard kernel drivers should raise a red flag. It is also possible to read drivers from disk. As drivers generally do not export many functions and IRP major function pointers are only initialized at runtime, it would probably be required that you compare the entire code section of the original and new driver. It is important to note that relative calls / jumps are susceptible to changes during relocation, this means that there will naturally be some differences between the original and new driver, however both relative calls / jumps should point to the same place. 

SSDT Hooks

The System Serivce Dispatch Table (SSDT) is a table of pointers for various Zw/Nt functions, that are callable from usermode.  A malicious application can replace pointers in the SSDT with pointers to its own code.
Windows system call path
Example call paths for Nt/Zw functions

Detection (Ring 0)
All pointers in the SSDT should point to code within ntoskrnl, if any pointer is pointing outside of ntsokrnl it is likely hooked. It's possible a rootkit could modify ntoskrnl.exe (or one of the related modules) in memory and slip some code into an empty space, in which case the pointer would still point to within ntoskrnl. As far as I'm aware, functions starting with "Zw" are intercepted by SSDT hooks, but those beginning with "Nt" are not, therefore an application should be able to detect SSDT hooks by comparing Nt* function addresses with the equivalent pointer in the SSDT.

A simple way to bypass SSDT hooks would be by calling only Nt* functions instead of the Zw* equivalent. It is also possible to find the original SSDT by loading ntoskrnl.exe (this can be done easily with LoadLibraryEx in usermode) then finding the export "KeServiceDescriptorTable" and using it to calculate the offset of KiServiceTable within the disk image (Usermode applications can use NtQuerySystemInformation to get the kernel base address) , a kernel driver is required to replace the SSDT.


SYSENTER_EIP points to the code to be executed when the SYSENTER instruction is used. Usermode applications use SYSENTER to transition into kernel mode and call a kernel function (Those beginning with Nt/Zw), usually it would point to KiFastCallEntry, but can be replaced in order to hook all usermode calls to kernel functions.

Detection / Bypass
SYSENTER_EIP hooking does not effect kernel mode drivers, and cannot be bypassed from usermode. In order to allow usermode applications to bypass this hook, a kernel driver must set SYSENTER_EIP to its original value (KiFastCallEntry), this can be done using the WRMSR instruction, however because KiFastCallEntry is not exported by ntoskrnl, getting the address could be tricky.

IRP Major Function Hook

The driver object of each driver contains a table of 28 function pointer, these pointer are to be called by other drivers via IoCallDriver or alternative means, the pointers correspond to operations such as read/write (IRP_MJ_READ/IRP_MJ_WRITE). These pointers can easily be replace by another driver.

Generally all IRP major function pointers for a driver should point to code within the driver's address space, this is not always the case, but is a good start to identifying malicious drivers which have redirected the IRP major functions of legitimate drivers to their own code.

Due to IRP major function pointers being initialized from withing the driver entry point (during runtime), it's not really possible to get the original values by reading the original driver from disk, there are also issues with loading a new copy of the driver due to collisions. The only way I can think of for bypassing these sorts of hooks would be calling the lower driver (Drivers are generally stacked and the top driver passes the data to the driver below and so on, if the lowest driver isn't hooked, an application could just send the request directly to the lowest driver).


I can't think of what to write for a conclusion, so here's a picture of a man being chased by a seal.

Seal of disapproval



Hi. I’m a 22 year old programmer and security enthusiast currently living in the UK, my skills include: Malware Analysis, Reverse Engineering, and Windows Internals. I also have experience programming in the following languages: C/C++, ASM, PHP, Python, C#, Objective-C, as well as a few web scripting languages. Hope you enjoy the blog!

Email: admin@malwaretech.com

    Blogger Comment


  1. Firstly about Inline Hooks. If non exported functions are hooked (E.g. the supposed Trusteer Bypass currently for sale hooks internal functions and LdrpCheckForLoadedDll) you won't catch that just by checking whether the exported functions are hooked.
    Also hooks may appear at any place in the function (I think gataka/Zutick modified the end of WinVerifyTrust()) so it may not be enough to just check the first X bytes.

    Then you left out the information about the KiFastSystemCall hook (Or a Heavens Gate hook for WOW64).
    The normal path (Just quickly again for demonstration purposes) is shown here: http://waleedassar.blogspot.de/2012/07/native-x86-user-mode-system-calls.html
    The path from the Nt* to the _KUSER_SHARED_DATA->SystemCall is checked by the unhooking of inline hooks.
    As KiFastSystemCall looks the same on all versions it can be checked with a simple memcmp().

    Sadly this doesn't even start to cover all possible hook methods.
    vtable pointer replacement (As Chrome is hooked), exception based (maybe a little slow but still possible), filter drivers (Not exactly a hook per say..), ...

    1. I am aware non-exported functions are hooked by some (very few) bits of malware, in order to detect/remove those hooks, you would likely have to compare the code section of both dlls, being careful to avoid absolute jumps/calls an such, It's a minefield and beyond the scope of this article which is meant to show a few common hook methods.

      As for KiFastSystemCall hook, _KUSER_SHARED_DATA is not writable so this isn't hookable on xp SP0 & SP1, On later windows systems it's just a pointer to the KiFastSystemCall Export, in which case it will be unhooked by the method I explained in this article.
      Please don't refer to the wow32reserved field as heavens gate, it is a term used in the malware scene to describe a method of breaking out of the wow64 layer by abusing the 0x33 segment, it shouldn't be used as an umbrella term to describe the normal and proper operation of wow64 (a jump to CpupReturnFromSimulatedCode). I have not featured it in my articles because it clearly states that I am talking about 32-bit systems, I will do a whole other article for 64-bit systems because they introduce new hooking methods and prevent old.

      As I said this article is just a quick introduction to hooking, it was not meant to cover all hooking methods, just the common ones. I don't even know any software that uses a vtable and is targeted by rootkits. the PRIOMethods field of chrome has nothing to do with rootkits and is only targeted by datastealers / banking malware, I'll save this for another article about detecting infostealers maybe? Also filter drivers are not hooks, they are completely legitimate and commonly used feature of the operating system, they can be easily bypassed by just calling the lower driver (again, beyond the scope of the article). Exception hooks are useless, either you have to overwrite the code with an instruction to trigger an exception (just glorified inline hooking), or you have to use debug registers which are in the context of the current thread in usermode so hooking would require you to hijack every thread in every process you wish to hook, unless you're operating in kernel mode, in which case you can use the global flag. Kernelmode debug register hooking would make for a nice separate article, but it doesn't really fit in here.

      Thanks for your feedback (I'm not sure if it was feedback of flexing of intellectual muscles), but thanks anyway :D

  2. Nice Intro TM.
    Any thoughts about bypassing EMET Hooks .
    They Hook Function Prologue + Random Bytes
    (They are using EAF ,also i assume you are at beginning of shellcode ,so can't use any API )

    1. I've never looked into EMET, I might take a look when i have some free time.