Abstract
This chapter explores in detail the system service implementation of Windows NT. The authors explain the mechanism for adding new system services to the Windows NT kernel and provide an example that adds three new system services.
CUSTOMIZING THE KERNEL for specific purposes has been very popular among developers long before Windows NT. Ancient Unix gurus and developers alike practiced the art. In Unix, for example, kernel developers can modify the kernel in several ways, such as adding new device drivers, kernel extensions, system calls, and kernel processes. In Windows NT, DDK provide means to add new device drivers. However, one of most effective ways of modifying the kerneladding new system services to itis not documented. This method proves more efficient than adding device drivers for several reasons discussed later in this chapter. Here, we focus on the detailed implementation of a system service inside the Windows NT kernel and explain, with examples, how new system services can add to the Windows NT.
In Inside Windows NT, Helen Custer mentions the design of system services and the possibility of adding new system services to the kernel:
Using a system service dispatch table provides an opportunity to make native NT system services extensible. The kernel can support new system services simply by expanding the table without requiring changes to the system or to applications. After a code is written for a new system service, a system administrator could simply run a utility program that dynamically creates a new dispatch table. The new table will contain another entry that points to a new system service.
The capability to add new system services exists in Windows NT but it is not documented.
Very little changed between NT 3.51 and later versions of Windows NT in this area. The only thing being changed is that some of the data structures involved in implementation of a system service are located at the different offsets in the later versions of the operating system. We feel that our method of adding new system services may hold, possibly with very minor modifications, in future releases of Windows NT.
At the end of this chapter, we try to shed some light on the possible thought that went into the design of this portion of the operating system.
DETAILED IMPLEMENTATION OF A SYSTEM SERVICE IN WINDOWS NT
In Chapter 6, we discussed how a system service is invoked by the NTDLL.DLL at the request of the application. The SSDT (System Service Dispatch Table) and SSPT (System Service Parameter Table) help the kernel in accessing the right system service ID. The implementation of the SSDT and SSPT occurs similarly in all versions of Windows NT to date. We present the two implementations separately for clarity, one for Windows NT 3.51 and one for the later versions of the operating system such as Windows NT 4.0 and Windows 2000.
Below is the table containing the service ID mappings for all versions of Windows NT to date.
In Windows NT 3.51, only the KERNEL32 and ADVAPI32 functions of the operating system route through NTDLL.DLL to NTOSKRNL. The USER32 and GDI32 functions of the operating system implement as a part of the Win32 subsystem process (CSRSS). The USER32.DLL and GDI32.DLL provide wrappers, which calls the CSRSS process using the local procedure call (LPC) facility.
The functionality of USER32.DLL and GDI32.DLL is implemented differently in Windows NT 4.0 and Windows 2000. The functionality of the USER32 and GDI32 components is moved into the kernel mode driver WIN32K.SYS. The workhorse routines of NT 3.51s Win32 subsystem have transferred their load on the system services added by the addition of the WIN32K.SYS component. This explains why we see more system services versions later to Windows NT 3.51. This new set of system services corresponds to the USER32 and GDI32 components of the operating system.
Windows NT System Service Implementation
Here, we discuss the implementation of a system service under Windows NT. An INT 2Eh instruction implements the system services. The INT 2Eh handler is internally named as KiSystemService and hereafter we refer to it as the handler. Before entering the handler, the EAX register is loaded with the service ID and the EDX register with a pointer to the stack frame required for implementation of a particular service. The handler gets to the current TEB (Thread Environment Block) by looking at the Processor Control Region (PCR). The current TEB is stored at an offset of 0x124 in the Processor Control Region. The handler gets the address of the System Service Descriptor Table from the TEB. You can locate the address of the Service Descriptor Table at 0x124 offset in the TEB. Chapter 6 explains the format of the Service Descriptor Table.
The handler refers to the first entry in the Service Descriptor Table for service IDs less than 0x1000 and refers to the second entry of the table for service IDs greater than or equal to 0x1000. The handler checks the validity of service IDs. If a service ID is valid, the handler extracts the addresses of the SSDT and SSPT. The handler copies the number of bytes (equal to the total number of bytes of the parameter list) described by the SSPT for the servicefrom user-mode stack to kernel-mode stackand then calls the function pointed to by the SSDT for that service.
Initially, when any thread is started, the TEB contains a pointer to the Service Descriptor Tableidentified internally as KeServiceDescriptorTable. KeServiceDescriptorTable contains four entries. Only the first entry in this table is used, which describes the service ids for some of the KERNEL32 and ADVAPI32 calls. Another Service Descriptor Table, internally named KeServiceDescriptorTableShadow, identically matches KeServiceDescriptorTable under NT 3.51. However, under later versions of the operating system, the second entry in the table is not NULL. The second entry points to another SSDT and SSPT. This SSDT and SSPT comprise part of the WIN32K.SYS driver. The WIN32K.SYS driver creates this entry during its initialization (in its DriverEntry routine) by calling the function called KeAddSystemServiceTable. (We provide more information on this later in this chapter.) This second entry describes the services exported by WIN32K.SYS for USER32 and GDI32 modules.
You should note that in all versions of Windows NT, KeServiceDescriptorTable contain only one entry and that all started threads point their TEBs to KeServiceDescriptorTable. This continues so long as the threads call services belonging to first entry in KeServiceDescriptorTable. When the threads call services above these limits (unlikely in 3.51, but very likely in later versions of Windows NT, because USER and GDI service IDs start with 0x1000), the KiSystemService jumps to a label _KiEndUnexpectedRange under NT 3.51 and _KiErrorMode under NT 4.0 and KiBBTEndUnexpectedRange in Windows 2000. Lets see what role the code at each label plays.
_KiEndUnexpectedRange (NT 3.51)
The following example shows the role of the code at the _KiEndUnexpectedRange label:
if (serviceID < 0x1000) {
/* It means if service id > 0xC3 and
* service id < 0x1000
*/
return STATUS_INVALID_SYSTEM_SERVICE;
}
if (PsConvertToGuiThread() != STATUS_SUCCESS) {
return STATUS_INVALID_SYSTEM_SERVICE;
}
PsConvertToGuiThread()
{
if (PspW32ProcessCallout) {
/* In case of NT 3.51 this is code is never
* invoked, since PspW32ProcessCallout is
* always = 0
*/
/* This is only invoked for the later versions of the operating system
* Please refer to the next section for details
*/
} else {
return STATUS_ACCESS_DENIED;
}
}
_KiErrormode (in Windows NT 4.0 and KiBBTEndUnexpectedRange in Windows 2000)
The code resembles _KiEndUnexpectedRange, except that the PspW32ProcessCallout variable is always nonzero. Hence, the code in PsConvertToGuiThread proceeds further. It performs several tasks; we now describe the one of immediate interest.
PsConvertToGuiThread allocates a block of memory and copies KeServiceDescriptorTableShadow to the allocated block. Note that under NT 4.0 and Windows 2000, KeServiceDescriptorTableShadow contains two entriesone for KERNEL32 calls and one for USER32 and GDI32 calls. After copying this, the code updates the TEB of the current thread to point to this copy of KeServiceDescriptorTableShadow and then returns. This happens only the first time a USER32 or GDI32 service is invoked. After this, all system services, including KERNEL32 module, route through this new table, since the first entry in this table already points to the SSDT and SSPT for the KERNEL32 functions.
KeServiceDescriptorTableShadow is not exported by the NTOSKRNL and therefore is a nonaccessible table.
Under Windows NT 3.51, both KeServiceDescriptorTable and the Shadow Table point to the same SSDT and SSPT and contain only one entry. Now, ask yourself this logical question: Why do we have the Shadow Table at all when apparently it does not provide much help in NT 3.51? We attempt to answer this question later in the chapter.
Note: Note that once a process makes a USER32/GDI32 call, it permanently stops using the original KeServiceDescriptorTable and switches entirely to a copy of KeServiceDescriptorTableShadow.
ADDING NEW SYSTEM SERVICES
Adding new system services involve the following steps:
Allocate a block of memory large enough to hold existing SSDT and SSPT and the extensions to each of the table.
Copy the existing SSDT and SSPT into this block of memory.
Append the new entries to the new copies of the two tables as shown in Figure 7-2.
Update KeServiceDescriptorTable and KeServiceDescriptorTableShadow to point to the newly allocated SSDT and SSPT.
In NT 3.51, because the Shadow Table is never used, you could get away without having to update it. In NT 4.0 and Windows 2000, however, the Shadow Table takes a leading role once a GDI32 or a USER32 call has been made. Therefore, it is important that you update both KeServiceDescriptorTable and KeServiceDescriptorTableShadow. If you fail to update KeServiceDescriptorTableShadow in NT 4.0 or Windows 2000, the newly added services will fail to work once a GDI32 or USER32 call is made. We recommend that you update both the tables in all versions of Windows NT so that you can use the same piece of code with all the versions of the operating systems.
One implementation issue in updating the KeServiceDescriptorTableShadow is that NTOSKRNL does not export this table. However, NTOSKRNL does export KeServiceDescriptorTable. So, how can you get the address of KeServiceDescriptorTableShadow?
The method we used for this is as follows. There is a function in NTOSKRNL called KeAddSystemServiceTable. This function is used by WIN32K.SYS driver for adding the USER32 and GDI 32 related functions. This function does refer to KeServiceDescriptorTableShadow. The first entry in both KeServiceDescriptorTable and KeServiceDescriptorTableShadow is the same. We iterate through each DWORD in the KeAddSystemServiceTable code, and for all valid addresses found in this function, we compare the 16 bytes (size of one entry in descriptor table) at this address with the first entry in KeServiceDescriptorTable. If we find the match, we consider that as the address of the KeServiceDescriptorTableShadow. This method seems to work in all Windows NT versions.
EXAMPLE OF ADDING A NEW SYSTEM SERVICE
This example consists of three modules. One device driver contains the code for new system services and the mechanism of adding new system services to a Windows NT kernel. One DLL represents an interface to new system services (just as NTDLL.DLL provides interface for services called by KERNEL32.DLL). And one application links to this wrapper DLL and calls the newly added services. The newly added services print a debug message saying, kernel service .... Called and print the parameters passed to the services. Each service returns values 0, 1, and 2. The function AddServices() isolates the code for the mechanism of adding new system services.
Assuming first that the sample binaries are copied in C:\SAMPLES directory, here are the steps to try out the sample:
Run instdrv extndsys c:\samples\extndsys.sys. This will install the extndsys.sys driver. The driver will add three new system services to Windows NT Kernel.
Run MYAPP.EXE. This will call wrapper functions in MYNTDLL.DLL to call newly added system services in EXTNDSYS.SYS.
Order Your SQL Fundamentals CD Today! Learn how to use SQL Server, understand Office integration techniques and dive into the essentials of SQL Express and Visual Basic with this free SQL Fundamentals CD.
You've Deployed SharePoint...Now What? This one-day free online conference delivers the technical knowledge needed to kick MOSS up a notch. In one information-packed day, independent SharePoint experts will present practical, real-world information and provide take-away, ready-to-use solutions
What Would You Do If You Ran Microsoft? ITTV's 2008 inaugural video contest, "If I Ran Microsoft..." is your chance to tell it like it is. Be goofy or be serious, but don"t miss this chance to have fun, win prizes, and go viral in a major way.
Maximize Your SharePoint Investment This web seminar discusses how true bi-directional replication of SharePoint content from one server to another enables branch offices to maintain access to current SharePoint content.