Microsoft Windows NT (In)security Model
-- Ognen Duzlevski
Dont get me wrong. No operating system is secure. Thus the "in" before
"security model" - it has nothing to do with not liking Microsoft. The same name
would have gone nicely with any other OS for the masses. In order to understand NT's
security model one needs to understand the underlying structure of this (quite complex)
operating system. Before ever working with NT, I was (naturally) a Linux user (well, OK,
we wont talk about DOS times now). I say "naturally" because in academic
environments (such as Universities) most of the machines are usually (one kind or the
other) UNIX boxes. Of course, working with Solaris or Linux or Digital UNIX I brushed off
any mention of Windows NT as a serious OS (this usually comes from lack of knowledge, not
from excess of it). But, having the time to get deeper into it - I find it to be pretty
fascinating. Perhaps a bit to complex but it is obviously well suited for
Microsoft's business model. Not everyone has the luxury of coming out with a new version
of their operating system every once in a while. So, I decided to undo the injustice and
treat NT in this article. After all, people should know.
Microsoft Windows NT - How It Works
First a bit of OS theory. There are several models of how one goes about building an
operating system. A while ago we had the monolithic model where OS code ran pretty much
unorganized all over the memory space. One can deduce for oneself that this model gave
little to scalability, maintenance and ultimately, security. A nice improvement to this
would be the layered model. Here, only code from a higher level can call upon the routines
in lower levels. Thus, debugging, maintenance and overall security are much improved.
However, this model alone is not used. This comes naturally, because there is a better
approach - the client/server model. When one connects layers with client/server model, one
gets Windows NT. Layers are a good way to utilize hardware protection that the latest CPU
models offer. Intel CPUs themselves define four levels (or rings) where applications
(including OSs) can run. Of course, ring0 is reserved exclusively for the OS (and Windows
NT is no stranger). However, as NT also runs on Digital's (now Compaq's) Alphas and used
to run on MIPS and PowerPC (all of them being RISCs), NT only utilizes two protection
levels - one is the user mode and other the kernel mode. In theory, the
client/server model divides the OS clearly into servers - pieces of code that
implement the file system, process and memory management and other vital components. User
applications and even kernel parts are users of the services provided by these servers.
The one responsible for communication between servers and clients is the
microkernel - the only part that should be implemented within privileged CPU levels. Also
in theory, the kernel should be minimal in order to make it maintainable, scalable, easier
to control etc. And the educational Mach
kernels (that started all of this) are implemented that way. However, for a commercial OS
to be fast and secure, most of it's code will run within the kernel mode, for the simple
reason that the context switches between kernel and user mode (when passing messages back
and forth) would just eat up to much time. For this reason, even the graphics and controls
services provided by NT are running from within kernel level. Some would say that one
could implement at least graphics and controls as user-mode servers which would make the
OS more stable. However, if we stop and think about it for a moment - what good if
user-mode implemented graphics server crashes and it does not crash the whole OS when the
whole OS will be unusable without the graphics routines anyway?
After the main model come(s)...smaller model(s), what else?
Most of the Windows NT OS is implemented in C (with some assembler for time-sensitive
and low-level parts) and a bit of C++ for the graphics routines. The OS is not
object-oriented in the OOP sense, however it heavily relies on the notion of objects,
subsystems and alike.
The overall structure of NT's implementation is the following -> first off, there
are two modes of operation, user and kernel. Within user mode are the system
processes, services, subsystems and applications. Within kernel mode are the executive,
the kernel, HAL, the device drivers, the GDI (Graphics Device Interface) and user
functions. Between user and kernel mode stands NTDLL.DLL - a bridge translating
(un)documented user API calls to (un)documented executive calls.
First off, the subsystem(s)
There are initially three subsystems that ship with NT (and nothing stops you from
adding your own fourth one). The subsystems are Win32, Posix and OS/2.
For those of you who dont know, IEEE Posix 1003.1-1990
is a standard outlining functions that should be present in an operating system (to
support portability). Windows NT only implements the minimal set required for it to earn
Posix certification so it is not exactly the ultimate Unix->NT->Unix porting and
development environment. The same goes for the OS/2 subsystem which only supports 16-bit
OS/2 1.2 character and video based applications. Knowing that OS/2 Warp 3.0 and 4.0 are 32-bit, very
powerful operating systems, one can immediately get the notion of the usefulness of NT's
OS/2 subsystem. The third and most important piece of these subsystems is the Win32. It is
NT's complete API bridge of (un)documented calls to the operating system. It is what
applications (and programmers) have at their disposal when programming the NT. The API
calls implemented in the NT are the superset of the API calls present in Windows 95 and
98, with exception of some calls dealing with PnP and some others. However, Windows NT 5.0
is supposed to implement the 'real' API superset. Win32 is NT's native interface and is
the one that does all the job beneath any other subsystems - Posix and OS/2 calls are
translated to Win32 API calls and further passed down to the executive. Of course, when
programming Windows (and just to make things sweater) one is not allowed to mix the calls
between subsystems in the same application. The Win32 subsystem is implemented in the
image CSRSS.EXE and provides basic support for:
- Console windows
- Process and thread creation and deletion
- some support for 16-bit Virtual DOS machine (VDM)
- other functions
Win32 subsystem also consists of the WIN32K.SYS, a kernel-mode device driver
that provides I/O support, user message passing to applications (and back), the GDI
(Graphical Device Interface) - a library of functions dealing with graphics output
devices. Within Win32 are also subsystem .DLLs like USER32, ADVAPI32 and KERNEL32
- their job is to translate Win32 API calls to undocumented internal calls to NTOSKRNL.EXE
and WIN32K.SYS. Finally, last part of the Win32 subsystem are the graphics device drivers
that are hardware dependant and generally provide support for display devices like
printers and video cards. The role of GDI becomes obvious here where it provides a set of
higher level encapsulation functions that enable transparent communication between the
applications and the hardware. For example, an application calls API to draw a circle,
this call reaches the GDI which "knows" whether the hardware is capable of
drawing this circle or it has to be "spelled out" pixel-by-pixel. The actual
drawing is done via commanding the graphical device drivers. All of this is hidden from
the programmer and ultimately the user, something we should all thank Microsoft for.
NTDLL.DLL
This is a support library for all of the subsystems, services and applications. A user
processes use this .DLL. NTDLL enables translation interface services between user-mode
applications and the kernel-mode executive. Naturally, NTDLL runs in user mode.
The executive
A funny piece of software, runs on top of the "real" kernel and does the
overhead policy decisions regarding (virtual) memory management, process and thread
management, security checks (Security Reference Monitor), I/O system
and cache management. It provides a set of functions that are exported to user-mode apps
through NTDLL.DLL, a set of functions callable from user mode but are undocumented, a set
of functions documented in the NT DDK (that's NT's development kit) and executable
only within kernel mode (mostly for device drivers), a set of undocumented functions
callable within kernel mode and a set of functions internal to the kernel components.
Besides doing the above (as if it wasnt enough), the executive (which is the
"politician of the OS") also provides object management (logically,
through an object manager), the LPC (Local Procedure Calls - a refined version of
Remote Procedure Calls or RPC) facility, a broad set of mathematical, string and other
run-time functions and the executive support routines. And besides this it even...naaaah,
that is all.
The Kernel
Low level "part" of NTOSKRNL.EXE, the one that does the
"dirty" work - the kernel is the raw object provider for the executive. And just
to be on the fair side (if there is one), the executive is not the only one providing
policy decisions for the OS, the kernel does this when thread and process scheduling and
dispatching is in question. The kernel also indulges itself into trap, interrupt and
exception handling and dispatching (something we will talk about in the May article) and
multi-CPU synchronization. However, it's the most important function is to provide raw
kernel objects to the executive, which then adds the policy overhead (like object
names, handles, reference counters etc.). When we say "kernel objects" we
usually mean system resources. Two types of these objects are present - control and
dispatcher objects. Control objects are the kernel process object (a process is the
whole memory space assigned to it, together with this control object and all the threads
executing within the process - technically a process does not execute, it is the main
thread that is doing this), APC (Asynchronous procedure call interrupts) object, DPC
(Dispatched or Deferred Procedure Calls) object, interrupt object etc. The
dispatcher objects are involved with thread synchronization (also will be visited
in the May article) and among these are the mutex (or the mutant), event, semaphore,
timer, waitable timer, event pair etc.
Hardware support
The job of the kernel (as being low-level) is to provide the architecturally specific
routines that (try to) abstract the architectural differences and provide a coherent
interface to the outside world. Being a properly written OS, NT implements the kernel this
way. Most of the routines within the kernel are written with common code in mind.
They heavily rely on a structure closest to the hardware (and the one actually dependant
on the architecture) - the HAL (Hardware Abstraction Layer). It is
"the" low-level interface to the system and is there mainly for clarity,
maintainability and (most important) portability purposes. You are welcome to sit down and
write your own HAL, one that will take your specific architectural differences and exploit
them rightly (well, actually, if it was "that" easy, I wouldnt be sitting
here and still taking classes). Other examples of the kinds of services this part of the
kernel provides are context switching (threads run part of their time in user-mode and
part in kernel-mode), provision of a (lookaside) translation buffer and CPU cache support.
Device Drivers - commonly known as .SYSes
These are loadable kernel-mode modules (just as HAL.DLL is) and provide the interface
between the HAL as the mediator to the hardware (or actually between the naked hardware)
and the I/O subsystem within the OS. Their functions are activated mostly as a result of
an interrupt request which these drivers are supposed to service (and we will see the
exact procedure in the May article when we write such a driver). Device drivers run in one
of three contexts - user-mode, kernel-mode or in arbitrary thread context (this is the
interrupt servicing part). There are four different kinds of device drivers:
- Hardware DDs (manipulate hardware i/o devices)
- File System Drivers (interception of file-oriented i/o requests and their translation
into i/o requests bound to specific device)
- Filter drivers (or interceptors, usually provide disk mirroring, i/o interception,
data-flow encryption etc.)
- Network redirectors (handling of network traffic, low-level)
System processes
Following are main system processes:
- idle process
- system process/thread(s)
- session manager
- logon process
- local security authentication server (lsas)
- service controller
The idle process is not running a real program image. It is also known as
"process ID 2".
The system process/thread(s) are threads provided by the system process, they
have the same attributes as user-mode threads, however, are restricted to execution within
the two gigabytes of kernel memory space. System threads are commonly used by such pieces
of system software as the file server, cache manager, memory manager etc. to provide for
the kinds of services these servers respectively implement.
The session manager is the first user process that is initiated by the OS. Along
with doing system initialization, the session manager is responsible for the communication
between applications and debuggers. It is also the parent of the Logon process.
The logon process is the one that is "always there", responsible for
the usual screen answering the activation of the Secure Authentication Sequence
(normally ctrl-alt-del keys). This sequence exists to provide untemperable logins to the
system (network). The name and password received from the user are sent to the Local
Security Authentication Server (LSAS) for verification, and if everything goes okay, a
process called USERINIT.EXE is created. USERINIT creates the default user shell and
then dies a quick death (no longer needed). The authentication itself is implemented
within a .DLL called GINA (stands very imaginatively for Graphical
Identification and Authentication). Of course, if you have an idea how to
implement a system better that this one, MSGINA.DLL is replaceable and it is up to
you to make it or break it.
Local Security Authentication Server (LSAS) relies on the Security Account
Manager which provides user and account handling for the LSAS. LSAS is responsible
for creating a user profile object, the one that will have a say later in "just what
an object is supposed to do, and what not".
Service controller is the process that controls NT services, or pieces of
software UNIX people know as daemons. Writing a service is a tricky task and
requires great attention with regards to security issues. I guess one could relate the
service controller to UNIX inetd daemon.
Next month I will explain the interrupt and exception mechanisms, LPC, DPC and APC
(just a review) and will guide you through writing a simple (but model) NT device driver.
Enjoy and do not over-read!
Some useful links:
Active Matrix Hideaway
Windows NT FAQ
Windows Hacking FAQ
Windows Security FAQ
Windows Systems internals
Microsoft NT Server page(s)
Most of the material in this article is my understanding of a book "Inside Windows
NT" 2nd edition by David A. Solomon (Microsoft Press)
This article brought to you by

|