Interfacing the the Native API in Windows 2000
You are not always required to write a kernel-mode driver in order to talk to the Windows 2000/NT kernel—there is a nifty system component called ntdll.dll that allows you to call a subset of the functions exported by the kernel module ntoskrnl.exe from a user-mode application. However, this technique is not officially supported by Microsoft and is largely undocumented, so neither Microsoft's Platform Software Development Kit (SDK) nor the Device Driver Kit (DDK) will get you very far here. This article gives you the basic knowledge needed to access the so-called Native Application Programming Interface (API) from user-mode.
INT 2Eh Interrupt Gate
The main duty of the ntdll.dll component is to provide the Win32 kernel32.dll library with a large set of utility functions. The function set exported by this DLL can be subdivided into two basic categories:
Runtime functions executed entirely in user-mode. This set includes parts of the standard C runtime library.
Kernel function wrappers that perform a switch from user-mode to kernel-mode and back.
Although the former function set is very convenient, to say the least, the latter is a true pearl. It allows a user-mode application to call an important subset of the Windows 2000/NT kernel-mode interface by simply linking dynamically to a DLL—exactly as an application calls the standard Win32 API functions. The only problem is that Microsoft doesn't provide header files that would contain the necessary definitions required to set up the function calls properly. Of course, some of the definitions can be found in the DDK header files, but unfortunately, you cannot simply #include these headers in a user-mode application project written in plain C because this results in lots of conflicts with the Win32 SDK header files.
Before I'll show you a way out of this mess, let us first examine how the ntdll.dll component does the magic of calling into the kernel and returning to user-mode. If you disassemble one of the functions with the name prefix Nt or Zw, such as NtQuerySystemInformation(), you come across a surprisingly short implementation, shown in Listing 1. Is this all? Just a simple interrupt invocation? Well, of course, there is more to it. I guess this code will seem quite familiar to DOS application developers. Most DOS operating system functions are called by setting up the CPU register AH with a function code and DX with an additional data parameter, if any, followed by an INT 21h instruction. Obviously, the implementations of NtQuerySystemInformation() and its fellow Nt*() functions use the same scheme: Register EAX contains a function code, and EDX points to the first function argument on the caller's stack.
Listing 1 Implementation of ntdll.NtQuerySystemInformation()
NtQuerySystemInformation: mov eax, 97h lea edx, [esp+4] int 2Eh ret 10h
The DOS INT 21h simply forces the CPU to branch to an address stored in slot 21h of the Interrupt Vector Table (IVT). In a protected-mode operation system such as Windows 2000/NT, interrupts are handled quite differently. The IVT is replaced by the Interrupt Descriptor Table (IDT), which contains memory descriptors of the target addresses rather than simple pointers. In the case of INT 2Eh, the IDT provides an interrupt gate that switches the CPU from user-mode to kernel-mode before branching to the specified address, and switches back to user-mode when the call is about to return. The target address of the INT 2Eh gate is located in the ntoskrnl.exe module, and is named KiSystemService(). This function examines the function number supplied in register EAX, retrieves the corresponding function pointer from an internal kernel structure called Service Descriptor Table (SDT), copies the caller's argument stack to the current kernel-mode stack, and invokes the target function. On return, control is passed back to the caller by means of the ntoskrnl.exe function KiServiceExit(). Actually, this mechanism is a bit more complex than explained here. (If you want to know more, please refer to Chapter 2 of my book Undocumented Windows 2000 Secrets [3]).