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 } }
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:
- the signature of the system service function for
stackFrameinitialization - the system service call number in the system service dispatch table
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 ..
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
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:
- Insert entries for the new system service call into the system service dispatch table (files
systable.asmandsysstubs.asm). - Implement the new system service function.
- Create a user mode wrapper function for the system service call.
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.
[...] Howto: Implementation of new system service calls (I) [...]
[...] the implementation of new system service calls in the Windows Research Kernel can be found in the first and second part of this small [...]
[...] 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 [...]
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?
[...] 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 [...]