No, a ??\C:\...
without the leading \
is not valid, to the best of my knowledge. Perhaps some libraries will accept this as input, however, and convert it appropriately. Or perhaps you missed some bits from the paths you copied and pasted?
Either way we'll stick to the (NT flavored) Windows behavior.
However, \??\C:\...
definitely is. You can find a comprehensive discussion here and the official documentation here. Some of the aspects we need to know are also explained here.
Some implementation details
Quoting from the Google Project Zero page above:
There are 7 types of path that the Win32 API distinguishes between,
and potentially does different things with. NTDLL has a function,
RtlDetermineDosPathNameType_U
, which, given a Unicode string will
return you the path type. We’ll go through each one of these types in
the next section. The following prototype can be used to call this
function:
enum RTL_PATH_TYPE {
RtlPathTypeUnknown,
RtlPathTypeUncAbsolute,
RtlPathTypeDriveAbsolute,
RtlPathTypeDriveRelative,
RtlPathTypeRooted,
RtlPathTypeRelative,
RtlPathTypeLocalDevice,
RtlPathTypeRootLocalDevice
};
RTL_PATH_TYPE NTAPI RtlDetermineDosPathNameType_U(_In_ PCWSTR Path);
To follow along, download WinObj from Sysinternals/Microsoft and start it elevated (right-click + "Run as Administrator").
Potentially valid paths you may see
You have a number of path representations (as discussed in-depth in the linked articles). Let's consider the path C:\Windows\System32\csrss.exe
you gave:
- Win32 path:
C:\Windows\System32\csrss.exe
(\Windows\System32\csrss.exe
would be a variation that depends on the current directory at the time the path is processed, if it is somewhere on C:
they should be identical)
- NT native path:
\??\C:\Windows\System32\csrss.exe
\GLOBAL??\C:\Windows\System32\csrss.exe
\SystemRoot\System32\csrss.exe
- Local device path:
\\.\C:\Windows\System32\csrss.exe
- Root local device path:
\\?\C:\Windows\System32\csrss.exe
- DOS paths: this is relevant for all paths whose individual path segments are longer than the classic 8.3 DOS path name segments. If configured, a 8.3 DOS name will be generated (usually something in the form of
C:\LongPathName
-> C:\LONGPA~1
) ... this may be important for forensic purposes (you quote volatility), but is mostly legacy behavior.
UNC paths also exist and in fact a local device path (\\.\...
) looks like a UNC path with server name .
which could even be a thing, considering .
is the current directory further down in paths, so why not the current machine up top? Either way, go read the articles if you want to know about those in more detail.
Starting at the top, drilling down
C:\Windows\System32\csrss.exe
when passed to an API like CreateFile
will be converted to \??\C:\Windows\System32\csrss.exe
which the object manager then takes in for processing.
- If a relative path was given, there will be additionally processing to convert to a full path, basically.
\\.\C:\Windows\System32\csrss.exe
when passed to an API like CreateFile
will be "converted" to \??\C:\Windows\System32\csrss.exe
after some processing. Just like without the \\.\
prefix some additional processing happens before this path is passed down to the object manager.
\\?\C:\Windows\System32\csrss.exe
when passed to an API like CreateFile
will be "converted" to \??\C:\Windows\System32\csrss.exe
. No further processing happens at the Win32 level. This is basically an escape route straight to the object manager (and in the Fun facts section I'll share a related fun fact).
The object manager view
Receiving a path like \??\C:\Windows\System32\csrss.exe
the object manager attempts to find \??
first.
\??
used to be an actual item, these days it is a "view" of \GLOBAL??
overlaid with your session-specific DosDevices
.
- An example of session-specific
DosDevices
would be mapped network shares.
- For a drive letter
F:
mapped to \\fileserver\Share
- Under the hood you will have a session-specific
DosDevices
object directory like \Sessions\0\DosDevices\00000000-00261e8a
which would contain a symbolic link named F:
, pointing to \Device\LanmanRedirector\;F:0000000000261e8a\fileserver\Share
... or similar.
- So in essence when looking up names in
\??
the object manager will look in your session specific DosDevices
, followed by \GLOBAL??
(or was it the other way around? )
- Another example would be VeraCrypt mounted volumes. For example your drive letter
T:
may map to \Device\VeraCryptVolumeT
- Having figured out that
C:
is \GLOBAL??\C:
in our case, the object manager checks what C:
is. On my Windows 10 it figures out that this is a symbolic link pointing to \Device\HarddiskVolume3
.
- Having found the device (and implicitly the driver associated with it) the object manager now will pass the remainder of the path (
\Windows\System32\csrss.exe
) to the device \Device\HarddiskVolume3
, whose driver will hopefully do the right thing, create a file object and so on ...
And now that you know this you can follow along on your system for arbitrary paths.
What about \SystemRoot
?
Well, on modern Windows versions the behavior differs a little from how it used to be, but \SystemRoot
is a symbolic link resolving to the Windows directory on all NT-based Windows versions I am aware of. The Win32 subsystem parrots this in the %SystemRoot%
environment variable (albeit in Win32 path notation).
On my Windows 10 \SystemRoot
is a symbolic link to \Device\BootDevice\windows
and so the path to a driver (which is the typical use case in the registry) can be expressed much more easily for early boot drivers (less processing needed by the object manager).
As a side note: on my system - as you'd expect - \Device\BootDevice
maps to \Device\HarddiskVolume3
.
Fun facts
- The
DosDevices
object directory cited in the Google Project Zero article is no longer a thing. These days it's a symbolic link to \??
(not \GLOBAL??
).
- The
subst
command line utility allows you to create symbolic links inside your session-specific DosDevices
object directory to map some directory path to a drive letter (under the hood uses DefineDosDevice()
).
- Files are unnamed objects, that is they have no name that could be directly processed by the NT object manager. Instead the remainder of a file name - after reparsing etc - will be passed to the driver responsible for the device object representing the file system volume (i.e. partition). That driver then takes care of making sense of the name.
\REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
(while this doesn't name a file, it is a perfectly valid native path to what you would see as HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
That escape routes named \??\
and \\?\
I mentioned before that the prefix \??\
is an escape route. Consider a file/folder path with a trailing dot, something you will not be able to create or access ordinarily from the Win32 subsystem. Similarly the reserved DOS device names (e.g. NUL
, COM1
etc.) cannot be created or manipulated ordinarily.
Little experiment ...
- start
cmd.exe
(Win+R)
- go to your desktop folder:
cd /d "%USERPROFILE%\Desktop"
- Now create a new file
test.txt.
(note the trailing dot): echo . > \??\%CD%\test.txt.
(the %CD%
refers to the current directory)
- Use Windows Explorer to navigate to your desktop folder. Try to open the file
test.txt.
by double-clicking. Pick Notepad
when it asks you what program to use to open it.
- You should see:

... please note the path (without trailing dot).
- Now try to delete the file from Windows Explorer. It won't work.
NB: Some alternative file managers like Far or Speed Commander won't have trouble with this!
- Now switch back to the command line window and use
del /f \\?\%CD%\test.txt.
Did you notice something? I sneakily changed from \??\
to \\?\
because apparently the implementation of del
doesn't like the former.
Depending on the implementation of your specific program it can vary whether \??\
or \\?\
is successful.
Another experiment would be to create reserved names like NUL
or COM1
or tinker with WSL2's per-directory case-sensitivity.
Note
- Win32 may sound outdated, but the Win32 subsystem and the respective "Windows personality" implemented through
csrss.exe
and its subsystem DLLs (kernel32.dll
and friends) was called just that. Besides, if you're picky it may bother you particularly to find out that all the native 64-bit binaries on a modern x64 Windows reside in %SystemRoot%\System32
(<- yes, that's a 32 right there ). Enjoy!
Further reading
In addition to the articles from the top, I can recommend these books:
- Obviously the Windows Internals book series.
- Windows System Programming by Pavel Yosifovich also has some details