Archive for November, 2006

SDbgExt 1.09 released (support for displaying x64 EH data)

Thursday, November 23rd, 2006

I’ve put out SDbgExt 1.09. This is an incremental release of my collection of WinDbg debugger extensions.

The 1.09 release primarily adds support for displaying exception handler data on x64. While there is “some” built-in debugger support for this (via the “.fnent”) command, this support is extremely minimal. You are essentially required to dump the unwind data structures yourself and manually parse them out, which isn’t exactly fun. So, I added support for doing all of that hard work to SDbgExt, via the !fnseh SDbgExt extension (display function SEH data). This support is complementary to the !exchain command supplied by ext.dll for x86 targets.

The “!fnseh” command supports displaying most of the interesting fields of the unwind metadata (besides information on how the prologue works). It also properly supports chained unwind information records (both the documented and undocumented formats). There is also basic support for detecting and processing CL’s C/C++ exception scope tables, if a function uses C language exception handling (__try/__except/__finally).

Here’s a couple quick examples of it:

1: kd> !fnseh nt!CcPinRead
nt!CcPinRead L6a 2B,10 [ U ] nt!_C_specific_handler (C)
> fffff800012bf937 L2 (fffff800012fe4c0 -> fffff80001000000)
> fffff800012bf939 L16 (fffff800012fe4c0 -> fffff80001000000)
> fffff800012bf94f L5b (fffff800012fe4c0 -> fffff80001000000)
> fffff800012bf9aa L48 (fffff800012fe4c0 -> fffff80001000000)
> fffff800012c5199 Ld (fffff800012fe4c0 -> fffff80001000000)
> fffff800012c51a6 L58 (fffff800012fe4c0 -> fffff80001000000)
> fffff800012c51fe L1b (fffff800012fe4c0 -> fffff80001000000)
1: kd> !fnseh nt!CcCopyRead
nt!CcCopyRead Lae 3A,10 [E  ] nt!_C_specific_handler (C)
> fffff80001272c01 Lbf (fffff800012fe2e0 -> fffff800012c4c39)
> fffff80001272cc0 Lc (fffff800012fe2e0 -> fffff800012c4c39)
> fffff800012871f4 L2b (fffff800012fe2e0 -> fffff800012c4c39)
> fffff8000128721f L5a (fffff800012fe2e0 -> fffff800012c4c39)
> fffff800012961b1 L8 (fffff800012fe2c0 -> fffff800012c4b93)
> fffff800012961b9 L56 (fffff800012fe2c0 -> fffff800012c4b93)
> fffff800012c4aae Lcc (fffff800012fe2c0 -> fffff800012c4b93)
> fffff800012c4b7a L19 (fffff800012fe2c0 -> fffff800012c4b93)
1: kd> !fnseh nt!NtAllocateVirtualMemory
nt!NtAllocateVirtualMemory L5e 30,10 [E  ]
nt!_C_specific_handler (C)
> fffff8000103f74f L22 ( -> fffff8000103f771)
> fffff8000103f7f9 L16 ( -> fffff8000109adbf)
> fffff8000105ed3e L46 (fffff800010f3dc0 -> fffff8000105f173)
> fffff8000105f14d L22 ( -> fffff8000105f16f)
1: kd> !fnseh nt!KiSystemCall64
nt!KiSystemCall64 L390 50,0C [EU ]
nt!KiSystemServiceHandler (assembler/unknown)
0:000> !fnseh ntoskrnl + 00001180
401180 L2e 29,06 [  C] <none> (none)
 ntoskrnl!CcUnpinFileDataEx L32 13,05 [   ] <none> (none)

The basic output format is for unwind information as presented by the extension is as so:

EH-start-address LEH-effective-length prologue-size,unwind-code-count [unwind-flags (Exception handler, Unwind handler, Chained unwind information)] exception-handler (exception-handler-language)

Additionally, if the extension thinks that the function in question is using C language support, it will display each of the scope table entries as well (scope table entries divide up the various regions in a function that may have a __try or __except; there is typically only one lowest-level exception handler per function, with the scope table being used to implement multiple __try/__except clauses per function):

> EH-start-address LEH-effective-length (__except-filter-function (optional) -> __except-handler-function)

This information can be useful for tracking down exception filters and handlers to breakpoint on and the like, as EH registrations are completely isolated from code on x64. Note that not all functions may have exception or unwind handlers, as an unwind information is required to be provided for all x64 functions that modify the stack or call other functions.

For more information on how x64 exception handling works under the hood, you might look at my article on the subject, or skape’s paper about x64 Windows binary analysis.

The kernel object namespace and Win32, part 3

Wednesday, November 22nd, 2006

Recently, I have posted about various features of the kernel object namespace. Here’s the table of contents for this series:

  1. The kernel object namespace and Win32, part 1.
  2. The kernel object namespace and Win32, part 2.
  3. The kernel object namespace and Win32, part 3.

This posting (the last in the series) attempts to focus on the remaining two parts of the kernel namespace that are visible to Win32. These two parts are broken up as follows:

  1. DOS devices. DOS device names are object names that can be manipulated with the file management functions, such as CreateFile or DefineDosDevice. The set of DOS devices also includes traditional DOS device names (hence the name), such as NUL, CON, AUX, and soforth. This object namespace is “sessionized” by Terminal Server.
  2. Window station and desktop objects. These are infact named kernel objects, although this fact is only barely made visible to Win32. Like DOS devices, window stations and desktops have a namespace that is “sessionized” in the presence of Terminal Server.

Frist, I’ll cover how DOS devices are “sessionized”. This information is as of how the namespace is implemented in Windows XP and later.

In Windows XP, DOS devices are virtualized based off of the LSA logon session ID a token has associated with it (otherwise known as an authentication ID). This value is actually a bit more granular than the Terminal Server session ID.

While a Terminal Server session ID represents a complete “terminal” (essentially, a complete input/output stack with all of the software support that goes with that – a “session”), an LSA logon session ID simply represents an instance of a user that is logged on to the local computer.

Now, in normal circumstances, there is typically a very similar correlation between Terminal Server sessions and LSA logon sessions. For each Terminal Server session, there is typically at least one LSA logon session, which corresponds to the user that is logged on interactively to that Terminal Server session. However, this is not necessarily a one-to-one relationship; for instance, a Terminal Server session that is idle does not really have an LSA logon session ID associated with it (other than perhaps the system logon session, under which winlogon and csrss operate). Or, a Terminal Server session could have many LSA logon session IDs associated with it; think of the case where a user starts a program under a different account with the “RunAs” command. To add to that, some LSA logon session IDs may not even correspond to a local user at all; for example, a remote network logon from a different computer that corresponds to an attempt to access a share hosted on the local machine may be given a logon session ID, even though it corresponds to no user physically logged on to the local machine.

The reason why DOS devices must be virtualized is fairly obvious; different users may have different drive maps, and you need to be able to map the same drive letter as someone else in a different session. The way this was handled needed to be more granular than Terminal Server sessions, though (at least in Windows XP); this is due to how RunAs interacts with drive share access and network drive mappings.

Specifically, the behavior of associating DOS devices with an LSA logon session ID is used because the credentials used to access a remote share are associated with the LSA logon session of the calling user. This means that if you start a program under RunAs, it won’t have access to the same credentials as the parent LSA logon session. As a result, a program started via RunAs doesn’t really have access to network drive mappings (in particular, drive letter based network drive mappings). Because of this, it makes more sense to isolate DOS devices based on LSA logon sessions than Terminal Server sessions, as a separate LSA logon session cannot access drive share connections made by a different LSA logon session anyway.

The resulting architecture that isolates DOS devices on a per-LSA-logon-session basis thus operates as follows:

For each Terminal Server session ID, there is an object directory of the name \Sessions\<TS-session-ID>\DosDevices. Although this object directory is created under the session-named directory for each Terminal Server logon session, only the object directory \Sessions\0\DosDevices is actually used in Windows XP and beyond.

In the the \Sessions\0\DosDevices directory, there is a set of subdirectories that are each named by a LSA logon session id, in the form of a 64-bit hexadecimal number with two 32-bit halves separated by a dash, with each 32-bit half padded out to 8 characters by leading zeros (Terminal Server session IDs used to name the \Sessions\<TS-session-ID> object directories are decimal with no leading zeroes). So, you might see a directory named in the form \Sessions\0\DosDevices\00000000-00b73dfe for an LSA logon session ID 0x0000000000b73dfe.

Each of these object directories named by an LSA logon session ID contains any DOS devices that were created by that LSA logon session. The way the system evaluates DOS device names is by first trying the LSA logon session local object directory, and then trying the global DOS device object directory (which is named \GLOBAL??, and has DOS devices that are associated with no one session in particular, such as physical hard drive DOS device names like C:, or predefined DOS devices like NUL or PhysicalDrive0). To override this lookup behavior and specify that a DOS device name only references the corresponding object in the global DOS device directory, the “Global\” prefix may be used with the DOS device name (similar to named kernel objects, such as mutex objects). This support is handled by a symbolic link named “Global” that is created in each LSA-logon-session ID-named object directory. This symbolic link refers to the “\GLOBAL??” object directory. (Of course, only \DosDevices is publicly documented, to my knowledge.)

If you’ve been reading the previous articles, you may be wondering how the “\DosDevices” and “\??” object directories relate to the “\GLOBAL??” object directory that I just said contained all global DOS device symbolic links (after all, the DDK documents these symbolic links as being present in “\DosDevices”). Well, the way it ends up working out is that there is a symbolic link named “\DosDevices” which points to “\??”. The “\??” name is a bit of a hack; it is not actually a real object name, but instead a hardcoded special case in the object manager’s parser routines that is interpreted as a reference to the global DOS device directory. (This was done for performance reasons; it is very common to look up objects in the \DosDevices directory, and adding a special case check first, before the normal object directory traversal speeds up name translations for Win32 names a bit.) As a result, you can actually use “\??, “\DosDevices”, and “\GLOBAL??” to refer to global DOS devices; they all end up pointing to the same underlying location (eventually).

The other part of Terminal Server session instancing on the kernel object namespace deals with window stations and desktops. Window stations for a particular Terminal Server session are placed in a directory named in the form of \Sessions\<TS-Session-ID>\Windows\WindowStations\<Window-Station-Name> (desktop objects have names that treat a window station as an object directory, such as \Sessions\2\Windows\WindowStations\WinSta0\Default). Note that for session zero, this convention is slightly different; instead, window stations for session zero reside in \Windows\WindowStations.

That wraps up the series on the kernel object namespace (as it relates to Win32). By now, you should have a working understanding of how to use the kernel object namespace from Win32, and how the “magic” behind Terminal Server and LSA logon session isolation is actually implemented. (It is worth noting that the abstraction layer that Win32 provides added real value here, as there were no gross hacks required to be added on to the kernel object namespace to support Terminal Server. Instead, everything is implemented as plain object directories and symbolic links.)

Terminal Server session isolation for the rest of Win32 (namely, the input/output portion) is much more complicated and is hardly as clean as the separation imposed on the object namespace is. That, however, is a story for a different series…

Frame pointer omission (FPO) optimization and consequences when debugging, part 1

Tuesday, November 21st, 2006

During the course of debugging programs, you’ve probably ran into the term “FPO” once or twice. FPO refers to a specific class of compiler optimizations that, on x86, deal with how the compiler accesses local variables and stack-based arguments.

With a function that uses local variables (and/or stack-based arguments), the compiler needs a mechanism to reference these values on the stack. Typically, this is done in one of two ways:

  • Access local variables directly from the stack pointer (esp). This is the behavior if FPO optimization is enabled. While this does not require a separate register to track the location of locals and arguments, as is needed if FPO optimization is disabled, it makes the generated code slightly more complicated. In particular, the displacement from esp of locals and arguments actually changes as the function is executed, due to things like function calls or other instructions that modify the stack. As a result, the compiler must keep track of the actual displacement from the current esp value at each location in a function where a stack-based value is referenced. This is typically not a big deal for a compiler to do, but in hand written assembler, this can get a bit tricky.
  • Dedicate a register to point to a fixed location on the stack relative to local variables and and stack-based arguments, and use this register to access locals and arguments. This is the behavior if FPO optimization is disabled. The convention is to use the ebp register to access locals and stack arguments. Ebp is typically setup such that the first stack argument can be found at [ebp+08], with local variables typically at a negative displacement from ebp.

A typical prologue for a function with FPO optimization disabled might look like this:

push   ebp               ; save away old ebp (nonvolatile)
mov    ebp, esp          ; load ebp with the stack pointer
sub    esp, sizeoflocals ; reserve space for locals
...                      ; rest of function

The main concept is that FPO optimization is disabled, a function will immediately save away ebp (as the first operation touching the stack), and then load ebp with the current stack pointer. This sets up a stack layout like so (relative to ebp):

[ebp-01]   Last byte of the last local variable
[ebp+00]   Old ebp value
[ebp+04]   Return address
[ebp+08]   First argument...

Thereafter, the function will always use ebp to access locals and stack based arguments. (The prologue of the function may vary a bit, especially with functions using a variation __SEH_prolog to setup an initial SEH frame, but the end result is always the same with respect to the stack layout relative to ebp.)

This does (as previously stated) make it so that the ebp register is not available for other uses to the register allocator. However, this performance hit is usually not enough to be a large concern relative to a function compiled with FPO optimization turned on. Furthermore, there are a number of conditions that require a function to use a frame pointer which you may hit anyway:

  • Any function using SEH must use a frame pointer, as when an exception occurs, there is no way to know the displacement of local variables from the esp value (stack pointer) at exception dispatching (the exception could have happened anywhere, and operations like making function calls or setting up stack arguments for a function call modify the value of esp).
  • Any function using automatic C++ objects with destructors must use SEH for compiler unwind support. This means that most C++ functions end up with FPO optimization disabled. (It is possible to change the compiler assumptions about SEH exceptions and C++ unwinding, but the default [and recommended setting] is to unwind objects when an SEH exception occurs.)
  • Any function using _alloca to dynamically allocate memory on the stack must use a frame pointer (and thus have FPO optimization disabled), as the displacement from esp for local variables and arguments can change at runtime and is not known to the compiler at compile time when code is being generated.

Because of these restrictions, many functions you may be writing will already have FPO optimization disabled, without you having explicitly turned it off. However, it is still likely that many of your functions that do not meet the above criteria have FPO optimization enabled, and thus do not use ebp to reference locals and stack arguments.

Now that you have a general idea of just what FPO optimization does, I’ll cover cover why it is to your advantage to turn off FPO optimization globally when debugging certain classes of problems in the second half of this series. (It is actually the case that most shipping Microsoft system code turns off FPO as well, so you can rest assured that a real cost benefit analysis has been done between FPO and non-FPO optimized code, and it is overall better to disable FPO optimization in the general case.)

Update: Pavel Lebedinsky points out that the C++ support for SEH exceptions is disabled by default for new projects in VS2005 (and that it is no longer the recommended setting). For most programs built prior to VS2005 and using the defaults at that time, though, the above statement about C++ destructors causing SEH to be used for a function (and thus requiring the use of a frame pointer) still applies.

Impressions of Vista

Monday, November 20th, 2006

Since Vista went RTM, I decided that it was finally time to bite the bullet, so to speak, and install a copy of Vista on a box I use on at least a semiregular basis (since I’ll have to deal with it sooner or later, as it’s going to be in stores soon).

I went into this with a fair amount of reservations, having encountered plenty of annoyances with Vista before (things like UAC prompts as a limited user for things like event viewer or regedit, or other not-so-polished things that were really annoying in the previous releases). However, I pretty much reversed my opinion after what I encountered…

The first thing was installation. Gone completely is text mode setup (although that isn’t something that I am all that excited about – setup is setup, after all, as long is it works). It’s definitely a big visual change from the previous versions of setup, and it’s perhaps a bit less power-user-ish (no option to format drives as NTFS or things like change the Windows install directory on a clean install that I saw, although I might have missed something). Installation went fairly smoothly, although it definitely took it’s sweet time.

The first real surprise, though, was that after Vista (finally) finished installing, I did not need a single third party driver. Not one. Even my Bluetooth transceiver and my GeForce 6800 Go just worked out of the box at full functionality without my having to do anything extra.

This, in and of itself, is a pretty impressive thing. Normally, you’d virtually always have to go and install drivers for your wireless NIC, bluetooth adapter, video card (if you wanted to do 3D), and soforth, but with my Vista install, everything was golden out of the box. Obviously, your experience my vary depending on your hardware, but the idea of the whole thing working at full functionality out of the box (for a non-server Windows flavor) on a brand new (clean) install is something I haven’t had in a looong time.

Now, that isn’t to say that things were perfect. I did have to do a fair amount of tweaking settings to get things to my liking, but most of the things I had to change were truly user preference type things and not necessities like setting up drivers and the like.

Like Srv03 x64, Vista right away prompts you to enable Windows Update in autoinstall mode (and to check for updates right now. This is definitely a Good Thing from a security perspective for your average user, who is likely to not even bother with that sort of thing unless you put it right in their face the first time they log on to their new box).

For my configuration, I had the Vista box joined to a domain, and was intending to use a domain account (a limited user) as my main user account. This turned out to work fairly well; there was the usual stuff with adding the computer to the domain, but unlike in Windows XP, joining the box to the domain doesn’t turn off the user switching features (or new logon UI). Being able to run multiple sessions is a real benefit for me, since I run as a limited user; instead of doing runas, for complex tasks, I find that it is much easier to just start another session up entirely. Previously, you could only do this on server OS’s by starting, say, a TS session to localhost and logged on as admin. With Vista, you can just do that naturally with user switching, even if you’re joined to the domain.

Speaking of user switching and sound/audio, another nice change is that switching sessions doesn’t abruptly terminate A/V output during the session switch, like in XP (where you would allyhave to restart your song in WMP after coming back to your original session). The new audio architecture (you can thank Larry Osterman in part for that) is also pretty cool, that is, being able to control volume on a per-application basis even for things that don’t have their own volume sliders.

A couple of other pet peeves of mine from XP are fixed as well. For instance, you finally no longer need to have the SeSystemtimePrivilege in order to pull up a calendar by clicking on the clock in the notify icon area. Also, the “there is nothing interesting here, go away” nag notices that used to plague the root partition, Windows, and System32 directories of your Windows install after a fresh install are gone as well.

UAC turned out to be not quite as bad as I had thought it would be. I was already planning on running as a limited user, so for me, UAC is pretty much just a glorified RunAs. Looking at it in that sense, it’s somewhat better than before, in that you can start some things as yourself and only later convert them to admin if necessary (especially good for things launched by other applications instead of the shell, where you might not necessarily have a good place to insert a “runas” command or shortcut to boost the new process to admin). I did end up having to disable UAC filesystem and registry virtualization, though (which is under Security Options in Group Policy, thanks to Alex Ionescu for that tip – be warned that it looks like you need to reboot and not just use “gpudate.exe /force” to apply the policy, though). UAC virtualization ended up being the root cause of some nasty performance issues in a couple of programs that I was using, which was strange given that I had already done the work necessary to allow them write access to things that they needed to write to in order to function. On the whole, though, I think I’ll be able to live with UAC as a limited user.

UAC is a bit over the top for my tasks as administrator, since the only time I’m doing something as admin is when I know that I really want to do it as admin, which makes the UAC confirmations there rather redundant at best. However, I still prefer the “improved RunAs” aspect of it while running as a limited user, so I think I’ll keep UAC enabled for now (especially since I don’t end up having to do most daily tasks as admin, just installing things and reconfiguring system-wide things for the most part). Having lived as a limited user on Windows XP and Windows Server 2003 for a long time now, this didn’t turn out to be all that much of a shocking change as it might be for someone who is used to only running as an admin.

Last but not least is DWM and the new video driver model. The box I put Vista on to play around with has a GeForce 6800, which (as I mentioned earlier) came with drivers out of the box. It just so happened that those drivers support WDDM (the new video driver model), though the card itself only implements the DX9 DDI (the first DirectX 10 video cards were just released a week or two ago; the GeForce 8 class from NVIDIA).

Vista enabled Aero Glass up-front, which is essentially the transparency and 3D window flip switching support that you may have heard about. This is somewhat cool in and of itself, but what really blew me away is how it worked when I went ahead and tossed an intensive 3D application at the WDDM drivers – World of Warcraft (naturally). WoW had a bit of weirdness running on Vista at first – it ran really, really slow in windowed mode until I, through trial and error, determined that doing ctrl-alt-del and switching to the secure desktop once after starting WoW (extremely mysteriously) cures the slowness. After working around that little bit of strangeness, I was really surprised to find out that in windowed mode, you could do 3D window flipping (or have Aero windows obscure WoW) while WoW was running and drawing at full speed, with practically no major drop in performance. Seeing that work out with a full D3D program rendering away into a window while in flip-window mode alongside the rest of my desktop applications was definitely very cool, especially when you consider that it all magically “just works” without any modifications to either DirectX programs or plain vanilla GDI programs.

All things considered, I can say that Vista is definitely was more solid than I thought it would be. There were a couple of snags along the way, but none any worse than with previous versions of Windows, and on the whole, a lot more painless in my experience (at least for my system, the hell of trying to find the NVIDIA video driver that crashed the least was gone – the one that shipped with the OS really worked, and while twice I got a “Your video card has encountered a problem and has recovered” bubble in the notification area, my system didn’t bluescreen and none of my programs crashed – a *big* improvement over what I’ve seen from NVIDIA with Windows XP and previous driver versions written against the old display driver model, to be certain). There are other less than savory aspects to consider though, such as the even more pervasive DRM tied in with the OS, although for me, that is more a philosophical problem than a practical problem as I make a point of not buying DRM-laden content. Even so, I’m definitely looking forward to it becoming generally available in the next month or two.

Windows Vista RTM released to MSDN subscribers

Friday, November 17th, 2006

If you’ve got an MSDN subscription with operating systems included, you can now head on over to MSDN subscriber downloads and grab a copy of Windows Vista RTM (final build).

You can open a PE image as a dump file with WinDbg

Friday, November 17th, 2006

There is a little known feature of WinDbg, ntsd, cdb, kd, and anything else that uses DbgEng to open dump files.

It turns out that with anything powered by DbgEng, anywhere where you could open a dump file (user dump, kernel dump, etc), you can instead open a PE image (.exe/.dll/.sys/etc) and have the debugger treat it as a dump containing just the contents of the selected PE image.

This is actually a relatively useful feature. When you open a PE image as a dump file, the debugger maps it as an image as if it were loaded in-memory as executable code (though it doesn’t actually run any code, just maps it as if it were an executable and not a data file). This gets you an in-memory representation of your exe/dll/sys/other PE file as if you were debugging a live process (or a dump) that had the image in question loaded.

Like a dump debugging session, this is essentially a read-only session; you can’t really modify anything, as there is no target to control. Additionally, there is no real register context either (or stack or heap), although things like initialized and zero filled global variables and executable code belonging to the module will be in-memory. (The preferred image base for the module is used in this situation for basing the requested PE module in the virtual address space constructed for the debugging session.)

After you have loaded the target, you can do anything that you would normally do with a dump for the most part, as far as examining symbols and disassembling the target go. If you need a disassembler with symbol support and can’t start a process or whatnot to contain a PE image, this particular trick is a great quick-n-dirty replacement for a more full-featured disassembler program.

Note that a side effect of opening a PE image in dump mode is that the symbol server is used to retrieve the binary (which might seem a bit strange, until you consider that for dump files, the normal case is that you don’t have the entire binary saved in memory; just enough header information to retrieve the binary from the symbol server). Therefore, make sure that your symbol path is setup correctly before trying this particular trick.

The troolean strikes back

Thursday, November 16th, 2006

There is a particularly amusing curiousity in Microsoft Win32 code that I have seen crop up a couple of times over the year: The “troolean”, or the “boolean” value with true/false/other as possible values.

You have probably seen this before in the winuser messaging APIs, such as the classic example of GetMessage:

    __out LPMSG lpMsg,
    __in_opt HWND hWnd,
    __in UINT wMsgFilterMin,
    __in UINT wMsgFilterMax);

According to the GetMessage documentation on MSDN, the return value has the following meanings:

If the function retrieves a message other than WM_QUIT, the return value is nonzero.

If the function retrieves the WM_QUIT message, the return value is zero.

If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

Yep, it’s zero, non-zero, or negative one.

Okay, I’ll give Microsoft a bit of slack there, since GetMessage has been around since the 16-bit Windows days, and the alternate return value for GetMessage was probably not there in the original design.

The thing that really got me started writing this article, though, was a little gem that I found while poking around in the CRT source code included with Visual Studio 2005. In “mstartup.cpp”, I found the following bit of code which is apparently used for Managed C++ support (which is not exactly legacy/old code):

class TriBool
    enum State { False = 0, True = -1, Unknown = 2 };

I guess trooleans are still all the rage these days (for some of us, anyway). Perhaps somebody needs to introduce the CRT programmer(s) to The Daily WTF

The kernel object namespace and Win32, part 2

Wednesday, November 15th, 2006

Last time, I talked about how the kernel object namespace intersects with the Win32 world as of how things stood in NT4 (and Windows 2000 when Terminal Server is disabled).

Although the object namespace model used in these operating systems worked well, some cracks began to appear in it with the introduction of Terminal Server as a mainstream product (and later, RunAs).

There are basically two problems that are imposed by Terminal Server:

  1. Many programs are designed to run as “singleton” programs, but in multi-session environments like Terminal Server, it is desirable to allow each session to run an instance of such programs. The classic way to enforce singleton behavior like this is to create a named kernel object at startup, and check to see if the object already existed before the program tried to create it. Without alterations to how the object namespace presented to Win32 functions, this would prevent singleton applications from working under Terminal Server.
  2. Drive letters and other “DOS Devices” symbolic links need to become “sessionized”, because the “glass terminal” (physical computer console) typically has things like COM1 or LPT1 pointing to physical serial ports or printer ports, whereas Terminal Server sessions might be using device redirection to point those device names to serial ports or printer ports on the Terminal Server client machine.

The solution to this problem was partitioning the view of the kernel object namespace provided to Win32 based on Terminal Server session id. This is done with the use of a set of symbolic links and object directories.

The basic idea is that session zero continues to use the \BaseNamedObjects object directory, as how things used to work on downlevel systems. In this object directory, there are a couple of new symbolic links:

  • \Local, which points to the session local namespace. For session zero, this symbolic link typically points to \BaseNamedObjects. For other sessions, it points to a “sessionized” namespace, such as \Sessions\<Terminal-Server-Session-ID>\BaseNamedObjects.
  • \Global, which always points to the session zero namespace (the “global” namespace). This always points to \BaseNamedObjects for all sessions.
  • \Session, which points to \Sessions\BNOLINKS. This latter symbolic link is not documented (except to say that it is “reserved for system use”), but its function is to allow one session (with the appropriate access granted to it) to create objects in an arbitrary session local namespace, by using a path in the form \Session\<Terminal-Server-Session-ID>\ObjectName. \Sessions\BNOLINKS is an object directory which contains a set of symbolic link objects, each named after a Terminal Server session ID. These links point to the appropriate “sessionized” BaseNamedObjects directory for each session (such as \BaseNamedObjects for session zero, \Sessions\1\BaseNamedObjects for session 1, and soforth).

For sessions other than session zero, a BasedNamedObjects directory named in the form of \Sessions\<Terminal-Server-Session-ID>\BaseNamedObjects is created. This is the session local namespace for that session, and it is what is linked to via the “\Local” symbolic link. Additionally, a corresponding symbolic link in \Sessions\BNOLINKS is created so that the (undocumented) “\Session” link works for the new session.

This scheme allows for maximum compatibility with pre-Terminal Server applications which are not aware of the “session isolation” concept, and need some help in order to have their named objects placed in a “sessionized” location where they will not conflict with other user sessions. A means for programs that truly need globally-accessible object names is also provided (the magical \Global prefix symbolic link), which is typically used by services and user-level UI applications that need to communicate with their privileged service counterpart.

In addition to the session isolation of kernel object names, “DOS device” names also became isolated based on Terminal Server session ID. The way this works differs between Windows 2000 and future OS versions, though; I’ll cover it in the next installment of this series. The basic idea for Windows 2000 is that each session got its own directory for DOS device names, but Windows XP and beyond go one step further to better accomodate the “runas” case.

Blog DNS issues fixed

Wednesday, November 15th, 2006

There was a small DNS glitch affecting access to the site which was resolved yesterday – sorry about that.

Win32 calling conventions review

Friday, November 10th, 2006

Recently, I’ve posted about the Win32 calling conventions. Here’s a table of contents of the various different posts I’ve made.

  1. Win32 calling conventions: Concepts
  2. Win32 calling conventions: Usage cases
  3. Win32 calling conventions: __cdecl in assembler
  4. Win32 calling conventions: __stdcall in assembler
  5. Win32 calling conventions: __fastcall in assembler
  6. Win32 calling conventions: __thiscall in assembler

Remember that when picking a calling convention to use, there are a number of factors to consider. There is no one calling convention that fits all cases (however, __stdcall is a good default if you are not sure).

Hopefully, you’ll have found this series to be enlightening, useful, and practically applicable.