A journey into the sys call path: heading toward the gateway (ntdll) of the kernel space : PartII
To read part I click here.
Recap of part I: the user space
The windows native system services (from now on, we will call them sys calls) are implemented as a set of routines that run in kernel/system mode. The number of sys calls depends on the windows version (Windows System Call Tables), for instance, windows 7 exposes 401 sys calls (UNIX like system calls), while windows 10 provides 471. It is important to note that these sys calls are hosted in the core kernel image (ntoskrnl.exe) in the kernel/system space.
When any user process makes a call to a win32 API routine (within a dll) that needs to execute a sys call, this sys call will directly or indirectly be forwarded to the ntdll module that acts as gateway to the kernel space. DLLs in the user space are layered on top of each other and only dll in the higher layer can import routines from a dll in the lower layer.
Before digging into the kernel space, let's enjoy a journey into the sys call path at the code level by exploring how the implementation (windows is not an open source project, you would say, you are right!) of the Win32 API routine WriteFile() looks like. The main objective of this journey is to show you how dll modules import routines from each other and how a sys call ends up in the ntdll module before being executed in the kernel space.
Heading toward the gateway (ntdll) of the kernel space
Microsoft does not expose to the public its Windows source code (although some privileged partners have restricted access to a part of the OS source code), well, this is not a secret. There is only one way to show you in reality how a Win32 API routine is implemented, namely, by using a debugger, where the routine’s disassembled source code will be presented.
People who are not familiar with/a fan of the assembly code can still enjoy reading this part, so you don't have to skip it. It is only intended to show you how the win32 API routine WriteFile() that resides in KERNELBASE.dll ends up calling the sys call routine ZwWriteFile() in the ntdll module, which in turn acts as a stub to the actual implementation in the kernel space.
The prototype of this routine is documented in Windows SDK as follows:
You have to download the Windows SDK in order to have the debugger on your windows OS. If you are interested, you can try it on your own, this will help you better understand the content of this article. Nevertheless, you should be able to understand it without trying the debugger.
We start a debug session using the below batch file to analyze the winlogon.exe (it imports many dlls, will see all of them in a bit (for instance, the KERNELBASE.dll that contains the win32 API routine WriteFile() and for sure the ntdll.dll, almost all windows user space applications import it):
- set PATH=%PATH%;C:\program Files\Debugging Tools for Windows(x86)
- set DBG_OPTIONS=-v
- set DBG_LOGFILE=-logo .\cdbgLogFile.txt
- set DBG_SYMBOLS=-y SRV*c:\Symbols*https://msdl.microsoft.com/download/symbols
- CDB.exe %DBG_LOGFILE% % DBG_SYMBOLS% .\winlogon.exe
Once the batch file has been executed the cdb.exe debugger will display the screen above, we can clearly spot all imported dlls (KERNELBASE.dll which hosts the WriteFile() routine, for instance), don't worry about the details, now the debugger is waiting for commands.
Since we want to examine the win32 API routine WriteFile(), we will ask the debugger to disassemble this routine using the command :uf WriteFile.
Looking at the listing above (just a part of it for simplicity), we can depict that the WriteFile() routine is implemented in the KERNELBASE.dll. The listing above is what the debugger displays on the screen. We don't care about the assembly code of the routine, except for the line that forwards the call to the NtWriteFile() exported by the ntddl.dll, using the assembly instruction "call".
We are interested in the address of this called routine, from the listing above we can extract it in hex: 0x754c1190
Now we ask the debugger (using the command "dps" followed by the routine address) to show us the content stored at this address, and there it is, we are inside ntddll.dll.
From the listing above we can clearly see that the address leads us to the routine ZwWritefFile() (is the same as NtWriteFile()) exported by ntdll.dll.
Now let's disassemble (using the debugger command "uf" followed by the routine name) this ZwWriteFile() routine, to see its implementation, and here we are, there is no real implementation of the sys call ZwWriteFile() routine. It is just a stub that forwards us to the address 0x7ffe0300 (stored in the register EDX, this is done using the assembly instruction "call", see listing below).
It is also important to mention that the value stored in the EAX register, namely 0x18CH, is the sys call identification number of the sys call routine ZwWriteFile() (later to this, when we explore the kernel space).
Let's ask the debugger about this address again (using the debugger command "dps"). Looking at the listing below we can see, that it is pointing at the routine "KiFastSystemCall()" (exported by ntdll.dll as well), in fact this is the only gate that is responsible to switch to the kernel space within the ntdll module, this means that all sys calls (401 in case of windows 7) are redirected to the this routine in order to switch to the kernel address space, to finally execute the real code.
Let's ask the debugger again to disassemble this routine using the debugger command "uf". In the listing below, we can spot the instruction "systenter". This is the assembly instruction responsible for forwarding the call to the kernel space. So, we hit the bottom of the user space sys call call path.
We hope you enjoyed this long and some how up and down journey. The figure below summarize the call path.
In this article, we have inspected the implementation of the of the Win32 API WriteFile() routine (exported by the KERNELBASE.dll), and presented how the routine calls the ZwWriteFile() (exported by ntdll.dll). We have also showed that the implementation of the routine ZwWriteFile() inside the ntdll.dll is restricted to a fewer instructions (acts just a stub) that calls the routine KiFastSystemCall() (exported by ntdll.dll as well), which in turn contains the instruction "systenter" that should do the switch to the kernel space, where actually the real implementation of the routine ZwWrieFile() resides and where it should execute, namely in the core kernel image (ntoskrnl.exe) of the kernel/system space.
Finally, it is important to mention that all sys calls are forwarded to the same routine, namely, the KiFastSystemCall() (the single gate/entry to the kernel space) routine, while the value stored in the EAX register will be used in the kernel space to distinguish between the different sys calls/routines (more to this in part III).
Part III : covers the kernel/system space where actually the sys calls are executed. We will start by showing what the execution of the instruction "sys enter" will trigger?
References
Pavel Yosifovich, Mark Russinovich, David Solomon, and Alex Ionescu. Windows Internals, Part 1: System architecture, processes, threads, memory management, and more. Microsoft Press; 7th edition (2017). ISBN-10 : 9780735684188.
Blunden, Bill. The Rootkit Arsenal: Escape and Evasion in the Dark Corners of the System. Jones & Bartlett Learning (2013). ISBN 978-1-59822-061-2.
Michael Hale Ligh, Andrew Case, Jamie Levy, and AAron Walters. The Art of Memory Forensics: Detecting Malware and Threats in Windows, Linux, and Mac Memory. Paperback. John Wiley & Sons (2014), ISBN-10: 1118825098.
Jeffrey Richter, and Christophe Nasarre. Windows via C / C ++ . Microsoft Press (2007), ISBN-10: 0735663777.
Tarik Soulami (2012). Inside Windows Debugging: A Practical Guide to Debugging and Tracing Strategies in Windows. Microsoft Press (2012), ISBN-10 : 0735662789.
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-nt-and-zw-versions-of-the-native-system-services-routines, accessed March 2021
Adjunct Assistant Professor at Embry- Riddle Aeronautical University
3 年It is interesting!