System Service Descriptor Table - SSDT
Last updated
Last updated
System Service Dispatch Table or SSDT, simply is an array of addresses to kernel routines for 32 bit operating systems or an array of relative offsets to the same routines for 64 bit operating systems.
SSDT is the first member of the Service Descriptor Table kernel memory structure as shown below:
SSDTs used to be hooked by AVs as well as rootkits that wanted to hide files, registry keys, network connections, etc. Microsoft introduced PatchGuard for x64 systems to fight SSDT modifications by BSOD'ing the system.
When a program in user space calls a function, say CreateFile
, eventually code execution is transfered to ntdll!NtCreateFile
and via a syscall to the kernel routine nt!NtCreateFile
.
Syscall is merely an index in the System Service Dispatch Table (SSDT) which contains an array of pointers for 32 bit OS'es (or relative offsets to the Service Dispatch Table for 64 bit OSes) to all critical system APIs like ZwCreateFile
, ZwOpenFile
and so on..
Below is a simplified diagram that shows how offsets in SSDT KiServiceTable
are converted to absolute addresses of corresponding kernel routines:
Effectively, syscalls and SSDT (KiServiceTable
) work togeher as a bridge between userland API calls and their corresponding kernel routines, allowing the kernel to know which routine should be executed for a given syscall that originated in the user space.
In WinDBG, we can check the Service Descriptor Table structure KeServiceDescriptorTable
as shown below. Note that the first member is recognized as KiServiceTable
- this is a pointer to the SSDT itself - the dispatch table (or simply an array) containing all those pointers/offsets:
Let's try and print out a couple of values from the SSDT:
As mentioned earlier, on x64 which is what I'm running in my lab, SSDT contains relative offsets to kernel routines. In order to get the absolute address for a given offset, the following formula needs to be applied:
Using the above formula and the first offset fd9007c4
we got from the KiServiceTable
, we can work out that this offset is pointing to nt!NtAccessCheck
:
We can confirm it if we try to disassemble the nt!NtAccessCheck
- routine addresses (fffff801`91dcb4ec) and first instructions (mov r11, rsp) of the above and below commands match:
If we refer back to the original drawing on how SSDT offsets are converted to absolute addresses, we can redraw it with specific values for syscall 0x1:
As a simple exercise, given a known syscall number, we can try to work out what kernel routine will be called once that syscall is issued. Let's load the debugging symbols for ntdll
module:
Let's now find the syscall for ntdll!NtCreateFile
:
...we can see the syscall is 0x55:
Offsets in the KiServiceTable
are 4 bytes in size, so we can work out the offset for syscall 0x55 by looking into the value the KiServiceTable
holds at position 0x55:
We see from the above that the offset for NtCreateFile
is 01fa3007
. Using the formula discussed previously for working out the absolute routine address, we confirm that we're looking at the nt!tCreateFile
kernel routine that will be called once ntdll!NtCreateFile
issues the 0x55 syscall:
Let's redraw the earlier diagram once more for the syscall 0x55 for ntdll!NtCreateFile
:
As another exercise, we could loop through all items in the service dispatch table and print absolute addresses for all routines defined in the dispatch table:
Nice, but not very human readable. We can update the loop a bit and print out the API names associated with those absolute addresses: