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.
Bypass / Detection (Ring 3)
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.
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.Detection
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).