¿Cómo iniciar un nuevo proceso sin privilegios de administrador de un proceso con privilegios de administrador?

Estoy creando un actualizador automático para una aplicación. La aplicación la inicia el usuario y se ejecuta sin privilegios de administrador. El autoupdater se inicia con privilegios de administrador y elimina la aplicación antes de que descargue los archivos nuevos.

El problema surge cuando quiero iniciar la aplicación actualizada después de que termine el autoupdater. Si uso System.Diagnostics.Process.Start (archivo) regular, la aplicación también comienza con privilegios de administrador, y debe ejecutarse en el usuario actual para que funcione como se esperaba.

Entonces, ¿cómo puedo hacer que el autoupdater inicie la aplicación como el usuario actual en lugar del administrador?

He intentado usar lo siguiente:

var pSI = new ProcessStartInfo() { UseShellExecute = false, UserName = Environment.UserName, FileName = file }; System.Diagnostics.Process.Start(pSI); 

Pero esto arroja el error “Nombre de usuario o contraseña no válidos”. He comprobado que el nombre de usuario es correcto y entiendo que la contraseña probablemente no sea válida, ya que no la he incluido. Pero no es una opción pedirle al usuario que ingrese su contraseña, ya que la razón completa para iniciarla automáticamente es facilitarle al usuario.

¿Alguna sugerencia?

Lo que intentas lograr no se puede hacer muy fácilmente y no es compatible. Sin embargo, es posible usar un mínimo de pirateo. Aaron Margosis escribió un artículo que describe una técnica.

Para citar la sección pertinente, deberá realizar estos pasos:

  1. Habilita el SeIncreaseQuotaPrivilege en tu token actual
  2. Obtenga un HWND que represente el shell de escritorio (GetShellWindow)
  3. Obtenga el ID de proceso (PID) del proceso asociado con esa ventana (GetWindowThreadProcessId)
  4. Abra ese proceso (OpenProcess)
  5. Obtener el token de acceso de ese proceso (OpenProcessToken)
  6. Crea un token primario con ese token (DuplicateTokenEx)
  7. Comience el nuevo proceso con ese token primario (CreateProcessWithTokenW)

El artículo contiene un enlace de descarga para alguna fuente demo de C ++ desde la cual debería ser lo suficientemente simple para traducir a C #.

Código de C # para el artículo de Aaron Margosis:

  private static void RunAsDesktopUser(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName)); // To start process as shell user you will need to carry out these steps: // 1. Enable the SeIncreaseQuotaPrivilege in your current token // 2. Get an HWND representing the desktop shell (GetShellWindow) // 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId) // 4. Open that process(OpenProcess) // 5. Get the access token from that process (OpenProcessToken) // 6. Make a primary token with that token(DuplicateTokenEx) // 7. Start the new process with that primary token(CreateProcessWithTokenW) var hProcessToken = IntPtr.Zero; // Enable SeIncreaseQuotaPrivilege in this process. (This won't work if current process is not elevated.) try { var process = GetCurrentProcess(); if (!OpenProcessToken(process, 0x0020, ref hProcessToken)) return; var tkp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Privileges = new LUID_AND_ATTRIBUTES[1] }; if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid)) return; tkp.Privileges[0].Attributes = 0x00000002; if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero)) return; } finally { CloseHandle(hProcessToken); } // Get an HWND representing the desktop shell. // CAVEATS: This will fail if the shell is not running (crashed or terminated), or the default shell has been // replaced with a custom shell. This also won't return what you probably want if Explorer has been terminated and // restarted elevated. var hwnd = GetShellWindow(); if (hwnd == IntPtr.Zero) return; var hShellProcess = IntPtr.Zero; var hShellProcessToken = IntPtr.Zero; var hPrimaryToken = IntPtr.Zero; try { // Get the PID of the desktop shell process. uint dwPID; if (GetWindowThreadProcessId(hwnd, out dwPID) == 0) return; // Open the desktop shell process in order to query it (get the token) hShellProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID); if (hShellProcess == IntPtr.Zero) return; // Get the process token of the desktop shell. if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken)) return; var dwTokenRights = 395U; // Duplicate the shell's process token to get a primary token. // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation). if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken)) return; // Start the target process with the new token. var si = new STARTUPINFO(); var pi = new PROCESS_INFORMATION(); if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi)) return; } finally { CloseHandle(hShellProcessToken); CloseHandle(hPrimaryToken); CloseHandle(hShellProcess); } } #region Interop private struct TOKEN_PRIVILEGES { public UInt32 PrivilegeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public LUID_AND_ATTRIBUTES[] Privileges; } [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct LUID_AND_ATTRIBUTES { public LUID Luid; public UInt32 Attributes; } [StructLayout(LayoutKind.Sequential)] private struct LUID { public uint LowPart; public int HighPart; } [Flags] private enum ProcessAccessFlags : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VirtualMemoryOperation = 0x00000008, VirtualMemoryRead = 0x00000010, VirtualMemoryWrite = 0x00000020, DuplicateHandle = 0x00000040, CreateProcess = 0x000000080, SetQuota = 0x00000100, SetInformation = 0x00000200, QueryInformation = 0x00000400, QueryLimitedInformation = 0x00001000, Synchronize = 0x00100000 } private enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } private enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [DllImport("kernel32.dll", ExactSpelling = true)] private static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool LookupPrivilegeValue(string host, string name, ref LUID pluid); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr hObject); [DllImport("user32.dll")] private static extern IntPtr GetShellWindow(); [DllImport("user32.dll", SetLastError = true)] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr phNewToken); [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); #endregion 

Suponiendo que está indicando a la aplicación que se apague limpiamente en lugar de terminarla, y si aún puede realizar cambios en la aplicación antes de liberar su actualizador, una solución simple sería hacer que la aplicación inicie un proceso provisional antes de salir. Puede crear el ejecutable para el proceso provisional en una ubicación temporal. Cuando finalice la actualización, señale el proceso provisional para volver a iniciar la aplicación y salir. De esa forma, todo sucede de manera natural y no tiene que perder el tiempo.

Otra opción sería usar OpenProcess , OpenProcessToken y DuplicateToken para obtener una copia del token de seguridad de la aplicación antes de eliminarla. A continuación, puede usar CreateProcessAsUser para reiniciar la aplicación en el contexto original.

Ambos enfoques deberían funcionar incluso si el actualizador se ejecuta bajo una cuenta diferente y / o en una sesión diferente a la aplicación.

Esta es una pregunta muy antigua que vi hace años y que he vuelto a visitar ahora. Dado que es el primer resultado de búsqueda de Google … publicaré mi respuesta aquí.

Todas las soluciones que he encontrado son extremadamente complejas y ridículas. A lo largo de los años, me he topado con una solución que no he visto documentada en ningún lado, y que realmente no he compartido hasta ahora.

El código es muy simple … Básicamente estamos escribiendo un archivo por lotes con el nombre / ruta del ejecutable del proceso que desea ejecutar, con los argumentos que desee. Luego generamos un proceso explorer.exe con la ruta al archivo por lotes …

 File.WriteAllText(@"C:\test.bat", @"C:\test.exe -randomArgs"); var proc = new Process { StartInfo = new ProcessStartInfo { FileName = "explorer.exe", Arguments = @"C:\test.bat", UseShellExecute = true, Verb = "runas", WindowStyle = ProcessWindowStyle.Hidden } }; proc.Start(); 

El proceso del explorador que engendramos es eliminado inmediatamente por el sistema operativo. ¡Sin embargo! El proceso de root explorer.exe que se ejecuta ejecuta el archivo por lotes. Puede darle a explorer.exe el nombre de su ejecutable y hará lo mismo, sin embargo, ese método no admite argumentos.

Por lo que sé, esto es un error o una característica no documentada. Sin embargo, no puedo imaginar cómo se podría usar maliciosamente, ya que permite la de-elevación de permisos … Esto funciona en Windows 7/8 / 8.1 / 10.

Hay un proyecto en codeplex llamado User Account Control Helpers .

El proyecto proporciona una biblioteca para la interacción con el mecanismo de UAC.

En la biblioteca encontrarás una clase llamada UserAccountControl . La clase tiene un método estático llamado CreateProcessAsStandardUser para iniciar un proceso desde un proceso elevado con derechos de usuario estándar.

En resumen, las funciones abren el token de proceso del proceso de shell de escritorio. Luego, duplica ese token para obtener un token primario. Este token se usa para iniciar el nuevo proceso en el usuario conectado.

Para obtener más información, lea la siguiente publicación del blog de Aaron Margosis .

Enfrenté el problema similar. En el proceso de verificación hay un campo de contraseña, el problema es que debe proporcionar la contraseña como una cadena segura. Entonces, el código sería:

 System.Security.SecureString password = new System.Security.SecureString(); password.AppendChar('c1'); //append the all characters of your password, you could probably use a loop and then, Process p =new Process(); p.UseShellExecute = false; p.UserName = Environment.UserName; p.FileName = file ; p.Sassword=password; p.Start(); 

Código práctico VB.NET para iniciar un proceso con permisos de shell predeterminados del proceso principal elevado

 #Region "References" Imports System.Runtime.InteropServices #End Region Public Class LaunchProcess #Region "Native Methods"  Private Shared Function GetShellWindow() As IntPtr End Function  Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean End Function  Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, ByRef lpdwProcessId As IntPtr) As Integer End Function  Private Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger,  ByVal bInheritHandle As Boolean, ByVal dwProcessId As IntPtr) As IntPtr End Function  _ Private Shared Function DuplicateTokenEx( _ ByVal ExistingTokenHandle As IntPtr, _ ByVal dwDesiredAccess As UInt32, _ ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _ ByVal ImpersonationLevel As Integer, _ ByVal TokenType As Integer, _ ByRef DuplicateTokenHandle As System.IntPtr) As Boolean End Function  Private Shared Function LookupPrivilegeValue(lpSystemName As String, lpName As String, ByRef lpLuid As LUID) As Boolean End Function  _ Private Shared Function AdjustTokenPrivileges( _ ByVal TokenHandle As IntPtr, _ ByVal DisableAllPrivileges As Boolean, _ ByRef NewState As TOKEN_PRIVILEGES, _ ByVal BufferLengthInBytes As Integer, _ ByRef PreviousState As TOKEN_PRIVILEGES, _ ByRef ReturnLengthInBytes As Integer _ ) As Boolean End Function  Private Shared Function CreateProcessWithTokenW(hToken As IntPtr, dwLogonFlags As Integer, lpApplicationName As String, lpCommandLine As String, dwCreationFlags As Integer, lpEnvironment As IntPtr, lpCurrentDirectory As IntPtr, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean End Function #End Region #Region "Structures"  Private Structure SECURITY_ATTRIBUTES Friend nLength As Integer Friend lpSecurityDescriptor As IntPtr Friend bInheritHandle As Integer End Structure Private Structure TOKEN_PRIVILEGES Friend PrivilegeCount As Integer Friend TheLuid As LUID Friend Attributes As Integer End Structure Private Structure LUID Friend LowPart As UInt32 Friend HighPart As UInt32 End Structure  Private Structure STARTUPINFO Friend cb As Integer Friend lpReserved As String Friend lpDesktop As String Friend lpTitle As String Friend dwX As Integer Friend dwY As Integer Friend dwXSize As Integer Friend dwYSize As Integer Friend dwXCountChars As Integer Friend dwYCountChars As Integer Friend dwFillAttribute As Integer Friend dwFlags As Integer Friend wShowWindow As Short Friend cbReserved2 As Short Friend lpReserved2 As Integer Friend hStdInput As Integer Friend hStdOutput As Integer Friend hStdError As Integer End Structure Private Structure PROCESS_INFORMATION Friend hProcess As IntPtr Friend hThread As IntPtr Friend dwProcessId As Integer Friend dwThreadId As Integer End Structure #End Region #Region "Enumerations" Private Enum TOKEN_INFORMATION_CLASS TokenUser = 1 TokenGroups TokenPrivileges TokenOwner TokenPrimaryGroup TokenDefaultDacl TokenSource TokenType TokenImpersonationLevel TokenStatistics TokenRestrictedSids TokenSessionId TokenGroupsAndPrivileges TokenSessionReference TokenSandBoxInert TokenAuditPolicy TokenOrigin TokenElevationType TokenLinkedToken TokenElevation TokenHasRestrictions TokenAccessInformation TokenVirtualizationAllowed TokenVirtualizationEnabled TokenIntegrityLevel TokenUIAccess TokenMandatoryPolicy TokenLogonSid MaxTokenInfoClass End Enum #End Region #Region "Constants" Private Const SE_PRIVILEGE_ENABLED = &H2L Private Const PROCESS_QUERY_INFORMATION = &H400 Private Const TOKEN_ASSIGN_PRIMARY = &H1 Private Const TOKEN_DUPLICATE = &H2 Private Const TOKEN_IMPERSONATE = &H4 Private Const TOKEN_QUERY = &H8 Private Const TOKEN_QUERY_SOURCE = &H10 Private Const TOKEN_ADJUST_PRIVILEGES = &H20 Private Const TOKEN_ADJUST_GROUPS = &H40 Private Const TOKEN_ADJUST_DEFAULT = &H80 Private Const TOKEN_ADJUST_SESSIONID = &H100 Private Const SecurityImpersonation = 2 Private Const TokenPrimary = 1 Private Const SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege" #End Region #Region "Methods" Friend Shared Sub LaunchFile(ByVal FilePath As String, ByVal IsWaitToFinish As Boolean) Try 'Enable the SeIncreaseQuotaPrivilege in current token Dim HPrcsToken As IntPtr = Nothing OpenProcessToken(Process.GetCurrentProcess.Handle, TOKEN_ADJUST_PRIVILEGES, HPrcsToken) Dim TokenPrvlgs As TOKEN_PRIVILEGES TokenPrvlgs.PrivilegeCount = 1 LookupPrivilegeValue(Nothing, SE_INCREASE_QUOTA_NAME, TokenPrvlgs.TheLuid) TokenPrvlgs.Attributes = SE_PRIVILEGE_ENABLED AdjustTokenPrivileges(HPrcsToken, False, TokenPrvlgs, 0, Nothing, Nothing) 'Get window handle representing the desktop shell Dim HShellWnd As IntPtr = GetShellWindow() 'Get the ID of the desktop shell process Dim ShellPID As IntPtr GetWindowThreadProcessId(HShellWnd, ShellPID) 'Open the desktop shell process in order to get the process token Dim HShellPrcs As IntPtr = OpenProcess(PROCESS_QUERY_INFORMATION, False, ShellPID) Dim HShellPrcSToken As IntPtr = Nothing Dim HPrimaryToken As IntPtr = Nothing 'Get the process token of the desktop shell OpenProcessToken(HShellPrcs, TOKEN_DUPLICATE, HShellPrcSToken) 'Duplicate the shell's process token to get a primary token Dim TokenRights As UInteger = TOKEN_QUERY Or TOKEN_ASSIGN_PRIMARY Or TOKEN_DUPLICATE Or TOKEN_ADJUST_DEFAULT Or TOKEN_ADJUST_SESSIONID DuplicateTokenEx(HShellPrcSToken, TokenRights, Nothing, SecurityImpersonation, TokenPrimary, HPrimaryToken) Dim StartInfo As STARTUPINFO = Nothing Dim PrcsInfo As PROCESS_INFORMATION = Nothing StartInfo.cb = Marshal.SizeOf(StartInfo) Dim IsSuccessed As Boolean = CreateProcessWithTokenW(HPrimaryToken, 1, FilePath, "", 0, Nothing, Nothing, StartInfo, PrcsInfo) If IsSuccessed = True Then If IsWaitToFinish = True Then Try Dim Prcs As Process = Process.GetProcessById(PrcsInfo.dwProcessId) Prcs.WaitForExit() Catch ex As Exception End Try End If Else 'Could not launch process with shell token may be the process needs admin rights to launch, we will try to launch it with default parent process permissions. Dim Prcs As New Process Prcs.StartInfo.FileName = FilePath Prcs.Start() If IsWaitToFinish = True Then Prcs.WaitForExit() End If Catch ex As Exception End Try End Sub #End Region End Class 

Uso de la clase LaunchProcess

 LaunchProcess.LaunchFile("C:\Program Files\Test\text.exe", False) 

Puede usar el convertidor de código Telerik para convertir el código a C # http://converter.telerik.com/

Puede usar la función CreateProcessWithLogonW o CreateProcessAsUser y CreateProcessWithTokenW similares.

Como el escritor describe este problema no se resuelve. Tengo el mismo problema ahora quiero describir cómo resolví esto.

Como usted sabe, no es tan fácil, pero la mejor solución es forzar a la computadora a iniciar el archivo “* .exe” mediante otro proceso que no sea de administrador. Lo que hago es crear una tarea en el Progtwigdor de tareas without Highest Privilege parámetros de without Highest Privilege . Significa hacer un tiempo o ejecutar esta tarea manualmente.

Parece estúpido, pero parece que no hay forma.

Puede ver este enlace que describe cómo crear una nueva Tarea en el progtwigdor de tareas de Windows /