Windows - Finding the System Root Path

05 Apr, 21
Tags:
22
0
CDRIVE

This article describes how to retrieves the System Root path and demonstrates the use of ZwOpenSymbolicLinkObject, ZwQuerySymbolicLinkObject, IoGetDeviceObjectPointer, and RtlVolumeDeviceToDosName. The path is found by opening then querying the symbolic link "\SystemRoot".  The drive letter is then found by calling RtlVolumeDeviceToDosName.  Source code only. 

Note, amongst other methods, it is also possible to retrieve the system root path from the registry HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PathName.  However, this sample was designed to show the use of certain API's thus these other methods fall outside the scope of this sample driver.

// **************************************************************************************
// System Root Driver
// This driver prints the system root path and drive to the debugger.  
//
// Jun 2004 - DriverEntry (www.DriverEntry.com)
// **************************************************************************************

//#######################################################################################
// I N C L U D E S
//#######################################################################################

#include <ntddk.h>

//#######################################################################################
// D E F I N E S
//#######################################################################################

#define SYSTEM_ROOT        L"\\SystemRoot"

//#######################################################################################
// P R O T O T Y P E S
//#######################################################################################

NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath );

// Allow the DriverEntry routine to be discarded once initialization is completed
#pragma alloc_text ( INIT, DriverEntry )

//***************************************************************************************
// NAME:        DriverUnload
//
// DESCRIPTION: This routine is our dynamic unload entry point.
//					
// PARAMETERS:  DriverObject    IN  Address of our DRIVER_OBJECT
//
// IRQL:        IRQL_PASSIVE_LEVEL
//
// RETURNS:     None
//***************************************************************************************
VOID DriverUnload( IN PDRIVER_OBJECT DriverObject )
{
    // Unloading - no resources to free so just return.
    DbgPrint( "Driver Unloading...\n" );    
    return;
}

//***************************************************************************************
// NAME:        GetSymbolicLink
//
// DESCRIPTION: Given a symbolic link name this routine returns a string with the 
//              links destination and a handle to the open link name.
//                
// PARAMETERS:  SymbolicLinkName   IN
//              SymbolicLink       OUT
//              LinkHandle         OUT
//
// IRQL:        IRQL_PASSIVE_LEVEL
//
// RETURNS:     STATUS_SUCCESS
//              STATUS_UNSUCCESSFUL
//
// NOTE:        Caller must free SymbolicLink->Buffer AND close the LinkHandle 
//              after a successful call to this routine.
//***************************************************************************************
static
NTSTATUS GetSymbolicLink( IN PUNICODE_STRING SymbolicLinkName, OUT PUNICODE_STRING SymbolicLink, OUT PHANDLE LinkHandle )
{
    NTSTATUS status;
    NTSTATUS returnStatus = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES oa;
    UNICODE_STRING tmpSymbolicLink;
    HANDLE tmpLinkHandle;
    ULONG symbolicLinkLength;

    // Open and query the symbolic link
    InitializeObjectAttributes( &oa, SymbolicLinkName, OBJ_KERNEL_HANDLE, NULL, NULL );
    status = ZwOpenSymbolicLinkObject( &tmpLinkHandle, GENERIC_READ, &oa );

    if ( STATUS_SUCCESS == status )
    {
        // Get the size of the symbolic link string
        symbolicLinkLength = 0;
        tmpSymbolicLink.Length = 0;
        tmpSymbolicLink.MaximumLength = 0;

        status = ZwQuerySymbolicLinkObject( tmpLinkHandle, &tmpSymbolicLink, &symbolicLinkLength );

        if ( STATUS_BUFFER_TOO_SMALL == status && symbolicLinkLength > 0 )
        {
            //
            // Allocate the memory and get the ZwQuerySymbolicLinkObject
            //
            tmpSymbolicLink.Buffer = ExAllocatePool( NonPagedPool, symbolicLinkLength );
            tmpSymbolicLink.Length = 0;
            tmpSymbolicLink.MaximumLength = (USHORT) symbolicLinkLength;

            status = ZwQuerySymbolicLinkObject( tmpLinkHandle, &tmpSymbolicLink, &symbolicLinkLength );

            if ( STATUS_SUCCESS == status )
            {
                SymbolicLink->Buffer = tmpSymbolicLink.Buffer;
                SymbolicLink->Length = tmpSymbolicLink.Length;
                *LinkHandle = tmpLinkHandle;
                SymbolicLink->MaximumLength = tmpSymbolicLink.MaximumLength;
                returnStatus = STATUS_SUCCESS;
            }
            else
            {
                ExFreePool( tmpSymbolicLink.Buffer );
            }
        }
    }
    return returnStatus;
}

// **************************************************************************************
// NAME:        ExtractDriveString
// 
// DESCRIPTION: Extracts the drive from a string.  Adds a NULL termination and 
//              adjusts the length of the source string.
//                     
// PARAMETERS:  Source IN OUT
// 
// IRQL:        IRQL_PASSIVE_LEVEL
// 
// RETURNS:     STATUS_SUCCESS
//              STATUS_UNSUCCESSFUL
// **************************************************************************************
static
NTSTATUS
    ExtractDriveString( IN OUT PUNICODE_STRING Source )
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    ULONG i = 0;
    ULONG numSlashes = 0;

    while ( ( (i * 2) < Source->Length ) && ( 4 != numSlashes ) )
    {
        if ( L'\\' == Source->Buffer[i] )
        {
            numSlashes++;
        }
        i++;
    }

    if ( ( 4 == numSlashes ) && ( i > 1 ) )
    {
        i--;
        Source->Buffer[i]    = L'\0';
        Source->Length        = (USHORT) i * 2;
        status                = STATUS_SUCCESS;
    }

    return status;
}

// **************************************************************************************
// NAME:        GetSystemRootPath
// 
// DESCRIPTION: On success this routine allocates and copies the system root path 
//              (ie C:\Windows) into the SystemRootPath parameter
//                     
// PARAMETERS:  SystemRootPath out
// 
// IRQL:        IRQL_PASSIVE_LEVEL
// 
// RETURNS:     STATUS_SUCCESS
//              STATUS_UNSUCCESSFUL
// 
// NOTE:        Caller must free SystemRootPath->Buffer after a successful call to 
//              this routine.
// **************************************************************************************
static NTSTATUS GetSystemRootPath( OUT PUNICODE_STRING SystemRootPath )
{
    NTSTATUS status;
    NTSTATUS returnStatus = STATUS_UNSUCCESSFUL;
    UNICODE_STRING systemRootName;
    UNICODE_STRING systemRootSymbolicLink1;
    UNICODE_STRING systemRootSymbolicLink2;
    UNICODE_STRING systemDosRootPath;
    PDEVICE_OBJECT deviceObject;
    PFILE_OBJECT fileObject;
    HANDLE linkHandle;
    ULONG fullPathLength;

    RtlInitUnicodeString( &systemRootName, SYSTEM_ROOT );

    // Get the full path for the system root directory
    status = GetSymbolicLink( &systemRootName, &systemRootSymbolicLink1, &linkHandle );

    if ( STATUS_SUCCESS == status )
    {
        // At this stage we have the full path but its in the form:
        // \Device\Harddisk0\Partition1\WINDOWS lets try to get the symoblic name for 
        // this drive so it looks more like c:\WINDOWS.
        DbgPrint( "Full System Root Path: %ws\n", systemRootSymbolicLink1.Buffer );
        fullPathLength = systemRootSymbolicLink1.Length;
        ZwClose( linkHandle );

        // Remove the path so we can query the drive letter
        status = ExtractDriveString( &systemRootSymbolicLink1 );

        if ( STATUS_SUCCESS == status )
        {
            // We've added a NULL termination character so we must reflect that in the 
            // total length.
            fullPathLength = fullPathLength - 2;

            // Query the drive letter
            status = GetSymbolicLink( &systemRootSymbolicLink1, &systemRootSymbolicLink2, &linkHandle );

            if ( STATUS_SUCCESS == status )
            {
                status = IoGetDeviceObjectPointer( &systemRootSymbolicLink2, SYNCHRONIZE | FILE_ANY_ACCESS, &fileObject, &deviceObject );

                if ( STATUS_SUCCESS == status )
                {
                    ObReferenceObject( deviceObject );

                    // Get the dos name for the drive
                    status = RtlVolumeDeviceToDosName( deviceObject, &systemDosRootPath );

                    if ( STATUS_SUCCESS == status && NULL != systemDosRootPath.Buffer )
                    {
                        SystemRootPath->Buffer = ExAllocatePool( NonPagedPool, fullPathLength );
                        RtlZeroMemory( SystemRootPath->Buffer, fullPathLength );
                        SystemRootPath->Length = 0;
                        SystemRootPath->MaximumLength = (USHORT) fullPathLength;

                        // Drive
                        RtlCopyMemory( SystemRootPath->Buffer, systemDosRootPath.Buffer, systemDosRootPath.Length );

                        // Drive Slash
                        RtlCopyMemory( SystemRootPath->Buffer + (systemDosRootPath.Length/2), L"\\", 2 );

                        // Drive Slash Directory
                        RtlCopyMemory( SystemRootPath->Buffer + (systemDosRootPath.Length/2) + 1,  
                                       systemRootSymbolicLink1.Buffer + (systemRootSymbolicLink1.Length/2) + 1,
                                       fullPathLength - systemRootSymbolicLink1.Length );

                        SystemRootPath->Length = (systemDosRootPath.Length + 2) + 
                            ((USHORT) fullPathLength - systemRootSymbolicLink1.Length);

                        ExFreePool( systemDosRootPath.Buffer );
                        returnStatus = STATUS_SUCCESS;
                    }
                    ObDereferenceObject( deviceObject );
                }
                ZwClose( linkHandle );
                ExFreePool( systemRootSymbolicLink2.Buffer );
            }
        }

        ExFreePool( systemRootSymbolicLink1.Buffer );
    }
    return returnStatus;
}

//***************************************************************************************
// NAME:        DriverEntry
// 
// DESCRIPTION: Registers the unload routine and calls GetSystemRootPath.
//                     
// PARAMETERS:  DriverObject    IN        
//                  Address of the DRIVER_OBJECT created by NT for this driver
//              RegistryPath    IN        
//                  UNICODE_STRING which represents this drivers KEY in the Registry    
// 
// IRQL:        IRQL_PASSIVE_LEVEL
// 
// RETURNS:     STATUS_SUCCESS
//***************************************************************************************
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
    NTSTATUS status;
    UNICODE_STRING systemRootPath;

    DbgPrint( "\n"
                "www.DriverEntry.com\n"
                "-------------------\n"
                "System Root Path Driver\n"
                "Compiled %s %s\n\n",
                __DATE__, 
                __TIME__ );

    DriverObject->DriverUnload    = DriverUnload;
    status = GetSystemRootPath( &systemRootPath );

    if ( STATUS_SUCCESS == status )
    {
        DbgPrint( "System Root Path: %ws\n", systemRootPath.Buffer );
        ExFreePool( systemRootPath.Buffer );
    }

    return STATUS_SUCCESS;
}