In this article we will talk about a few hooking techniques used by antivirus software. For the purpose of this analysis the antivirus chosen will be Kaspersky (http://www.kaspersky.com/it/trials PURE 3.0 Total Security), we will deal with various hooking techniques used both at user and kernel mode.
The reference operating system will be Windows 7 Professional 32-bit.
The image below shows a summary of the techniques we will analyze in this article.
In order we will deal with:
- User-space Processes
- Inline hooking
- IAT and EAT
- Virtual Address Descriptor
- Hidden registry entry
- TDI HOOKING
We start with a brief introduction by looking at the processes of kaspersky in user-space.
The main userspace process is “avp.exe” which is instantiated twice: one instance runs under the privileges of NT AUTHORITY\SYSTEM the other is used for the user interface (avp.exe user). The other process: ProtectedObjectsSrv.exe acts as encryption service.
We will focus on the last one: it runs as a background Windows service called “CSObjectsSrv” (CryptoStorage control service).
InfoWatch CryptoStorage is intended for centralized protection of confidential data using cryptographic methods during data storage and processing. The product is based on the integrative approach to data protection. The functional capabilities include file and folder encryption using resilient encryption algorithms, an option to create special data storage objects – the container files, logical disks and flash drives and differentiation of access rights to the protected objects.
InfoWatch CryptoStorage protects against unauthorised access to the RAM content dumped to the hard disk in case of hibernation, crash dumps or data coming from temporary files and swap files. More information about this topic can be found at http://infowatch.com
Let’s now look at all the hooking methods starting from the userland.
To find API hooks in User-mode, we can use use the apihooks plugin of Volatility
The processes involved in the inline hooking are:
- avp.exe[pid1] NT Authority\SYSTEM
avp.exe one is for the protection service (avp.exe system), the other one, as already said, is for the user interface (avp.exe user). The service requires full system access, that’s why it runs as System.
Let’s examine svchost.exe, this process is subject to inline hooking, in fact, at address 0x7453b5dd we can find a jump that leads to wfapigp.dll, which resides at location 0x74586218.
Using Volatility we can dump the process with pid 1560 and using IDA we can quickly disassemble the dump and double check for the presence of the hook at location 0x7453b5dd.
Scrolling again the report generated by Volatility, we can see that the process avp.exe uses different modules (image below)
The process avp.exe makes use of different hooking techniques, let’s try and investigate the following module:
In this case the hooking occurs inside ntdll.dll, the function hooked is ZwProtectVirtualMemory, which is located at 0x77015f18, checking with IDA we can confirm the presence of a jump at 0x71722066, which is the location in which the hooking module ushata.dll is loaded.
In here we can see how avp.exe loads ushdata.dll using a standard LoadLibraryEx()
In this context, can occur something similar to what is described in the following code, basically:
hDLL = LoadLibraryExW(L"USHATA.DLL", null, 8); lpGetNumber = (LPGETNUMBER)GetProcAddress((HMODULE)hDLL, "InitHooks");
Let’s see what are the methods exported by the module ushdata.dll:
So the exported functions of ushdata.dll module are InitHooks, SetClientVerdict and SetShuttingDownHint.
In general from the report generated by volatility, the modules and functions that are subject to inline hooking, are:
- C:\Program Files\Kaspersky Lab\Kaspersky PURE 3.0\avp.exe[pid1] and [pid2] C:\Windows\SYSTEM32\ntdll.dll (FUNCTIONS ntdll.dll!NtProtectVirtualMemory e ntdll.dll!ZwProtectVirtualMemory)
- C:\Program Files\Kaspersky Lab\Kaspersky PURE 3.0\avp.exe[pid1] and [pid2] ntdll.dll!NtProtectVirtualMemory JMP 70B12066 C:\Program Files\Kaspersky Lab\Kaspersky PURE 3.0\ushata.dll
- C:\Program Files\Kaspersky Lab\Kaspersky PURE 3.0\avp.exe[pid1] and [pid2] C:\Windows\system32\kernel32.dll
- C:\Program Files\Kaspersky Lab\Kaspersky PURE 3.0\avp.exe[pid1] and [pid2] C:\Windows\system32\ole32.dll
- C:\Program Files\Kaspersky Lab\Kaspersky PURE 3.0\avp.exe[pid1] and [pid2] USER32.dll!NotifyWinEvent + 6AE
The undocumented used is NtProtectVirtualMemory, which will allows to set the page protection and returns the old protection (http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html).
IAT and EAT
Continuing the analysis of the hooking in User-space, we can still make use of Volatility to detect the hooking performed on both the IAT and EAT. We will find only two functions: one for the IAT hooking and another one for EAT hooking.
Referring to the image at the top (EAT Hook), we conclude that the affected module is kernel32.dll, specifically the CreateThread function, as shown in the figure below:
Virtual Address Descriptor
We can open a brief parenthesis on the kernel data structure that takes care of registering the use of virtual addresses in a process, it is called Process VAD (Virtual Address Descriptor). For each process the memory manager maintains a set of VADs, which contain information on the address space of the process itself. Reconstructing the VAD tree allows for the reconstruction of the process with all of its mapped files.
Here’s an example:
The protection field highlighted in red is extracted from the flProtect parameter passed as input to the VirtualAlloc API (http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887%28v=vs.85%29.aspx).
You can also use the windbg command !vad to display the VADs of a given process:
kd> !process 0 1 avp.exe kd> !vad [address of VadRoot]
Hidden registry entry
A hive is a database of registry values divided in logical groups of keys and subkeys; the values in the registry, in turn, have a number of supporting files containing backups of their data.
These files are located mainly in the %SystemRoot% \System32\Config and are created/updated each time the user logs in.
Here is a table showing the standard hive with the respective files (http://msdn.microsoft.com/en-us/library/windows/desktop/ms724877%28v=vs.85%29.aspx):
|Registry hive||Supporting files|
|HKEY_CURRENT_CONFIG||System, System.alt, System.log, System.sav|
|HKEY_LOCAL_MACHINE\SAM||Sam, Sam.log, Sam.sav|
|HKEY_LOCAL_MACHINE\Security||Security, Security.log, Security.sav|
|HKEY_LOCAL_MACHINE\Software||Software, Software.log, Software.sav|
|HKEY_LOCAL_MACHINE\System||System, System.alt, System.log, System.sav|
|HKEY_USERS\.DEFAULT||Default, Default.log, Default.sav|
- !reg hivelist
It displays a list of all hives in the system, then we select the Hive Address of SYSTEM using the following command:
- !reg openkeys “Hive Address of SYSTEM”
It displays all open keys in a hive:
I also used the above the command:
- !reg cellIndex “HiveAddress of SYSTEM” “Index”
It displays the virtual address for a cell in a hive, Index specifies the cell index.
Using the command:
- !reg valuelist “HiveAddress of SYSTEM” KeyNodeAddress
- !reg kvalue Address
Address specifies the address of the value, finally, we can reuse the cell index with the new index of the cell and dc command (it displays double word values, 4 bytes, and ASCII characters)
We can achieve the same result using Volatility, let’s briefly show how to do that using the command hivelist:
And once again we come across the KLIF service:
Let’s now move to the analysis of the hooking at kernel space, in particular we will deal with: IDT, SSDT and IRP hooking.
System calls are used to traverse the barrier that exists between user space and kernel space, for this task the IDT is used, the IDT is the table that implements the interrupt vector table, in turn used to dispatch the interrupts. The IDT is composed, internally, of a data structure of 8 bytes entries, which describes how the interrupt must be managed (x86 CPU). In the picture below you can see the relationship between IDT and the instruction “int 2e” that is normally used to initiate a system call, even though on recent CPUs the SYSENTER instruction is used a replacement.
The goal of IDT hooking is to hook any function already registered for a given interrupt.
Let’s see if the software in question uses these techniques, so we can analyze it with windbg and the command !idt
Now let’s run the same check with volatility,using the command idt, we will see that the two results match:
In the selected row we can see, from the column Value, that the address matches the one analyzed with windbg, also in the column Module we can notice the presence of ntoskrnl.exe, which shows that there are no hooks in place.
The System Service Descriptor Table (SSDT) contains pointers to kernel mode functions provided by the kernel executable module (ntoskrnl.exe). There is a second SSDT called shadow SSDT table, that instead stores the native functions provided by the GUI module win32k.sys. It ‘important to make an observation: when a system call reaches ntdll.dll, EAX will contain the hexadecimal value corresponding to the index into the SSDT of the function to be called, and immediately after the command int 2E the control is transferred to KiSystemService:
We’re going to check the contents of the two tables for the software in question, it is possible to analyze the memory with Volatility or WinDBG:
From the figure on the left hand side we can see the memory belonging to klif.sys at address 0x8C836000. On the right hand side we have the output of the command:
- kd> dps KiServiceTable l11C
that show the presence of SSDT hooks from the klif module.
We can also investigate KeServiceDescriptorTable and KeServiceDescriptorTableShadow.
The module klif.sys seems to be the one that deals with SSDT and SSDT Shadow hooking.
Let’s look more closely at klif.sys, the first function we’re going to inspect is “PsSetLoadImageNotifyRoutine” that registers a driver-supplied callback that is subsequently notified whenever an image is loaded (or mapped into memory).
In the image below we see a series of two calls, the first one calls ZwQuerySystemInformation and then the second one invokes KeServiceDescriptorTable, which is the classical sequence used to install an SSDT hook.
SSDT hooking is not performed on 64-bits systems because the Kernel Patch Protection (KPP), also known as Patchguard, protects this structure.
It is anyway possible to use a mini-filter driver as a workaround.
And indeed that’s what we have, a minifilter driver:
A mini-filter driver must specify an altitude value from an altitude range that represents a load order group.
A minifilter driver’s altitude ensures that the instance of the minifilter driver is always loaded at the appropriate location related to other minifilter driver instances, and it determines the order in which the filter manager calls the minifilter driver to handle I/O. Altitudes are allocated and managed by Microsoft itself.
The following figure shows a simplified I/O stack with the filter manager and three minifilter drivers.
In our case we have
|Load order group||Altitude range||Description|
|FSFilter Anti-Virus||320000-329999||This group includes filter drivers that detect and disinfect viruses during file I/O.|
An IRP is an object used to communicate between all the different layers of a driver stack (http://msdn.microsoft.com/en-us/library/windows/hardware/hh439632%28v=vs.85%29.aspx).
For each driver, there are some major functions that receive IRPs to process. These major functions are kept inside a table of pointers.
This driver contains the following functions:
- Driver Entry
- Dispatch routine
The Driver Object structure is presented as follows:
By default the I/O manager does point the DriverInit at the DriverEntry(). The array MajorFunction is essentially a table, each driver populates this table with function pointers, called Dispatch routine. The main data structures used by the kernel driver majors are the IRPs. Some of the most used are: IRP_MJ_CREATE, IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_DEVICE_CONTROL.
We can sniff the traffic IRP to the driver klif using Irp Tracker:
From the red boxes we can see the two processes: avp.exe and svchost.exe calling the NtFsControlFile API (which sends a control code directly to the driver klif)
The kernel module responsible for TDI HOOKING is kltdi.sys, we can look for it inside the structure LDR_DATA_TABLE_ENTRY, pointed by PsLoadedModuleList. By running the modules command in Volatility we will get:
- kltdi.sys 0x8cb7a000 0x9000 \SystemRoot\system32\DRIVERS\kltdi.sys
At this point we can check to see if there is something unusual for the driver “tdx“:
As we can see this is a list of devices that belongs to \Driver\tdx and in each device the module kltdi.sys is present, loaded at the address 0x8cb57000. Using Windbg we can check what happens at the location where tdx.sys is loaded:
- tdx.sys 0x8cb57000 0x17000 \SystemRoot\system32\DRIVERS\tdx.sys
We only see the location of the major Function IRP_MJ_CREATE:
0 IRP_MJ_CREATE 0x8cb62faa tdx.sys
Let’s set a breakpoint at the address 0x8cb62faa, this is the location where the major function IRP_MJ_CREATE of the module tdx.sys. After then we can start a ping and the debugger will immediately break at the address we are expecting, thus confirming the existence of a TDI hook.
We can see in the call stack the presence of the module kltdi.sys, let’s focus on the function kltdi+0x4803:
The IoCallDriver routine sends an IRP to the driver associated with a specified device object, it accepts two input parameters DEVICE_OBJECT*an IRP*
NTSTATUS IoCallDriver( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp );
Quoting the Microsoft’s documentation:
An IRP passed in a call to IoCallDriver becomes inaccessible to the higher-level driver, unless the higher-level driver has called IoSetCompletionRoutine to set up an IoCompletion routine for the IRP. If it has, the IRP input to the IoCompletion routine has its I/O status block set by the lower drivers, and all lower-level drivers’ I/O stack locations are filled with zeros.
Now let’s take a look at the kernel callbacks, once again with Volatility:
Thread creation (PsSetCreateThreadNotifyRoutine):
Shutdown callbacks (IoRegisterShutdownNotification):
There are several addresses for the callbacks, but we want to point out the presence of kernel module kl1.sys, so let’s dig deeper:
kl1.sys is is a boot start driver, in the image below you can see the presence (in the DriverEntry routine) of the API IoRegisterBootDriverReinitialization.
IoRegisterBootDriverReinitialization() function registers a callback routine that will be called whenever all boot drivers have been loaded. This routine is typically used in filters that attach on non-Plug-and-Play devices, and thus, they cannot rely on AddDevice() function calling to be notified that a new device was created (check this example for more details http://driverentry.com.br/en/blog/?p=261).
Now let’s also look at the Driver Dispatch Routines:
As you can see, all Driver Dispatch Routines point to the same address, kl1+0x32f0
The article was written for educational purposes, the analysis is not detailed and many things have been analysed very quickly, also there is still research to be done on the network part.
A big thanks goes to Quequero.
Malware Analysts Cookbook and DVD: Tools and Techniques for Fighting Malicious Code