The following post entry opens a series of CSRSS-oriented articles, aiming at describing the uncovered CSRSS mechanism internals, present in the Windows OS for more than fifteen years now. Although some great research has already been carried out by a few curious guys (check out the references), no thorough case study is available until now. In this series, I am going to cover both the very basic ideas and their implementations, as well as the recent CSRSS changes applied in modern operating systems (i.e. Windows 7). And so, just have a good read! ;)
Environment subsystems in Windows
The general idea behind the environment subsystems is to expose a strictly-defined subset of native functions to the user application. Because of the fact that Windows OS was designed to support both native Windows and POSIX (Portable Operating System Interface for Unix) executables, the developers had to separate API for both types of applications. Each of the supported subsystems consists of two, major parts:
- The subsystem process – a regular ring-3 application, responsible for handling some of the subsystem-specific functions.
- The subsystem DLLs – a special set of system libraries dedicated to a certain subsystem; they provide an additional layer between user programs and the native system calls.
As it turns out, the Windows Subsystem – otherwise known as CSRSS (Client/Server Runtime Subsystem) is an obligatory part of the system execution environment; in other words, Windows is unable to work correctly without CSRSS.exe running in the background. Because of the duties bound to CSRSS, the subsystem is required on every Windows, including the server editions (which doesn’t deal with interactive user sessions). On the other hand, POSIX (psxss.exe) belongs to the optional subsystems group – therefore, the process is started on demand, only.
Some specific configuration data, related to all of the supported subsystems can be found in the following registry key:
By default, the subsystem configuration looks similar to the following:
Name: Debug Type: REG_EXPAND_SZ Value:
Name: Kmode Type: REG_EXPAND_SZ Value: \SystemRoot\System32\win32k.sys
Name: Optional Type: REG_MULTI_SZ Value: Posix
Name: Posix Type: REG_EXPAND_SZ Value: %SystemRoot%\system32\psxss.exe
Name: Required Type: REG_MULTI_SZ Value: Debug Windows
Name: Windows Type: REG_EXPAND_SZ Value: %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileC
Most of us already know the subsystem DLLs, supporting the Windows API – these are basically kernel32.dll, user32.dll, gdi32.dll, advapi32.dll, and many more – a majority of these libraries are used by numerous Windows software developers by their daily routine. When it comes to POSIX, only one module is apparently enough to implement the desired Unix API – that’s psxdll.dll.
Furthermore, each process is associated with one, certain subsystem; this property is being set by the linker (during the compilation process), and resides in the following PE structure field:
while the available constant values are listed below:
#define IMAGE_SUBSYSTEM_UNKNOWN 0 #define IMAGE_SUBSYSTEM_NATIVE 1 #define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 #define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 #define IMAGE_SUBSYSTEM_OS2_CUI 5 #define IMAGE_SUBSYSTEM_POSIX_CUI 7 #define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 #define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 #define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 #define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 #define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 #define IMAGE_SUBSYSTEM_EFI_ROM 13 #define IMAGE_SUBSYSTEM_XBOX 14
As can be seen, the Portable Executable format is pretty vast, and can be used to build executables for different processor (Intel x86, Intel x86-64, ARM) and system (Microsoft Windows, WIndows CE, EFI, XBOX) architecture.
API division based on the kernel and subsystem involvement
As it turns out, the subsystem process (such as csrss.exe) doesn’t necessarily have to take part in every API call being issued by an user application. Due to the fact that the actual API implementation resides in the Enviroment DLLs, they can decide whether to communicate with the NT Kernel and/or subsystem process or not. In particular, one can distinguish three separate types of API routines:
- The function is entirely implemented by the Subsystem DLL, hence no signal is being sent to either the kernel or subsystem process. This is the case for a majority of simple helper functions, such as GetCurrentProcess or GetCurrentThread (both of these routines return a constant pseudo-handle), functions which implement ring-3 mechanisms, e.g. SwitchToFiber, or functions implementing a specific processor feature, like InterlockedIncrement oraz InterlockedExchange (read more about Interlocked Variable Access),
- The function must issue one or more calls into the NT Kernel in order to complete correctly. The functionalities provided by these routines require ring-0 privileges (e.g. in order to switch the process context), thus cannot be entirely implemented by the DLL. On the other hand, these functions perform subsystem-unrelated operations, and thus don’t have to communicate with the subsystem process. Examples of such functions include ReadProcessMemory and WriteProcessMemory, both responsible for operating on memory address space belonging to an external process.
- The function takes advantage of mechanisms implemented by the subsystem process, and must therefore send one or more requests e.g. to csrss.exe. In this case, the client application issues a special packet via some Inter-Process communication mechanism [in this case – (Advanced) Local Procedure Calls], using an internal, undocumented protocol. A great example of this API type can be CreateProcess or CreateRemoteThread, as both of them must inform CSRSS about creating a new process/thread; otherwise, the entire function would fail (a win32 process/thread cannot be successfully spawned without letting the subsystem know about this being done).
Surprisingly, on Windows versions prior to NT4, the Windows Subsystem was responsible for performing much more work than it is now. More precisely, both the Window Manager and Graphic Services were a part of that user-mode application! Due to the fact that a special, separate process was used, dedicated to dealing with the window / graphics management, enormous efficiency problems began arising. That’s because every time a normal graphics-oriented application sent a request to CSRSS, a bunch of time had to be taken in order to switch the process / thread context between the client and server. Even though the developers were doing their best to optimize the communication process, it was still worse than it could be, having the window management implemented in kernel-mode. In particular, the problem was most noticeable in the context of graphics intense programs – and so, in Windows NT4, a majority of the graphics-related code was moved into a special system driver called win32k.sys, which is available to user-mode applications through the standard system-call mechanism (just like the core NT functions are).
The question is – what has actually remained after replacing CSRSS’s main functionality with a ring-0 module?
Nowadays, the Client/Server Runtime subsystem is responsible for performing a few minor tasks, related to:
- Console Window management (prior to Windows 7),
- Process and Thread list management,
- Supporting the 16-bit virtual DOS machine emulation (VDM),
- Other, miscellaneous functions, such as GetTempFile, DefineDosDevice, ExitWindows and more.
All of the above functionalities are generally implemented by three DLLs (otherwise known as ServerDlls), imported by the csrss.exe image:
BASESRV.DLL (process and thread lists, VDM support)
WINSRV.DLL (console management, user services)
A complete list of the functions supported on different Windows OS versions can be found here (sorted by the dispatch table) and here (by OS version).
Windows CSRSS instances
If we already know that CSRSS is a standard, user-mode application – the question is, when is it spawned, how many instances of the process should be concurrently running in the system, and what security token csrss.exe executes with. Let’s answer these questions respectively.
The actual time and number of csrss.exe processes being spawned is different on Windows XP and Windows Vista together with Seven. In the first case, CSRSS is created at boot time, by the Session Manager (smss.exe) process, right after loading the win32k.sys module into kernel memory. The process inherits his parent’s security token, and therefore runs with the highest possible – SYSTEM – privileges. After spawning csrss.exe, regular win32 applications (such as explorer.exe, or even winlogon.exe) can be successfully launched. This Windows Subsystem process is only started once, and remains running until the system is shut-down. If, however, csrss.exe is unexpectedly terminated during normal system execution (e.g. due to an unhandled exception crash or manual user termination), the NT kernel detects this issue and manually crash the system by issuing a call to KeBugCheckEx routine with the CRITICAL_OBJECT_TERMINATION error code. Furthermore, there is only one instance of CSRSS running on a Windows XP box – this process is responsible for handling the requests coming from all active processes, no matter what user they’re running under.
When it comes to Windows Vista and later, a quite different approach was adopted. One CSRSS instance is started by the Session Manager, for handling the requests coming from session-0 modules. This process also stays alive until the very end of system execution. Furthermore, whenever an interactive user logs on, another separate csrss.exe is started by a dedicated smss.exe – the new Windows Subsystem instance is again designed to deal with signals sent by applications running under the new user’s session. When, in turn, the user decides to log out, CSRSS is politely terminated (though no BSoD is triggered by the kernel this time). All of the active CSRSS instances run with the same, highest user rights, as all of them are launched by the smss.exe process, highly tight up with the NT kernel itself.
In this short post, I have tried to give you a basic insight into the precise way of where csrss.exe is from and how it works internally. As it turns out, it is possible to make use of the Windows Subsystem in the context of various, interesting hacks – but this remains yet to be described. More detailed information can be found in the Windows Internals 5 book (section Environment Subsystems and Subsystem DLLs), and numerous articles and advisories, listed in the following section. Comments – as always – welcome!
- Wikipedia, Portable Operating System Interface [for Unix]
- Wikipedia, Client/Server Runtime Subsystem
- Wikipedia, Windows NT Startup Process
- Secunia, Microsoft Windows CSRSS MsgBox Memory Corruption Vulnerability
- SecurityFocus, Microsoft Windows CSRSS HardError Messages Denial of Service Vulnerability
- Ivanlef0u, Win7 and CreateRemoteThread
- Cesar Cerrudo, Story of a dumb patch
- Mark Russinovich, Inside the Windows Vista kernel: Part 1, Part 2, Part 3
5 thoughts on “Windows CSRSS write up: the basics”
lol part 1/1 :)
Nice work btw. Keep posting dude!
@Ivanlef0u: haha, that’s because I expect some of the posts to be split into more parts ;>
nevertheless, it might look funny indeed ;>
Actually, csrss has been per-session from the beginning of terminal services, I think.
Why have they decided to make process and create functions available through lpc to process instead putting all in dll? Also what happens if you dont let csrss know about new process?
Comments are closed.