From: http://www.volynkin.com/sdts.htm
In Windows NT based operating system every system call originated in user mode which is to be processed by the system抯 kernel must go through the gate to the kernel itself where it would be dispatched and executed. This gate is an interrupt INT 2Eh. While on the user side library ntdll.dll handles a system call, after passing the gate ntoskrnl.exe takes over invoking an internal function KiSystemService() and passing the original system call into it. Then, KiSystemService uses a lookup table for the information on which Native API call to assign the original call to. This lookup table is called System Service Table (SST), it has the following structure:
typedef NTSTATUS (NTAPI * NTPROC) ();
typedef NTPROC * PNTPROC;
#define NTPROC_ sizeof (NTPROC)
typedef struct _SYSTEM_SERVICE_TABLE
{
PNTPROC ServiceTable;
PDWORD CounterTable;
DWORD ServiceLimit;
PBYTE ArgumentTable; }
SYSTEM_SERVICE_TABLE,
*PSYSTEM_SERVICE_TABLE,
**PP SYSTEM_SERVICE_TABLE;
System Service Table itself is a member of another structure called Service Descriptor Table:
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLE ntoskrnl;
SYSTEM_SERVICE_TABLE win32k;
SYSTEM_SERVICE_TABLE Table3;
SYSTEM_SERVICE_TABLE Table4;
}
SERVICE_DESCRIPTOR_TABLE,
*PSERVICE_DESCRIPTOR_TABLE,
**PPSERVICE_DESCRIPTOR_TABLE;
System Service Table structure is used by two different handles in the Kernel:

While KeServiceDescriptorTable is readily available for kernel mode drivers to access, KeServiceDescriptorTableShadow is not. Moreover, KeServiceDescriptorTable does not have the System Service Table for win32k calls support.
Unfortunately, different versions of Windows NT have these two tables placed in different order. For example, on Windows 2000, KeServiceDescriptorTableShadow follows immediately after KeServiceDescriptorTable, while on Windows XP it appears right before KeServiceDescriptorTable.
Several different ways have been proposed to obtain the correct address of the Shadow table. In this example I shall demonstrate an approach to obtain the KeServiceDescriptorTableShadow address on Windows 2000 and Windows XP (incl. SP1) operating systems.
The idea behind this method (originally introduced in the book Undocumented Windows NT) is to use a function called KeAddSystemServiceTable as an indicator. This function is used by win32k.sys and therefore it just has to address the Shadow table since KeServiceDescriptorTable does not support win32k calls. Another assumption to be made is that the first entry in both KeServiceDescriptorTable and KeServiceDescriptorTableShadow is the same. Since it is relatively simple to obtain the address of KeServiceDescriptorTable, we may try to compare its first entry with the one obtained from KeAddSystemServiceTable. If they match, we consider the corresponding address to be the correct address of KeServiceDescriptorTableShadow. There is no guaranty that the very first valid address, produced by KeAddSystemServiceTable will match, thus we have to go through several addresses to find the right one.
The following code shows a correct solution for obtaining a KeServiceDescriptorTableShadow address on windows XP, it will work for a kernel mode device driver:
struct SYS_SERVICE_TABLE {
void **ServiceTable;
unsigned long CounterTable;
unsigned long ServiceLimit;
void **ArgumentsTable;
};
extern struct SYS_SERVICE_TABLE *KeServiceDescriptorTable;
static struct SYS_SERVICE_TABLE * GetServiceDescriptorShadowTableAddress ();
__declspec(dllimport) KeAddSystemServiceTable (ULONG, ULONG, ULONG, ULONG, ULONG);
static struct SYS_SERVICE_TABLE * GetServiceDescriptorShadowTableAddress ()
{
unsigned char *check = (unsigned char*)KeAddSystemServiceTable;
int i;
struct SYS_SERVICE_TABLE *rc=0;
for (i=0; i<=99; i++) {
__try {
rc = *(struct SYS_SERVICE_TABLE**)check;
if (!MmIsAddressValid (rc) || (rc == KeServiceDescriptorTable)
|| (memcmp (rc, KeServiceDescriptorTable, sizeof (*rc)) != 0)) {
check++;
rc = 0;
}
} __except (EXCEPTION_EXECUTE_HANDLER) { rc = 0; }
if (rc)
break;
}
return rc;
}
Note, that if you by some reason failed to find the correct address, your number of iterations (100 in my example) was not enough. Try to increase it slightly, I would not recommend to have the number of iterations larger than 4096, if you failed again even with the number that high, there are definitely other problems, most likely the assumptions mentioned above are no longer true for the operation system you use.
To call this function, simply create a new pointer to System Service Table with the same structure the function returns its address with, and then assign this address to it:
struct SYS_SERVICE_TABLE {
void **ServiceTable;
unsigned long CounterTable;
unsigned long ServiceLimit;
void **ArgumentsTable;
};
static struct SYS_SERVICE_TABLE *ShadowTable;
ShadowTable = GetServiceDescriptorShadowTableAddress();
Needless to say, that all these tricks can only be done with administrator rights. More over, if you plan to continue manipulating KeServiceDescriptorTableShadow, you will also need to enable Debug Privileges, even for the administrator.