Calling Windows functions in kernel mode

Many of you, dear Khabrovites, use the functions of the Windows kernel. However, not everyone can imagine how these functions are called. A knowledge of some of the mechanisms of the kernel can be very useful.

This text is not intended for driver-writers who (in theory) know this even at a deeper level.

To begin with, there are much more functions in kernel mode than in all the well-known WinAPIs. This is due to the fact that a lot of undocumented functions are embedded in the kernel (though the situation improved with the release of Win7 and Win8, many of these functions go to MSDN). A list of such functions can be found, for example, here.

These functions can be divided into several groups:

  • Kernel API - the functions are located in ntoskrnl.exe (actually a regular dll), the core functions of the kernel.
  • Windowing API - functions are located in gdi32.dll, functions for working with windows and graphics.
  • Messaging API - functions are located in user32.dll, message processing functions.

How is all this called?

Let's start by calling from user mode.

- First of all, we turn to NtDll.dll - the “layer” between user mode and kernel mode. It contains the kernel function we need. This function places its number in the EAX register . It is an index in two tables known to the kernel: ServiceTable and ArgumentTable. Characteristically, this number is specific to each version of the OS. Often, it changes not only when moving from, say, WinXP to Vista, but even when moving from Vista to Vista SP1.
- Next, the address of the top of the parameter stack is placed in EDX , in most cases this is the value of the ESP register. It would seem, why copy an existing value? The fact is that after the interrupt is executed, the entire state of the processor is flushed to the stack, and the ESP will no longer be what we need.
- Finally, there is a switch to kernel mode. On older processors and systems, this is done using the 0x2e interrupt; on modern processors, there is a special processor instruction for this: SysEnter for Intel, SysCall for AMD.
- The result of the call is the execution by the processor of the code whose address is in the global interrupt table in a specific cell. This code calls either KiSystemService (), in case of interruption, or KiFastCallEntry (),in the case of special instructions to the processor. By the way, the second one is registered in the coarse register MSR, which provides quick access to it.
- Further, the code that runs in the kernel executes a little differently, depending on where the function call came from, from kernel mode or from user mode. The task of the kernel code: to execute a function call from EAX using the internal function table. This table is called KiServiceDescriptorTable . This is a structure with three fields:

  • address of the table ServiceTable (nt! KiServiceTable) - an array of functions
  • address on ArgumentTable (nt! KiArgumentTable) - the number of bytes occupied by the parameters
  • number of items in these tables

When the SysEnter and SysCall functions are called, they need to dispatch to the OS functions. Each thread has a stack of user mode and kernel mode. The OS takes a cell value in the ArgumentTable table, which shows how many bytes the parameters occupy on the stack (for this we remembered EDX). Then it copies this number of bytes to the kernel stack. And after that it makes a function call through ServiceTable. All this trouble with two stacks is needed, of course, to make sure that no changes in user mode will harm the execution of the function in the kernel.

- After the function is called and executed, and its operation ends, the return from kernel mode is performed using the iret or SysExit / SysRet tool.

Calling kernel functions in kernel mode.

In principle, we have the same thing, but ...
If you carefully look at the list of kernel functions, you will notice that there are functions that are completely similar to nt functions, but with the zw- prefix . All of them are uniquely mapped to nt functions. However, in kernel mode, they work differently.

The difference is that in the zw-functions the parameters are not validated. In other words, when calling from user mode, the system carefully monitors the parameters passed to the function - God forbid the programmer misses something, and hello, the blue screen. In kernel mode, they primarily work with drivers, and there the system already believes that you will check all the parameters yourself and guarantee their validity, because additional checks will eat up valuable time. It works like this: if zw is called, it loads the function number into EAX, puts a pointer to the kernel stack in EDX, and then calls the nt option, which does not validate.

In this connection the question arises - what if I exit the zw function from user mode? Will the system forgive everything? Not really. If zw is called in usermode, then it just calls its nt twin.

Here, in general, all the basic information about calls to kernel functions. Hope you were interested.

Also popular now: