Howto: Implementation of new system service calls (I)
Windows Research Kernel @ HPIThe 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
stackFrame
initialization - 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.asm
andsysstubs.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.
Comments
5 Responses to "Howto: Implementation of new system service calls (I)"
[...] 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 [...]