Building the Windows kernel
In this first post I will introduce the WRK v1.2 (Windows Research Kernel). The idea is to explain Windows' OS concepts, especially with source code, as I dig in the sources and acquire more knowledge of the kernel. My 2 fundamentals learning resources are:
- Microsoft Windows Internals, 4th Ed 2005, Mark Russinovich and David Solomon.
- The Windows Curriculum Resource Kit (CRK)
The Windows Research Kernel
The Windows Research Kernel v1.2 contains the sources for the core of the Windows (NTOS) kernel and a build environment for a kernel that will run on x86 (Windows Server 2003 Service Pack 1) and AMD64 (Windows XP x64 Professional). The Windows kernel implements the basic OS functions for processes, threads, virtual memory and cache managers, I/O management, the registry, executive functions such as the kernel heap and synchronization, the object manager, the local procedure call mechanism, the security reference monitor, low-level CPU management (thread scheduling, Asynchronous and Deferred Procedure calls, interrupt/trap handling, exceptions), etc.
The NT Hardware Abstraction Layer, file systems, network stacks, and device drivers are implemented separately from NTOS and loaded into kernel mode as dynamic libraries. Sources for these dynamic components are not included in the WRK, but some are available in various development kits published by Microsoft.
The following picture represents the organization of the WRK sources:
The “.” in the tree or the %wrk% environment variable represents the directory where you copied the WRK sources. In my case is “C:\WRK”. The %wrk%\public\ directory contains a number of include files shared among system components. The %wrk%\base\ntos\ contains the Windows kernel sources. The primary kernel source components included in the WRK are organized as follows:
|cache\ ||cache manager |
|config\ ||registry implementation |
|dbgk\ ||user-mode debugger support |
|ex\ ||executive functions (kernel heap, synchronization, time) |
|fsrtl\ ||file system run-time support |
|io\ ||I/O manager |
|ke\ ||scheduler, CPU management, low-level synchronization |
|lpc\ ||local procedure call implementation |
|mm\ ||virtual memory manager |
|ob\ ||kernel object manager |
|ps\ ||process/thread support |
|se\ ||security functions |
|wmi\ ||Windows Management Instrumentation |
|inc\ ||NTOS-only include files |
|rtl\ ||kernel run-time support |
|init\ ||kernel startup |
To build and deploy the WRK:
- From the WRK root we set the current directory to %wrk%\base\ntos\
- Type nmake -nologo x86= or nmake -nologo amd64= to compile the kernel for the x86 or x64 architecture respectively. If something went wrong and you want the build to start over again then type nmake -nologo x86= clean or nmake -nologo amd64= clean.
- After a successful build the produced kernel files will be in %wrk%\base\ntos\BUILD\EXE\wrkx86.exe (for x86) or %wrk%\base\ntos\BUILD\EXE\wrkx64.exe (for x64).
At this moment we have a brand new kernel built using the WRK build environment on Windows Vista. As I stated before, this kernel is a modified Windows Server 2003 SP1 kernel. So If I want to test it and debug it I need a Windows Server 2003 SP1 target OS.
To debug a kernel or a device driver, you need two computers: the machine being debugged (called the target machine) and another machine to run the debugger (called the host machine). In this case I will use a virtual machine infrastructure to avoid the need of having to configure an additional physical machine. From now on, the physical machine that runs Virtual PC and the debugger (WinDbg) will be referred to as the host machine, and the virtual machine running on the host will be known as the target machine.
A debugging session requires that the host and target machines are connected. When both machines are physical machines, you need to use a physical connection such as a null-modem cable, an Institute of Electrical and Electronics Engineers (IEEE) 1394 cable, or a USB 2.0 cable. But if the target machine is a virtual machine on the host machine, no physical connection is necessary because no second physical machine exists. Virtual PC does not support IEEE 1394 ports, but it does support serial ports and it is possible to emulate the behavior of a physical null modem cable by debugging a virtual machine over named pipes. Next I will configure the target, the debugger and the host to work in a virtual environment.
Configuring the target
To configure the target machine for debugging:
- From the Settings dialog box of the virtual machine, specify that we are going to connect to the COM1 serial port on the target virtual machine via the named pipe “\\.\pipe\debugPipe”.
- Start the virtual machine and open its boot.ini file. This file is in the root of C: but is hidden by default, so you might need to change your Windows Explorer settings to see the file.
- After that we copy the new kernel to the target virtual machine at %SystemRoot%\system32\ and the corresponding precompiled hal.dll from %wrk%\WS03SP1HALS\x86, which for an x86 virtual PC target is halacpim.dll.
- Then modify the boot.ini file as follows:
The boot.ini file will have 4 entries, this means that during boot time we will have to choose between 4 options.
Windows Server 2003, with the Microsoft provided kernel (ntoskrnl.exe) and debugging disabled.
Windows Server 2003, with the Microsoft provided kernel (ntoskrnl.exe) and debugging enabled.
Windows Server 2003, with my custom provided kernel (wrkx86.exe) and debugging enabled.
Windows Server 2003, with my custom provided kernel (wrkx86.exe) and debugging disabled.
- Please note the use of the /kernel and /hal switches for providing an alternative kernel to boot. Also notice the /debug and /debugport switches, which enable to attach a debugger (WinDbg) throught the named piped configured for the port COM1. For more information visit the BOOT INI Options Reference.
- Shut down the target virtual machine. (Do not turn off and close the machine; only shut it down. The virtual machine will be restarted to use the changes you just made.) Then select the boot option for the custom provided kernel with debugging enabled.
- And we will boot up the new kernel we have recently built/linked.
- Let’s see the output of winver for the new kernel (wrkx86.exe).
And now let’s contrast it with the output of winver for the official kernel (ntoskrnl.exe).
Configuring the debugger
The first step is to enable kernel debugging between the host and the target is to download and install the debug symbols on the host machine. The debug symbols are found in Program Debug Database (.pdb) files, which you can obtain from a symbol server or from a file system cache. The symbol server can be a corporate symbol server or the Microsoft symbol server at http://msdl.microsoft.com/download/symbols.
Using the Microsoft symbol server is a good idea since Microsoft keeps the server current with the most recent symbols for service packs and Windows updates. When using a symbol server, you need to specify a local or network directory in which to cache the symbols that you download from the symbol server. If the directory you specify does not exist, it will be created for you. The general syntax for using the Microsoft symbol server is the following:
To use it from WinDbg, press Ctrl+S to set the symbol search path.
Configuring the host
In WinDbg, press Ctrl+K to start kernel debugging and configure it to connect to the target virtual machine via a COM port. In order to achieve this check the Pipe checkbox to enable the use of pipe addresses on serial connections and also set the Port Name to the named pipe defined previously on the target “\\.\pipe\debugPipe” . This allows us to debug over a named pipe.
Debugging the target virtual machine from the physical host
To debug the target virtual machine from a physical host machine:
- Run WinDbg from host machine and start kernel debugging. Then start the target virtual machine. The kernel debugger will wait until the boot.ini debug-enabled virtual target boots up. Once the target machine starts the boot process, the debugger continues.
- Press CTRL+BREAK to break into the debugger and get a command prompt.
- At the resulting command prompt, enter lmDvmnt to confirm that the kernel debugging connection was successful. The previous command displays the information about a module named “nt”, which corresponds to the Windows kernel. In this case the kernel is the new one(wrkx86.exe):
And now let’s contrast it with the output of WinDbg when debugging the official kernel (ntoskrnl.exe).