Howto: Implementation of new system service calls (I)

By Michael Schöbel

The kernel interface to user mode applications can be described by the set of system service calls. Implementing a new service call is the easiest way to expose new kernel functions to user mode programs.

This post describes the necessary steps to implement a new system service call in the WRK.

First, some background information about system service calls and system service dispatching in Windows is given. Afterwards, the user mode side of directly calling system services is described. Finally, the kernel mode side is examined and a detailed description of how new service calls can be implemented is given.

System service dispatching

A special, processor dependent, instruction is called to trigger a system service call. On 32bit x86 processors Windows used the int 0x23h, the sysenter (Intel CPUs) or the syscall (AMD CPUs) instruction. The service number and possible arguments are stored depending on which of these ways the system service is called. The system service dispatcher delegates the call to an appropriate function depending on the system service number. This delegation is done by looking into a system service dispatch table with mappings from service number to system function. After copying the arguments from the caller’s user mode to its kernel mode stack, the system service function is executed.

From the user mode point of view the kernel interface is wrapped in the Windows API, therefore in most cases no explicit system service call must be made by application developers. User mode applications for example call functions from NTDLL.DLL and the corresponding implementation deals with the kernel invocation.

Extending operating system functionality

The introduction of new operating system functionality to the kernel can be achieved in different ways:

Device drivers can be loaded into the kernel and then manipulate the system service table or they can be called in a driver-specific way. Without the kernel source code, this is the only way to extend the Windows kernel functionality

Extending existing service calls Many of the system service call interfaces are designed in a flexible way. For example NtSetInformationThread (see /base/ntos/ps/psquery.c) is used for almost all thread-related operations. An argument of the type THREADINFOCLASS (enumeration) determines the operation to be executed. Such a function can be easily extended by introducing new elements into the enumeration.

Creating new system service calls is the appropriate way to expose new kernel functionality if no matching system service already exists. This method requires the introduction of a new system service number and the modification of the system service dispatch table.

In this post we will concentrate on the last possibility: How can a new system service call be implemented? The next section provides the information needed for user mode side development for extended kernels. The section afterwards examines the kernel mode side.

System service calls – user mode side

From the (user mode) application development point of view the following questions have to be answered: How is a system service call invoked programmatically? How are arguments passed to the service routine?

NTSTATUS QuerySystemTime(OUT PLARGE_INTEGER CurrentTime) {
 
	void** stackFrame = &CurrentTime;
 
	// call NtQuerySystemTime
 
	__asm {
//		mov eax, 0x00AE		// XP
		mov eax, 0x00B6		// EE
		mov edx, stackFrame
		int 0x2E
	}
}

Listing 1: Calling the kernel with interrupt 0×2Eh
Listing 1 shows a short code fragment to get the current system time from the Windows kernel by using interrupt 0×2Eh and calling the system service function NtQuerySystemTime directly. An application could use the Win32 API call GetSystemTime with (almost) equivalent functionality (the return value is a SYSTEMTIME structure instead of a LARGE_INTEGER).The system service call arguments have to be copied to the kernel mode stack of the calling process. The pointer to the parameters (stackFrame, line 2) is passed via the edx register (line 9). Because of using C calling conventions (function arguments are placed on the stack “from right to left”, so the argument on top of the stack corresponds to the “first” parameter in the function signature), stackFrame has to be initialized with the “first” parameter of the wrapper function. This goes for the case, that the user mode function and the corresponding system service call function have the same signature.In the assembler part of the function the system service call number is stored in the eax register (line 8). The number of a specific service call depends on the version of the used Windows system and service pack (A good overview is provided in this table). For example on a Windows XP system NtQuerySystemTime has another number (line 7) than on a Windows Server 2003 system.The transition to the system service dispatcher occurs after activation of interrupt 0×2Eh (line 10). The system service function is executed and afterwards the function returns. The NTSTATUS return value (line 10) is set implicitly: the kernel sets the eax register to an appropriate value (= result of the system service function).In short, to call system service functions directly you need to know:

With this information, a generic wrapper function can be implemented and the new system service function can be used in user mode applications. To hide the low-level details from application developers, a user mode DLL can be designed to wrap the system service call.

System service calls – kernel mode side

The mechanism of system service call dispatching is the same for all service calls. Therefore, only the system service call dispatch table must be modified to hold a reference to the newly created service and the service function must be implemented.

..
TABLE_ENTRY  QueryPortInformationProcess, 0, 0
TABLE_ENTRY  GetCurrentProcessorNumber, 0, 0
TABLE_ENTRY  WaitForMultipleObjects32, 1, 5
TABLE_ENTRY  MyNewSystemServiceCall, 1, 1 
 
TABLE_END 296
..

Listing 2: /base/ntos/ke/i386/systable.asm
Listing 2 shows a piece of the system service table definition file. To insert a new service call, a new TABLE_ENTRY line must be inserted (line 4) and the system service call counter must be incremented by one (line 6). This counter defines the system service number of the new service, in this case 296 (or 0×128h).The two integer values after the service name (line 6) define the number of arguments to pass to the system service call. The first 1 shows that at least one argument is passed, the second 1 indicates that the service requires exactly one argument. To define a new service call with 5 arguments, line 4 must be TABLE_ENTRY SysCallName, 1, 5.

..
SYSSTUBS_ENTRY8  295, WaitForMultipleObjects32, 5
SYSSTUBS_ENTRY1  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY2  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY3  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY4  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY5  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY6  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY7  296, MyNewSystemServiceCall, 1
SYSSTUBS_ENTRY8  296, MyNewSystemServiceCall, 1 
 
STUBS_END

Listing 3: /base/ntos/ke/i386/sysstubs.asm
The system service dispatcher needs some (generated) stub functions. Listing 3 shows the related entries in the sysstubs.asm (lines 2-9). After SYSSTUBS_ENTRYx the service call number is given, then the service call function name and finally the number of arguments.These two modifications lead to a new system service dispatch table. Afterwards, the linker expects to find a NtMyNewSystemServiceCall implementation with one parameter. The corresponding implementation must be inserted appropriately into the source code.

Summary

The implementation of a new system service call with the Windows Research Kernel requires only a few steps:

The second part of this post introduces a simple example application. Additionally the whole “HOWTO” and a WRK patch for the example will be provided for download.

Comments

5 Responses to “Howto: Implementation of new system service calls (I)”

  1. Windows Research Kernel @ HPI - news, howtos and labdescriptions » Howto: Implementation of new system service calls (II) on July 19th, 2007 11:03

    [...] Howto: Implementation of new system service calls (I) [...]

  2. Windows Research Kernel @ HPI - news, howtos and labdescriptions » Howto: Implementation of new system service calls (III) on March 19th, 2008 15:20

    [...] the implementation of new system service calls in the Windows Research Kernel can be found in the first and second part of this small [...]

  3. Windows Research Kernel @ HPI - news, howtos and labdescriptions » System Service Call Wizard for Visual Studio on July 16th, 2008 13:32

    [...] For further information on how to build a system service call, please refer to Michael’s HowTo series or read the readme.txt in the project that was created with the [...]

  4. Chelsea M Heffner on February 22nd, 2009 03:55

    I’m having trouble implementing the following code

    SYSSTUBS_ENTRY8 295, WaitForMultipleObjects32, 5
    SYSSTUBS_ENTRY1 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY2 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY3 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY4 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY5 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY6 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY7 296, MyNewSystemServiceCall, 1
    SYSSTUBS_ENTRY8 296, MyNewSystemServiceCall, 1

    I keep getting a type 3 error in my decompiler, any suggestions?

  5. Windows Research Kernel @ HPI - news, howtos and labdescriptions » Implementation of a New System Service Call — 2009 Update on March 26th, 2009 16:10

    [...] tutorial on how to create a new system service call in the Windows Research Kernel in his HowTo series. An important part is to define the system service dispatch table that contains the new system [...]