如何启动流程升级

我的应用程序运行, highestAvailable设置为highestAvailable

如何运行一个没有升级的流程?

我尝试了以下但它不起作用:

 Process.Start(new ProcessStartInfo {FileName = "foo.exe", Verb = "open"}) 

我尝试了以下信任级别来使用Win32 API启动我的进程,但它们都没有正常工作:

 0 1260: This program is blocked by group policy. For more information, contact your system administrator. 0x1000 The application was unable to start correctly (0xc0000142). Click OK to close the application. 0x10000 Process starts then hangs 0x20000 All options are not available 0x40000 Runs as admin 

如果我从我的提升应用程序运行tskill foo ,它会以正确的权限重新启动foo。

我需要的是一个解决方案,我不必指定信任级别。 该过程应该自动以正确的信任级别开始,就像tskill工具以正确的信任级别重新启动foo.exe一样。 用户选择并运行foo.exe ,因此它可以是任何东西。

如果我能以某种方式获得进程的信任级别,我可以轻松地执行此操作,因为当我的应用程序可以捕获其信任级别时foo.exe运行。

Win32安全管理function提供了创建具有普通用户权限的受限令牌的function; 使用令牌,您可以调用CreateProcessAsUser来使用该令牌运行进程。 以下是将cmd.exe作为普通用户运行的概念validation,无论该进程是否在提升的上下文中运行。

 // Initialize variables. IntPtr hSaferLevel, hToken; STARTUPINFO si = default(STARTUPINFO); SECURITY_ATTRIBUTES processAttributes = default(SECURITY_ATTRIBUTES); SECURITY_ATTRIBUTES threadAttributes = default(SECURITY_ATTRIBUTES); PROCESS_INFORMATION pi; si.cb = Marshal.SizeOf(si); // The process to start (for demonstration, cmd.exe) string ProcessName = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); // Create the restricted token info if (!SaferCreateLevel( SaferScopes.User, SaferLevels.NormalUser, // Program will execute as a normal user 1, // required out hSaferLevel, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); // From the level create a token if (!SaferComputeTokenFromLevel( hSaferLevel, IntPtr.Zero, out hToken, SaferComputeTokenFlags.None, IntPtr.Zero)) throw new Win32Exception(Marshal.GetLastWin32Error()); // Run the process with the restricted token if (!CreateProcessAsUser( hToken, ProcessName, null, ref processAttributes, ref threadAttributes, true, 0, IntPtr.Zero, null, ref si, out pi)) throw new Win32Exception(Marshal.GetLastWin32Error()); // Cleanup if (!CloseHandle(pi.hProcess)) throw new Win32Exception(Marshal.GetLastWin32Error()); if (!CloseHandle(pi.hThread)) throw new Win32Exception(Marshal.GetLastWin32Error()); if (!SaferCloseLevel(hSaferLevel)) throw new Win32Exception(Marshal.GetLastWin32Error()); 

此方法使用以下Win32函数:

  • SaferIdentifyLevel指示身份级别(有限,正常或提升)。 将levelId设置为SAFER_LEVELID_NORMALUSER (0x20000)可提供正常的用户级别。
  • SaferComputeTokenFromLevel为提供的级别创建标记。 将NULL传递给InAccessToken参数使用当前线程的标识。
  • CreateProcessAsUser使用提供的令牌创建进程。 由于会话已经是交互式的,因此大多数参数可以保持默认值。 (第三个参数lpCommandLine可以作为字符串提供,以指定命令行。)
  • CloseHandle (Kernel32)和SaferCloseLevel释放分配的内存。

最后,P / Invoke代码如下(主要从pinvoke.net复制):

 [Flags] public enum SaferLevels : uint { Disallowed = 0, Untrusted = 0x1000, Constrained = 0x10000, NormalUser = 0x20000, FullyTrusted = 0x40000 } [Flags] public enum SaferComputeTokenFlags : uint { None = 0x0, NullIfEqual = 0x1, CompareOnly = 0x2, MakeIntert = 0x4, WantFlags = 0x8 } [Flags] public enum SaferScopes : uint { Machine = 1, User = 2 } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 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; } [StructLayout(LayoutKind.Sequential)] internal struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken, int dwFlags, IntPtr lpReserved); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 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", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferCreateLevel( SaferScopes dwScopeId, SaferLevels dwLevelId, int OpenFlags, out IntPtr pLevelHandle, IntPtr lpReserved); [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferCloseLevel( IntPtr pLevelHandle); [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferComputeTokenFromLevel( IntPtr levelHandle, IntPtr inAccessToken, out IntPtr outAccessToken, SaferComputeTokenFlags dwFlags, IntPtr lpReserved ); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject); 

我通过克隆Explorer的令牌获得了最佳结果,如下所示:

 var shellWnd = WinAPI.GetShellWindow(); if (shellWnd == IntPtr.Zero) throw new Exception("Could not find shell window"); uint shellProcessId; WinAPI.GetWindowThreadProcessId(shellWnd, out shellProcessId); var hShellProcess = WinAPI.OpenProcess(0x00000400 /* QueryInformation */, false, shellProcessId); var hShellToken = IntPtr.Zero; if (!WinAPI.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out hShellToken)) throw new Win32Exception(); uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/; var hToken = IntPtr.Zero; WinAPI.DuplicateTokenEx(hShellToken, tokenAccess, IntPtr.Zero, 2 /* SecurityImpersonation */, 1 /* TokenPrimary */, out hToken); var pi = new WinAPI.PROCESS_INFORMATION(); var si = new WinAPI.STARTUPINFO(); si.cb = Marshal.SizeOf(si); if (!WinAPI.CreateProcessWithTokenW(hToken, 0, null, cmdArgs, 0, IntPtr.Zero, null, ref si, out pi)) throw new Win32Exception(); 

替代方法

最初我选择了drf的优秀答案,但稍微扩展了一下。 如果上面(克隆资源管理器的令牌)不符合您的喜好,请继续阅读,但最后会看到问题

当使用所描述的drf方法时,该过程在没有管理访问权限的情况下启动,但仍具有高完整性级别。 典型的未升级过程具有中等完整性级别。

试试这个:使用Process Hacker以这种方式查看进程的属性; 您将看到PH认为该进程即使没有管理访问权限也会被提升。 添加完整性列,您会看到它是“高”。

修复非常简单:使用SaferComputeTokenFromLevel ,我们需要将令牌完整性级别更改为“中”。 执行此操作的代码可能如下所示(从MSDN示例转换):

 // Get the Medium Integrity SID if (!WinAPI.ConvertStringSidToSid("S-1-16-8192", out pMediumIntegritySid)) throw new Win32Exception(); // Construct a structure describing the token integrity level var TIL = new TOKEN_MANDATORY_LABEL(); TIL.Label.Attributes = 0x00000020 /* SE_GROUP_INTEGRITY */; TIL.Label.Sid = pMediumIntegritySid; pTIL = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(TIL, pTIL, false); // Modify the token if (!WinAPI.SetTokenInformation(hToken, 25 /* TokenIntegrityLevel */, pTIL, (uint) Marshal.SizeOf() + WinAPI.GetLengthSid(pMediumIntegritySid))) throw new Win32Exception(); 

唉,这仍然没有完全解决问题。 该流程无权管理; 它不会具有高完整性,但仍然会有一个标记为“提升”的标记。

这是否是一个问题,我不知道,但这可能是我最终克隆Explorer的令牌的原因,如本回答开头所述。


这是我的完整源代码(修改过的drf的答案),在它的所有P / Invoke荣耀中:

 var hSaferLevel = IntPtr.Zero; var hToken = IntPtr.Zero; var pMediumIntegritySid = IntPtr.Zero; var pTIL = IntPtr.Zero; var pi = new WinAPI.PROCESS_INFORMATION(); try { var si = new WinAPI.STARTUPINFO(); si.cb = Marshal.SizeOf(si); var processAttributes = new WinAPI.SECURITY_ATTRIBUTES(); var threadAttributes = new WinAPI.SECURITY_ATTRIBUTES(); var args = CommandRunner.ArgsToCommandLine(Args); if (!WinAPI.SaferCreateLevel(WinAPI.SaferScopes.User, WinAPI.SaferLevels.NormalUser, 1, out hSaferLevel, IntPtr.Zero)) throw new Win32Exception(); if (!WinAPI.SaferComputeTokenFromLevel(hSaferLevel, IntPtr.Zero, out hToken, WinAPI.SaferComputeTokenFlags.None, IntPtr.Zero)) throw new Win32Exception(); if (!WinAPI.ConvertStringSidToSid("S-1-16-8192", out pMediumIntegritySid)) throw new Win32Exception(); var TIL = new TOKEN_MANDATORY_LABEL(); TIL.Label.Attributes = 0x00000020 /* SE_GROUP_INTEGRITY */; TIL.Label.Sid = pMediumIntegritySid; pTIL = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(TIL, pTIL, false); if (!WinAPI.SetTokenInformation(hToken, 25 /* TokenIntegrityLevel */, pTIL, (uint) Marshal.SizeOf() + WinAPI.GetLengthSid(pMediumIntegritySid))) throw new Win32Exception(); if (!WinAPI.CreateProcessAsUser(hToken, null, commandLine, ref processAttributes, ref threadAttributes, true, 0, IntPtr.Zero, null, ref si, out pi)) throw new Win32Exception(); } finally { if (hToken != IntPtr.Zero && !WinAPI.CloseHandle(hToken)) throw new Win32Exception(); if (pMediumIntegritySid != IntPtr.Zero && WinAPI.LocalFree(pMediumIntegritySid) != IntPtr.Zero) throw new Win32Exception(); if (pTIL != IntPtr.Zero) Marshal.FreeHGlobal(pTIL); if (pi.hProcess != IntPtr.Zero && !WinAPI.CloseHandle(pi.hProcess)) throw new Win32Exception(); if (pi.hThread != IntPtr.Zero && !WinAPI.CloseHandle(pi.hThread)) throw new Win32Exception(); } 

除了drf的答案中列出的那些之外,这里还有你需要的P / Invoke定义:

 [DllImport("advapi32.dll", SetLastError = true)] public static extern Boolean SetTokenInformation(IntPtr TokenHandle, int TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("advapi32.dll")] public static extern uint GetLengthSid(IntPtr pSid); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool ConvertStringSidToSid( string StringSid, out IntPtr ptrSid); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LocalFree(IntPtr hMem);