Uninformed: Informative Information for the Uninformed

Vol 3» 2006.Jan


SSDT

One of the areas most notorious for being hooked by third-party drivers is the System Service Descriptor Table, also known as the SSDT. This table contains information about the service tables that are used by the operating for dispatching system calls. On Windows x64 kernels, nt!KeServiceDescriptorTable conveys the address of the actual dispatch table and the number of entries in the dispatch table for the native system call interface. In this case, the actual dispatch table is stored as an array of relative offsets in nt!KiServiceTable. The offsets are relative to the array itself using relative addressing. To obtain the absolute address of system service routines, the following approach can be used:

lkd> u dwo(nt!KiServiceTable)+nt!KiServiceTable L1
nt!NtMapUserPhysicalPagesScatter:
fffff800`013728b0 488bc4           mov     rax,rsp
lkd> u dwo(nt!KiServiceTable+4)+nt!KiServiceTable L1
nt!NtWaitForSingleObject:
fffff800`012b83a0 4c89442418       mov     [rsp+0x18],r8

The fact that the dispatch table now contains an array of relative addresses is one hurdle that driver developers who intend to port system call hooking code from 32-bit platforms to the x64 kernel will have to overcome. One solution to the relative address problem is fairly simple. There are plenty of places within the 2 GB of relative addressable memory that a trampoline could be placed for a hook routine. For instance, there is often alignment padding between symbols. This approach is rather hackish and it depends on the fact that PatchGuard is forcibly disabled. However, there are also other, more elegant approaches to accomplishing this that require neither.

As far as protecting the system service table is concerned, PatchGuard protects both the native system service dispatch table stored in nt!KiServiceTable as well as the nt!KeServiceDescriptorTable structure itself. This is done by making use of the nt!PgCreateBlockChecksumSubContext routine that was mentioned in the section on system images ([*]). The following code shows how the block checksum routine is called for both items:

PgCreateBlockChecksumSubContext(
    ParentContext,
    0,
    KeServiceDescriptorTable->DispatchTable, // KiServiceTable
    KiServiceLimit * sizeof(ULONG),
    0,
    NULL);

PgCreateBlockChecksumSubContext(
    ParentContext,
    0,
    &KeServiceDescriptorTable,
    0x20,
    0,
    NULL);

The reason the nt!KeServiceDescriptorTable structure is also protected is to prevent the modification of the attribute that points to the actual dispatch table.