Virtual Disk Drivers, UAC and NT

Macrium Reflect, as you may know, has the ability to mount your backup images as drives in Windows Explorer. We do this via a device driver which presents your image as a disk drive to the system.

One of the major wishlist items for Reflect has been a standalone installer for our image mounting product. Conceptually, this isn’t too hard to do, except our driver was never really written for that purpose; so as part of the work developing this product, I made quite a number of modifications to the driver, including adding support for the mount manager and plug ‘n’ play support so that our images are ejectable along with any other removable medium.

First Problem: UAC, or, why DefineDosDevice does not play with Vista or greater

The traditional route for creating drive letters was to do something like this:

[c]
TCHAR VolumeName[] = _T("Z:");
TCHAR DeviceName[] = _T("\Device\VDisk1");

if (!DefineDosDevice(
DDD_RAW_TARGET_PATH,
&VolumeName[4],
DeviceName
))
{
// error case
}
[/c]

However, be aware – this does not work with UAC; at least, not as expected. Firstly – DefineDosDevice creates a symbolic link from a given drive letter to your device. In principle, this is exactly what you want to achieve; however, depending on who you are depends on where the link is created:

  • NTAUTHORITYSYSTEM: link is created in \GLOBAL??
  • Administrator running as elevated – link is created in the elevated user’s session space
  • Administrator running as normal privileges – link is created in the normal user’s session space

So, depending on who you are depends on how visible your drive letter is. For example, if you create the drive as an administrator running as a normal user, when you run explorer in elevated mode you won’t be able to see the drive! Conversely, drives created from an elevated mode are invisible to the non-elevated users.

This situation arises because as of Windows Vista, DosDevices are by default local to your current session and with UAC enabled, an administrator user logs in with two sessions – one with the privileged administrator token and one with the filtered normal privileges token.

By constrast, NTAUTHORITYSYSTEM can DefineDosDevice into the \GLOBAL?? space, which is the behaviour for all users on Vista.

So the first challenge – how to make a device accessible to normal, unprivileged users. Normally, they cannot DeviceIoControl to devices with the default permissions. Luckily, using a SDDL string, we can describe the device’s permissions:

[c]
RtlInitUnicodeString(&usSDDL,
_T("D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;BU)(A;;GA;;;WD)"));

ntStatus = IoCreateDeviceSecure(
pDriverObject,
sizeof(DEVICE_EXTENSION), // we defined this structure
&usDeviceName, // name of device.
FILE_DEVICE_DISK, // Present disk device
FILE_DEVICE_SECURE_OPEN, // don’t allow trailing names.
FALSE,
&usSDDL, // security descriptor
NULL,
&pDeviceObject
);
[/c]

Just for interest’s sake, if you’re lost as to what I’m talking about – download WinObj and have a poke around. You should be able to find all your sessions and symlinks:

Screenshot of the WinObj program in operation.

So, getting this far we can mount a device either for the unprivileged administrator, or for the privileged one. Alternatively, we can run a service and create a symlink as NT AUTHORITYSYSTEM.

This is not a particularly polished solution, although it does work.

Second Problem: The NT Mount Manager

Now, whilst DefineDosDevice works, for all intents and purposes, we’re not really obeying NT’s management of volumes at all. To do this properly, we really need to use the NT Mount Manager. Three messages matter:

  • IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION – let the mount manager know we’ve arrived.
  • IOCTL_MOUNTMGR_CREATE_POINT – create a mount point, e.g. a drive letter.
  • IOCTL_MOUNTMGR_DELETE_POINTS – Undo CREATE_POINT.

Using these, and responding appropriately, we can create a volume via the mount manager which will show up in the global win32 devices namespace as \GLOBAL??Volume{GUID} and appear as a link \GLOBALX: -> DeviceTarget.

Combined with IoCreateDeviceSecure, this allows us to receive messages from our userland-side code, e.g. Reflect, and let Windows know a volume has arrived. We can then remove it when the times comes for this, too.

This has a number of advantages over the Windows 9x-era solution set:

  1. Works with UAC. This is a really crucial part of modern Windows, and requiring customers disable it would be poor. Similarly, we should be able to mount images from both elevated and normal states, which we now can.
  2. Delegates handling drive letters to Windows. Although we might request a certain letter, Windows knows if these have already been allocated or it cannot hand us one for some reason – and sorts that out for us.
  3. Plays correctly in every other possible way with Windows Volume Management, should we wish to support these features in the future.

What would I like to bring in next? Plug and Play support. It would be great to be able to eject images from the PnP manager as if they were devices. Oh, and we have some other ideas in progress too.