如何在没有pipe理员权限的情况下从具有pipe理员权限的进程启动新进程?

我正在为应用程序创build一个自动更新程序。 该应用程序由用户启动,并且在没有pipe理员权限的情况下运行。 autoupdater以pipe理员权限启动,在下载新文件之前终止应用程序。

当我想在autoupdater完成后启动更新的应用程序时,问题出现了。 如果我使用常规System.Diagnostics.Process.Start(文件),则应用程序也以pipe理员权限启动,并且必须在当前用户上运行才能按预期工作。

那么,如何让autoupdater作为当前用户而不是pipe理员启动应用程序?

我曾尝试使用以下内容:

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

但是这会引发错误“无效的用户名或密码”。 我检查了用户名是正确的,我知道密码可能是无效的,因为我没有包括它。 但是要求用户input他/她的密码不是一个select,因为自动启动应用程序的全部原因是为了使用户更容易。

有什么build议么?

你试图达到的目标不是很容易完成,不被支持。 但是,可以使用少量的黑客行为。 Aaron Margosis写了一篇描述一种技术的文章 。

引用相关部分,您将需要执行这些步骤:

  1. 在当前令牌中启用SeIncreaseQuotaPrivilege
  2. 获取表示桌面shell的HWND(GetShellWindow)
  3. 获取与该窗口关联的进程的进程ID(PID)(GetWindowThreadProcessId)
  4. 打开该进程(OpenProcess)
  5. 从该进程获取访问令牌(OpenProcessToken)
  6. 使用该令牌创建主令牌(DuplicateTokenEx)
  7. 用该主令牌启动新进程(CreateProcessWithTokenW)

这篇文章包含一些下载链接,用于演示C ++源代码,它应该足够简单,可以转换为C#。

C#代码为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 

假定您正在发信号通知应用程序关闭干净,而不是终止它,如果在发布更新程序之前仍然可以对应用程序进行更改,那么一个简单的解决方案就是让应用程序在退出之前启动临时过程。 您可以在临时位置为临时过程创建可执行文件。 更新完成后,通知临时进程重新启动应用程序并退出。 这样,一切都自然而然地发生,你不必乱七八糟。

另一个选择是在使用OpenProcessOpenProcessTokenDuplicateToken来获取应用程序的安全标记副本之前,将其杀死。 然后可以使用CreateProcessAsUser在原始上下文中重新启动应用程序。

即使更新程序在不同的帐户下运行,和/或在与应用程序不同的会话中运行,这两种方法都应该可以工作。

在Codeplex上有一个叫做User Account Control Helpers的项目 。

该项目提供了一个与UAC机制互动的图书馆。

在库中你会发现一个名为UserAccountControl的类。 该类有一个名为CreateProcessAsStandardUser的静态方法,以便从具有标准用户权限的高级进程中启动进程。

简而言之,这些函数打开桌面shell进程的进程标记。 然后,它复制该令牌以获取主令牌。 然后使用此令牌在已登录的用户下启动新进程。

欲了解更多信息,请阅读来自Aaron Margosis的以下博客文章。

我遇到了类似的问题。在processstartinfo中有一个密码字段,问题是你必须提供密码作为一个安全的字符串。所以代码会是这样的:

 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(); 

实用的VB.NET代码,用高级父进程的默认shell权限启动进程

 #Region "References" Imports System.Runtime.InteropServices #End Region Public Class LaunchProcess #Region "Native Methods" <DllImport("User32.dll", SetLastError:=True)> Private Shared Function GetShellWindow() As IntPtr End Function <DllImport("advapi32.dll", SetLastError:=True)> Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean End Function <DllImport("user32.dll", SetLastError:=True)> Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, ByRef lpdwProcessId As IntPtr) As Integer End Function <DllImport("kernel32.dll")> Private Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandle As Boolean, ByVal dwProcessId As IntPtr) As IntPtr End Function <DllImport("advapi32.dll", SetLastError:=True)> _ 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 <DllImport("advapi32.dll", SetLastError:=True)> Private Shared Function LookupPrivilegeValue(lpSystemName As String, lpName As String, ByRef lpLuid As LUID) As Boolean End Function <DllImport("advapi32.dll", SetLastError:=True)> _ 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 <DllImport("advapi32", SetLastError:=True, CharSet:=CharSet.Unicode)> 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" <StructLayout(LayoutKind.Sequential)> 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 <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> 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 

LaunchProcess类的用法

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

您可以使用Telerik代码转换器将代码转换为C# http://converter.telerik.com/

您可以使用CreateProcessWithLogonW函数或类似的CreateProcessAsUser和CreateProcessWithTokenW 。

正如笔者所描述的这个问题没有解决。 我现在有同样的问题,我想描述我是如何解决这个问题的。

如你所知,这并不容易,但最好的解决办法是强制计算机通过另一个非管理进程启动“* .exe”文件。 我所做的是在without Highest Privilege参数的without Highest Privilege在任务计划程序上创建一个任务,这意味着要腾出时间或手动运行此任务。

这似乎是愚蠢的,但似乎没有办法。

你可以看到这个链接 ,描述如何创建一个新的任务在Windows任务调度程序/