2012-04-12 17 views
8

Sto tentando di eseguire un programma con diritti di amministratore tramite un'applicazione C#, che viene richiamata solo con diritti utente..NET Start Process con diritti più elevati

Codice

 ProcessStartInfo psi; 
     try 
     { 
      psi = new ProcessStartInfo(@"WINZIP32.EXE"); 

      psi.UseShellExecute = false; 
      SecureString pw = new SecureString(); 
      pw.AppendChar('p'); 
      pw.AppendChar('a'); 
      pw.AppendChar('s'); 
      pw.AppendChar('s'); 
      pw.AppendChar('w'); 
      pw.AppendChar('o'); 
      pw.AppendChar('r'); 
      pw.AppendChar('d'); 
      psi.Password = pw; 
      psi.UserName = "administrator"; 

      Process.Start(psi); 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
     } 

E si avvia WinZip, ma solo con diritti utente. C'è qualcosa che sto facendo male o è persino possibile iniziare un processo con diritti più alti?

grazie!

Modifica: Ecco la ragione alla base della domanda, forse aiuta a capire di cosa ho realmente bisogno.

Ho usato Winzip ad esempio per avere un'idea generale di cosa non è corretto con il mio codice. Il vero problema è che la nostra azienda utilizza 2 versioni di un programma. Ma prima di avviare una qualsiasi delle versioni è necessario importare un file dll con regsvr32 (con diritti di amministratore). Ora vorrei scrivere un programma che permetta all'utente di selezionare la versione, importare la DLL e avviare l'applicazione corretta.

+0

vuoi dire che dovrei iniziare la mia applicazione con diritti di amministratore , quindi sì, l'ho provato ed è strano ma apre anche winzip con i diritti dell'utente. In ogni caso, il programma C# verrà eseguito da un utente senza diritti di amministratore. ** MODIFICA: ** Il commento a cui ho risposto è stato cancellato. – KenavR

risposta

10

è necessario impostare ProcessStartInfo.UseShellExecute a true e ProcessStartInfo.Verb a runas:

Process process = null; 
ProcessStartInfo processStartInfo = new ProcessStartInfo(); 

processStartInfo.FileName = "WINZIP32.EXE"; 

processStartInfo.Verb = "runas"; 
processStartInfo.WindowStyle = ProcessWindowStyle.Normal; 
processStartInfo.UseShellExecute = true; 

process = Process.Start(processStartInfo); 

questo farà sì che l'esecuzione dell'applicazione come amministratore. L'UAC richiederà tuttavia all'utente di confermare. Se ciò non è auspicabile, è necessario aggiungere un manifest per elevare in modo permanente i privilegi del processo host.

+0

L'utente non dovrebbe aver bisogno di inserire la password dell'amministratore. Winzip dovrebbe iniziare con l'account amministratore che definisco all'interno del codice. – KenavR

+0

UAC è lì per un motivo. Nessuna applicazione ben progettata dovrebbe avere bisogno di eseguire un processo di decompressione come Amministratore. –

+0

Ho usato Winzip per esempio per avere un'idea generale di cosa non è corretto con il mio codice. Il vero problema è che la nostra azienda utilizza 2 versioni di un programma. Ma prima di avviare una qualsiasi delle versioni è necessario importare un file dll con regsvr32 (con diritti di amministratore). Ora vorrei scrivere un programma che permetta all'utente di selezionare la versione, importare la DLL e avviare l'applicazione corretta. – KenavR

2

È possibile eseguire un processo come un altro utente (anche un amministratore) utilizzando CreateProcessAsUser function (API Win32). CreateProcessAsUser accetta un token utente come primo parametro, ovvero un token di rappresentazione.

È necessario utilizzare DLLImport per caricare la funzione dalla DLL di Windows.

Date un'occhiata a questo esempio di implementazione che ho usato in uno dei miei progetti:

[StructLayout(LayoutKind.Sequential)] 
internal struct PROCESS_INFORMATION 
{ 
    public IntPtr hProcess; 
    public IntPtr hThread; 
    public uint dwProcessId; 
    public uint dwThreadId; 
} 

[StructLayout(LayoutKind.Sequential)] 
internal struct SECURITY_ATTRIBUTES 
{ 
    public uint nLength; 
    public IntPtr lpSecurityDescriptor; 
    public bool bInheritHandle; 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct STARTUPINFO 
{ 
    public uint cb; 
    public string lpReserved; 
    public string lpDesktop; 
    public string lpTitle; 
    public uint dwX; 
    public uint dwY; 
    public uint dwXSize; 
    public uint dwYSize; 
    public uint dwXCountChars; 
    public uint dwYCountChars; 
    public uint dwFillAttribute; 
    public uint dwFlags; 
    public short wShowWindow; 
    public short cbReserved2; 
    public IntPtr lpReserved2; 
    public IntPtr hStdInput; 
    public IntPtr hStdOutput; 
    public IntPtr hStdError; 

} 

internal enum SECURITY_IMPERSONATION_LEVEL 
{ 
    SecurityAnonymous, 
    SecurityIdentification, 
    SecurityImpersonation, 
    SecurityDelegation 
} 

internal enum TOKEN_TYPE 
{ 
    TokenPrimary = 1, 
    TokenImpersonation 
} 

public class ProcessAsUser 
{ 

    [DllImport("advapi32.dll", SetLastError = true)] 
    private static extern bool CreateProcessAsUser(
    IntPtr hToken, 
    string lpApplicationName, 
    string lpCommandLine, 
    ref SECURITY_ATTRIBUTES lpProcessAttributes, 
    ref SECURITY_ATTRIBUTES lpThreadAttributes, 
    bool bInheritHandles, 
    uint dwCreationFlags, 
    IntPtr lpEnvironment, 
    string lpCurrentDirectory, 
    ref STARTUPINFO lpStartupInfo, 
    out PROCESS_INFORMATION lpProcessInformation); 

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)] 
    private static extern bool DuplicateTokenEx(
    IntPtr hExistingToken, 
    uint dwDesiredAccess, 
    ref SECURITY_ATTRIBUTES lpThreadAttributes, 
    Int32 ImpersonationLevel, 
    Int32 dwTokenType, 
    ref IntPtr phNewToken); 

    [DllImport("advapi32.dll", SetLastError = true)] 
    private static extern bool OpenProcessToken(
    IntPtr ProcessHandle, 
    UInt32 DesiredAccess, 
    ref IntPtr TokenHandle); 

    [DllImport("userenv.dll", SetLastError = true)] 
    private static extern bool CreateEnvironmentBlock(
    ref IntPtr lpEnvironment, 
    IntPtr hToken, 
    bool bInherit); 

    [DllImport("userenv.dll", SetLastError = true)] 
    private static extern bool DestroyEnvironmentBlock(
    IntPtr lpEnvironment); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    private static extern bool CloseHandle(
    IntPtr hObject); 

    private const short SW_SHOW = 5; 
    private const uint TOKEN_QUERY = 0x0008; 
    private const uint TOKEN_DUPLICATE = 0x0002; 
    private const uint TOKEN_ASSIGN_PRIMARY = 0x0001; 
    private const int GENERIC_ALL_ACCESS = 0x10000000; 
    private const int STARTF_USESHOWWINDOW = 0x00000001; 
    private const int STARTF_FORCEONFEEDBACK = 0x00000040; 
    private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400; 
    private const int STARTF_RUNFULLSCREEN = 0x00000020; 

    private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock) 
    { 
     bool result = false; 

     PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); 
     SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES(); 
     SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES(); 
     saProcess.nLength = (uint)Marshal.SizeOf(saProcess); 
     saThread.nLength = (uint)Marshal.SizeOf(saThread); 

     STARTUPINFO si = new STARTUPINFO(); 
     si.cb = (uint)Marshal.SizeOf(si); 

     //if this member is NULL, the new process inherits the desktop 
     //and window station of its parent process. If this member is 
     //an empty string, the process does not inherit the desktop and 
     //window station of its parent process; instead, the system 
     //determines if a new desktop and window station need to be created. 
     //If the impersonated user already has a desktop, the system uses the 
     //existing desktop. 

     si.lpDesktop = @"WinSta0\Default"; //Modify as needed 
     si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK; 
     si.wShowWindow = SW_SHOW; 

     //Set other si properties as required. 

     result = CreateProcessAsUser(
     token, 
     null, 
     cmdLine, 
     ref saProcess, 
     ref saThread, 
     false, 
     CREATE_UNICODE_ENVIRONMENT, 
     envBlock, 
     null, 
     ref si, 
     out pi); 

     if (result == false) 
     { 
      int error = Marshal.GetLastWin32Error(); 
      string message = String.Format("CreateProcessAsUser Error: {0}", error); 
      Debug.WriteLine(message); 

     } 

     return result; 
    } 

    /// <summary> 
    /// LaunchProcess As User Overloaded for Window Mode 
    /// </summary> 
    /// <param name="cmdLine"></param> 
    /// <param name="token"></param> 
    /// <param name="envBlock"></param> 
    /// <param name="WindowMode"></param> 
    /// <returns></returns> 
    private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock,uint WindowMode) 
    { 
     bool result = false; 

     PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); 
     SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES(); 
     SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES(); 
     saProcess.nLength = (uint)Marshal.SizeOf(saProcess); 
     saThread.nLength = (uint)Marshal.SizeOf(saThread); 

     STARTUPINFO si = new STARTUPINFO(); 
     si.cb = (uint)Marshal.SizeOf(si); 

     //if this member is NULL, the new process inherits the desktop 
     //and window station of its parent process. If this member is 
     //an empty string, the process does not inherit the desktop and 
     //window station of its parent process; instead, the system 
     //determines if a new desktop and window station need to be created. 
     //If the impersonated user already has a desktop, the system uses the 
     //existing desktop. 

     si.lpDesktop = @"WinSta0\Default"; //Default Vista/7 Desktop Session 
     si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK; 

     //Check the Startup Mode of the Process 
     if (WindowMode == 1) 
      si.wShowWindow = SW_SHOW; 
     else if (WindowMode == 2) 
     { //Do Nothing 
     } 
     else if (WindowMode == 3) 
      si.wShowWindow = 0; //Hide Window 
     else if (WindowMode == 4) 
      si.wShowWindow = 3; //Maximize Window 
     else if (WindowMode == 5) 
      si.wShowWindow = 6; //Minimize Window 
     else 
      si.wShowWindow = SW_SHOW; 


     //Set other si properties as required. 
     result = CreateProcessAsUser(
     token, 
     null, 
     cmdLine, 
     ref saProcess, 
     ref saThread, 
     false, 
     CREATE_UNICODE_ENVIRONMENT, 
     envBlock, 
     null, 
     ref si, 
     out pi); 

     if (result == false) 
     { 
      int error = Marshal.GetLastWin32Error(); 
      string message = String.Format("CreateProcessAsUser Error: {0}", error); 
      Debug.WriteLine(message); 

     } 

     return result; 
    } 

    private static IntPtr GetPrimaryToken(int processId) 
    { 
     IntPtr token = IntPtr.Zero; 
     IntPtr primaryToken = IntPtr.Zero; 
     bool retVal = false; 
     Process p = null; 

     try 
     { 
      p = Process.GetProcessById(processId); 
     } 

     catch (ArgumentException) 
     { 

      string details = String.Format("ProcessID {0} Not Available", processId); 
      Debug.WriteLine(details); 
      throw; 
     } 

     //Gets impersonation token 
     retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token); 
     if (retVal == true) 
     { 

      SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); 
      sa.nLength = (uint)Marshal.SizeOf(sa); 

      //Convert the impersonation token into Primary token 
      retVal = DuplicateTokenEx(
      token, 
      TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY, 
      ref sa, 
      (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
      (int)TOKEN_TYPE.TokenPrimary, 
      ref primaryToken); 

      //Close the Token that was previously opened. 
      CloseHandle(token); 
      if (retVal == false) 
      { 
       string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error()); 
       Debug.WriteLine(message); 
      } 

     } 

     else 
     { 

      string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error()); 
      Debug.WriteLine(message); 

     } 

     //We'll Close this token after it is used. 
     return primaryToken; 

    } 

    private static IntPtr GetEnvironmentBlock(IntPtr token) 
    { 

     IntPtr envBlock = IntPtr.Zero; 
     bool retVal = CreateEnvironmentBlock(ref envBlock, token, false); 
     if (retVal == false) 
     { 

      //Environment Block, things like common paths to My Documents etc. 
      //Will not be created if "false" 
      //It should not adversley affect CreateProcessAsUser. 

      string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error()); 
      Debug.WriteLine(message); 

     } 
     return envBlock; 
    } 

    public static bool Launch(string appCmdLine /*,int processId*/) 
    { 

     bool ret = false; 

     //Either specify the processID explicitly 
     //Or try to get it from a process owned by the user. 
     //In this case assuming there is only one explorer.exe 

     Process[] ps = Process.GetProcessesByName("explorer"); 
     int processId = -1;//=processId 
     if (ps.Length > 0) 
     { 
      processId = ps[0].Id; 
     } 

     if (processId > 1) 
     { 
      IntPtr token = GetPrimaryToken(processId); 

      if (token != IntPtr.Zero) 
      { 

       IntPtr envBlock = GetEnvironmentBlock(token); 
       ret = LaunchProcessAsUser(appCmdLine, token, envBlock); 
       if (envBlock != IntPtr.Zero) 
        DestroyEnvironmentBlock(envBlock); 

       CloseHandle(token); 
      } 

     } 
     return ret; 
    } 
+0

Come ti iscrivi a 'Process.Exited' http://msdn.microsoft.com/en-us/library/system.diagnostics.process.exited%28v=vs.110%29.aspx con questo approccio? Inoltre, non sembra essere in grado di accettare "EnvironmentBlock" personalizzato in formato .net amichevole come questo http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.environmentvariables%28v=vs.110 % 29.aspx Idealmente sto cercando una soluzione che faccia tutto quello che può fare lo standard di processo, ma accetta anche un token. Sembra che dovrò costruirne uno io stesso. –

0

Rif:
How to start a Process as administrator mode in C#
Elevating process privilege programmatically?

var psi = new ProcessStartInfo 
    { 
     FileName = "notepad", 
     UserName = "admin", 
     Domain = "", 
     Password = pass, 
     UseShellExecute = true, 
     RedirectStandardOutput = true, 
     RedirectStandardError = true, 
     Verb = "runas"; 
    }; 
    Process.Start(psi); 

//

var pass = new SecureString(); 
pass.AppendChar('s'); 
pass.AppendChar('e'); 
pass.AppendChar('c'); 
pass.AppendChar('r'); 
pass.AppendChar('e'); 
pass.AppendChar('t'); 
Process.Start("notepad", "admin", pass, ""); 

// Vista o assegno più alto

if (System.Environment.OSVersion.Version.Major >= 6) 
{ 
    p.StartInfo.Verb = "runas"; 
} 

Rif:How to run/start a new process with admin rights? ASP.net Forum

Un altro modo è quello di rappresentare l'utente admin. È possibile farlo chiamando chiamando la funzione di accesso e impersonare l'utente di cui verrà ottenuto il token . Per impersonare un utente nel codice, guarda: WindowsImpersonationContext Class .Utilizzare il controllo http://www.csharpfriends.com/Forums/ShowPost.aspx?PostID=31611 per GetCurrentUser per verificare se la rappresentazione ha avuto esito positivo.

Snippet di codice:

System.Diagnostics.Process process = null; 
System.Diagnostics.ProcessStartInfo processStartInfo; 

processStartInfo = new System.Diagnostics.ProcessStartInfo(); 

processStartInfo.FileName = "regedit.exe"; 

if (System.Environment.OSVersion.Version.Major >= 6) // Windows Vista or higher 
{ 
    processStartInfo.Verb = "runas"; 
} 
else 
{ 
    // No need to prompt to run as admin 
} 

processStartInfo.Arguments = ""; 
processStartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal; 
processStartInfo.UseShellExecute = true; 

try 
{ 
    process = System.Diagnostics.Process.Start(processStartInfo); 
} 
catch (Exception ex) 
{ 
    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
} 
finally 
{ 
    if (process != null) 
    { 
     process.Dispose(); 
    } 
} 

// Prova questo con login di amministratore, non ho ancora testato ..

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
     public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, 
      int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken); 

     [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
     public extern static bool CloseHandle(IntPtr handle); 

     // Test harness. 
     // If you incorporate this code into a DLL, be sure to demand FullTrust. 
     [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] 
     private void button1_Click(object sender, EventArgs e) 
     { 
      SafeTokenHandle safeTokenHandle; 
      const int LOGON32_PROVIDER_DEFAULT = 0; 
      //This parameter causes LogonUser to create a primary token. 
      const int LOGON32_LOGON_INTERACTIVE = 2; 
      bool returnValue = LogonUser("administrator", "", "password", 
       LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, 
       out safeTokenHandle); 

      Console.WriteLine("LogonUser called."); 

      if (false == returnValue) 
      { 
       int ret = Marshal.GetLastWin32Error(); 
       Console.WriteLine("LogonUser failed with error code : {0}", ret); 
       throw new System.ComponentModel.Win32Exception(ret); 
      } 
      using (safeTokenHandle) 
      { 
       Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No")); 
       Console.WriteLine("Value of Windows NT token: " + safeTokenHandle); 

       // Check the identity. 
       Console.WriteLine("Before impersonation: " 
        + WindowsIdentity.GetCurrent().Name); 
       // Use the token handle returned by LogonUser. 
       WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()); 
       using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) 
       { 
        System.Diagnostics.Process process = null; 
        System.Diagnostics.ProcessStartInfo processStartInfo; 


        processStartInfo = new System.Diagnostics.ProcessStartInfo(); 

        processStartInfo.FileName = "regedit.exe"; 

        //if (System.Environment.OSVersion.Version.Major >= 6) // Windows Vista or higher 
        //{ 
        // processStartInfo.Verb = "runas"; 
        //} 
        //else 
        //{ 
        // // No need to prompt to run as admin 
        //} 

        processStartInfo.Arguments = ""; 
        processStartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal; 
        processStartInfo.UseShellExecute = true; 

        try 
        { 
         process = System.Diagnostics.Process.Start(processStartInfo); 
        } 
        catch (Exception ex) 
        { 
         MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
        } 
        finally 
        { 
         if (process != null) 
         { 
          process.Dispose(); 
         } 
        } 

        // Check the identity. 
        Console.WriteLine("After impersonation: " 
         + WindowsIdentity.GetCurrent().Name); 
       } 
       // Releasing the context object stops the impersonation 
       // Check the identity. 
       Console.WriteLine("After closing the context: " + WindowsIdentity.GetCurrent().Name); 
      } 
     } 
+0

Il primo modo è esattamente lo stesso che faccio e non funziona. Il secondo apre l'UAC, non è quello che mi serve. Quando uso la funzione impersonate, ottengo un'eccezione di "accesso negato". – KenavR

+0

UAC sta facendo ciò per cui è stato progettato. Dovresti disattivare il controllo dell'account utente per aggirare il problema. È di design. Non dovresti eseguire processi elevati senza l'approvazione dell'utente. –

Problemi correlati