Pregunta

I know that a .NET application which is run as elevated cannot see the mapped drives of the user. I also know there is a registry hack to fix this (which involves a reboot).

I would like to create a different solution to this problem. Our application must run elevated and it relies heavily on the mapped drives that the user has created. I'd like to detect the mapped drives the user has, and map a similar bunch from the elevated application.

So the question is: how do I detect the mapped drives of the 'normal' user from an elevated application?

¿Fue útil?

Solución

If a mapped drive was mapped persistently (checkbox "reconnect at logon" enabled) then you can find it in the user's registry hive:

HKEY_CURRENT_USER\Network\<drive letter>

That key has a value RemotePath containing the UNC path.

Otros consejos

I've got a few solutions to this problem now. Reading the registry is ok but you won't find drives which are created by your domain profile or other temporary mapped drives.

One solution I have tested, which works, is to use this code to create an un-elevated process. I wrote a simple GetMappedDrives.exe app which writes the mapped drives to a file. The elevated app can then read the file to obtain the un-elevated mapped drives.

Another solution might be to use an un-elevated wrapper around your elevated application. The un-elevated wrapper would call GetMappedDrives.exe first then run the elevated app. Problem is that you won't know about changes to the mapped drives until you restart the app.

A third solution might be applicable if you happen have an un-elevated process running as part of your suite of applications. Just bounce a network message off the un-elevated app telling it to call GetMappedDrives.exe.

GetMappedDrives c# code...

    static void Main(string[] args)
    {
        //if (args.GetLength(0) != 1)
        //{
        //    Debug.Assert(false);
        //    Console.WriteLine("You must supply the target file path");
        //    Environment.Exit(1);
        //}
        //String strTargetPath = args[0];
        String strTargetPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\MyCompany\MappedDrives.csv";

        FileStream fs = new FileStream(strTargetPath, FileMode.Create, FileAccess.Write);
        StreamWriter writer = new StreamWriter(fs);

        DriveInfo[] allDrives = DriveInfo.GetDrives();
        foreach (DriveInfo dInfo in allDrives)
        {
            if (dInfo.DriveType == DriveType.Network || dInfo.DriveType == DriveType.Removable)
            {
                String s = String.Format("{0},{1},{2},{3},{4}\n", dInfo.Name, dInfo.VolumeLabel, dInfo.DriveType, dInfo.TotalSize, dInfo.AvailableFreeSpace);
                writer.Write(s);
            }
        }
        writer.Close();
    }

TestGetMappedDrives c++ code. Run this in an elevated command prompt...

#include "stdafx.h"
#include "windows.h"

//#ifndef SECURITY_MANDATORY_HIGH_RID
//  #define SECURITY_MANDATORY_UNTRUSTED_RID            (0x00000000L)
//  #define SECURITY_MANDATORY_LOW_RID                  (0x00001000L)
//  #define SECURITY_MANDATORY_MEDIUM_RID               (0x00002000L)
//  #define SECURITY_MANDATORY_HIGH_RID                 (0x00003000L)
//  #define SECURITY_MANDATORY_SYSTEM_RID               (0x00004000L)
//  #define SECURITY_MANDATORY_PROTECTED_PROCESS_RID    (0x00005000L)
//#endif

//#ifndef TokenIntegrityLevel
//  #define TokenIntegrityLevel ((TOKEN_INFORMATION_CLASS)25)
//#endif

//#ifndef TOKEN_MANDATORY_LABEL
//  typedef struct  
//  {
//      SID_AND_ATTRIBUTES Label;
//  } TOKEN_MANDATORY_LABEL;
//#endif

typedef BOOL (WINAPI *defCreateProcessWithTokenW)
(HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);


// Writes Integration Level of the process with the given ID into pu32_ProcessIL
// returns Win32 API error or 0 if succeeded
DWORD GetProcessIL(DWORD u32_PID, DWORD* pu32_ProcessIL)
{
*pu32_ProcessIL = 0;

HANDLE h_Process   = 0;
HANDLE h_Token     = 0;
DWORD  u32_Size    = 0;
BYTE*  pu8_Count   = 0;
DWORD* pu32_ProcIL = 0;
TOKEN_MANDATORY_LABEL* pk_Label = 0;

h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_PID);
if (!h_Process)
    goto _CleanUp;

if (!OpenProcessToken(h_Process, TOKEN_QUERY, &h_Token))
    goto _CleanUp;

if (!GetTokenInformation(h_Token, TokenIntegrityLevel, NULL, 0, &u32_Size) &&
     GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    goto _CleanUp;

pk_Label = (TOKEN_MANDATORY_LABEL*) HeapAlloc(GetProcessHeap(), 0, u32_Size);
if (!pk_Label)
    goto _CleanUp;

if (!GetTokenInformation(h_Token, TokenIntegrityLevel, pk_Label, u32_Size, &u32_Size))
    goto _CleanUp;

pu8_Count = GetSidSubAuthorityCount(pk_Label->Label.Sid);
if (!pu8_Count)
    goto _CleanUp;

pu32_ProcIL = GetSidSubAuthority(pk_Label->Label.Sid, *pu8_Count-1);
if (!pu32_ProcIL)
    goto _CleanUp;

*pu32_ProcessIL = *pu32_ProcIL;
SetLastError(ERROR_SUCCESS);

_CleanUp:
DWORD u32_Error = GetLastError();
if (pk_Label)  HeapFree(GetProcessHeap(), 0, pk_Label);
if (h_Token)   CloseHandle(h_Token);
if (h_Process) CloseHandle(h_Process);
return u32_Error;
}

// Creates a new process u16_Path with the integration level of the Explorer process (MEDIUM IL)
// If you need this function in a service you must replace FindWindow() with another API to find Explorer process
// The parent process of the new process will be svchost.exe if this EXE was run "As Administrator"
// returns Win32 API error or 0 if succeeded
DWORD CreateProcessMediumIL(WCHAR* u16_Path, WCHAR* u16_CmdLine)
{
HANDLE h_Process = 0;
HANDLE h_Token   = 0;
HANDLE h_Token2  = 0;
PROCESS_INFORMATION k_ProcInfo    = {0};
STARTUPINFOW        k_StartupInfo = {0};

BOOL b_UseToken = FALSE;

// Detect Windows Vista, 2008, Windows 7 and higher
if (GetProcAddress(GetModuleHandleA("Kernel32"), "GetProductInfo"))
{
    DWORD u32_CurIL;
    DWORD u32_Err = GetProcessIL(GetCurrentProcessId(), &u32_CurIL);
    if (u32_Err)
        return u32_Err;

    if (u32_CurIL > SECURITY_MANDATORY_MEDIUM_RID)
        b_UseToken = TRUE;
}

// Create the process normally (before Windows Vista or if current process runs with a medium IL)
if (!b_UseToken)
{
    if (!CreateProcessW(u16_Path, u16_CmdLine, 0, 0, FALSE, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
        return GetLastError();

    CloseHandle(k_ProcInfo.hThread);
    CloseHandle(k_ProcInfo.hProcess); 
    return ERROR_SUCCESS;
}

defCreateProcessWithTokenW f_CreateProcessWithTokenW = 
    (defCreateProcessWithTokenW) GetProcAddress(GetModuleHandleA("Advapi32"), "CreateProcessWithTokenW");

if (!f_CreateProcessWithTokenW) // This will never happen on Vista!
    return ERROR_INVALID_FUNCTION; 

HWND h_Progman = ::GetShellWindow();

DWORD u32_ExplorerPID = 0;      
GetWindowThreadProcessId(h_Progman, &u32_ExplorerPID);

// ATTENTION:
// If UAC is turned OFF all processes run with SECURITY_MANDATORY_HIGH_RID, also Explorer!
// But this does not matter because to start the new process without UAC no elevation is required.
h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_ExplorerPID);
if (!h_Process)
    goto _CleanUp;

if (!OpenProcessToken(h_Process, TOKEN_DUPLICATE, &h_Token))
    goto _CleanUp;

if (!DuplicateTokenEx(h_Token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &h_Token2))
    goto _CleanUp;

if (!f_CreateProcessWithTokenW(h_Token2, 0, u16_Path, u16_CmdLine, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
    goto _CleanUp;

SetLastError(ERROR_SUCCESS);

_CleanUp:
DWORD u32_Error = GetLastError();
if (h_Token)   CloseHandle(h_Token);
if (h_Token2)  CloseHandle(h_Token2);
if (h_Process) CloseHandle(h_Process);
CloseHandle(k_ProcInfo.hThread);
CloseHandle(k_ProcInfo.hProcess); 
return u32_Error;
}


int _tmain(int argc, _TCHAR* argv[])
{
DWORD u32_Err = CreateProcessMediumIL(L"C:\\src\\bin\\release\\GetMappedDrives.exe", NULL);

printf("CreateProcessMediumIL() exited with error %d\r\n", u32_Err);
Sleep(2000);
return 0;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top