如何从具有管理员权限的进程中启动没有管理员权限的新进程?
Posted
技术标签:
【中文标题】如何从具有管理员权限的进程中启动没有管理员权限的新进程?【英文标题】:How to start a new process without administrator privileges from a process with administrator privileges? 【发布时间】:2012-06-25 12:48:26 【问题描述】:我正在为应用程序创建自动更新程序。该应用程序由用户启动,无需管理员权限即可运行。自动更新程序以管理员权限启动,并在下载新文件之前终止应用程序。
当我想在自动更新程序完成后启动更新的应用程序时,问题就出现了。如果我使用常规 System.Diagnostics.Process.Start(file),应用程序也以管理员权限启动,并且它必须在当前用户上运行才能按预期工作。
那么,如何让自动更新程序以当前用户而不是管理员身份启动应用程序?
我尝试过使用以下方法:
var pSI = new ProcessStartInfo()
UseShellExecute = false,
UserName = Environment.UserName,
FileName = file
;
System.Diagnostics.Process.Start(pSI);
但这会引发错误“无效的用户名或密码”。我检查了用户名是否正确,并且我知道密码可能无效,因为我没有包含它。但是要求用户输入他/她的密码不是一个选项,因为自动启动应用程序的全部原因是为了让用户更容易。
有什么建议吗?
【问题讨论】:
This answer 描述了一种方法,您获取当前进程令牌的副本,使用SetTokenInformation
删除高完整性 SID,然后使用 CreateProcessAsUser
使用修改后的令牌启动新进程。
我知道这已经过时了,但我在这里找到了一个非常好的方法,它比这篇文章中的答案更适合我。 CreateProcessWithTokenW 调用对我来说失败了。这有效:code.msdn.microsoft.com/windowsapps/…
本题对应的C++题:***.com/questions/37948064/…
【参考方案1】:
codeplex 上有一个名为 User Account Control Helpers 的项目。
该项目提供了一个与 UAC 机制交互的库。
在库中,您会找到一个名为UserAccountControl
的类。班上
有一个名为CreateProcessAsStandardUser
的静态方法来启动一个
来自具有标准用户权限的提升进程的进程。
简而言之,这些函数打开桌面外壳进程的进程令牌。 然后,它复制该令牌以获得主令牌。然后这个令牌 用于在登录用户下启动新进程。
更多信息请阅读Aaron Margosis的以下博文。
【讨论】:
【参考方案2】:您可以使用CreateProcessWithLogonW function 或类似的CreateProcessAsUser 和CreateProcessWithTokenW。
【讨论】:
【参考方案3】:假设您发出信号让应用程序彻底关闭而不是终止它,并且如果您仍然能够在发布更新程序之前对应用程序进行更改,一个简单的解决方案是让应用程序在之前启动一个临时进程退出。您可以在临时位置为临时进程创建可执行文件。更新完成后,向中间进程发出信号以重新启动应用程序并退出。这样一来,一切都会自然发生,您不必乱来。
另一种选择是使用OpenProcess
、OpenProcessToken
和DuplicateToken
在终止应用程序之前获取应用程序安全令牌的副本。然后,您可以使用CreateProcessAsUser
在原始上下文中重新启动应用程序。
即使更新程序在不同的帐户下和/或在与应用程序的不同会话中运行,这两种方法也应该有效。
【讨论】:
【参考方案4】:您想要实现的目标不能很容易地完成,并且不受支持。但是,可以使用少量的黑客攻击。 Aaron Margosis 写了一封 article 描述了一种技术。
要引用相关部分,您需要执行以下步骤:
在您当前的令牌中启用 SeIncreaseQuotaPrivilege 获取代表桌面外壳的 HWND (GetShellWindow) 获取与该窗口关联的进程的进程 ID (PID) (GetWindowThreadProcessId) 打开该进程 (OpenProcess) 从该进程获取访问令牌 (OpenProcessToken) 使用该令牌创建一个主令牌 (DuplicateTokenEx) 使用该主令牌 (CreateProcessWithTokenW) 启动新进程
本文包含一些演示 C++ 源代码的下载链接,从中可以简单地转换为 C#。
【讨论】:
你给我的链接很好用!我不知道 c++ 我会尝试将该代码转换为 c#。一旦堆栈溢出,我会接受你的回答。谢谢! @Tono,在此之前一年多发布的the answer 中提到了相同的链接。 C# 移植是否可用? CreateProcessWithTokenW 对我来说失败了。我在这里找到了另一种效果很好的方法code.msdn.microsoft.com/windowsapps/… 如果CreateProcessWithTokenW
失败,则可能是“辅助登录服务”被禁用。不幸的是,许多所谓的“优化”网站建议禁用这种“不必要的”服务。 IShellDispatch2
technique 在这方面似乎更可靠,因为它不需要运行此服务。一个缺点是它对如何创建流程的控制较少。【参考方案5】:
我遇到了类似的问题。在 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();
【讨论】:
是的,您需要正确的密码。与此论坛上的最佳答案相比,我发现这更容易 如果您不知道密码,这并不容易。除非您拥有盒子,否则您怎么能希望知道密码? 这就是我所说的@DavidHeffernan【参考方案6】:正如作者所描述的,这个问题没有解决。 我现在有同样的问题,我想描述一下我是如何解决这个问题的。
如您所知,这并不容易,但最好的解决方案是强制计算机通过另一个非管理员进程启动“*.exe”文件。我所做的是在任务计划程序without Highest Privilege
参数上创建一个任务,这意味着要安排时间或手动运行此任务。
看起来很愚蠢,但似乎没有办法。
你可以看到this link描述了如何在windows task scheduler/上创建一个新的Task
【讨论】:
【参考方案7】:实用的 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/
【讨论】:
【参考方案8】:Aaron Margosis 的 C# 代码article:
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
【讨论】:
这是一个很好的开始,谢谢!将其扩展为返回Process
对象并传递参数相当容易:CreateProcessWithTokenW(hPrimaryToken, 0, fileName, $"\"fileName\" arguments",...
和 return Process.GetProcessById(pi.dwProcessId);
请注意,您可以等待进程退出,但不要检查其错误代码。
如何将此解决方案应用于运行我自己的 Windows 应用程序?
您好,我正在尝试使用此方案以提升用户身份运行该进程。示例:我正在从管理员帐户(或从 SYSTEM 帐户)运行代码,并且需要以桌面用户(不属于管理员组)的身份运行新进程,但我需要使用管理员运行新进程权利。【参考方案9】:
这是我多年前看到的一个非常古老的问题,现在又重新审视了。由于它是第一个谷歌搜索结果......我会在这里发布我的答案。
我发现的所有解决方案都极其复杂和荒谬。多年来,我偶然发现了一个我在任何地方都没有看到文档的解决方案,直到现在也没有真正分享过。
代码非常简单...基本上我们正在编写一个批处理文件,其中包含您要运行的进程的可执行文件的名称/路径,以及您想要的任何参数。然后我们用批处理文件的路径生成一个 explorer.exe 进程......
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();
我们生成的资源管理器进程立即被操作系统杀死,但是!根 explorer.exe 进程运行运行批处理文件!您可以为 explorer.exe 指定可执行文件的名称,它会执行相同的操作,但是该方法不支持参数。
据我所知,这是一个错误或未记录的功能。但是我无法想象它是如何被恶意使用的,因为它允许降低权限......这适用于 Windows 7/8/8.1/10。
【讨论】:
这如何解决不以管理员身份而是以不同用户身份启动进程的问题? @user3700562 因为它使用 explorer.exe 作为网关来启动 bat 文件。 这在 Windows 10 上对我不起作用。“此应用程序无法在您的电脑上运行。”。 Windows 拒绝启动.CMD
或 .BAT
这是一个安全屏幕错误。如果 Safescreen 阻止批处理文件,那是一个完全不同的问题。刚刚在没有安全屏的情况下进行了测试,它仍然有效。我维护的一个应用程序也使用了这个,所以如果它失败了,这对我来说将是一个主要的 PIA。
.bat
可以在运行后自行删除,如果你添加del %0
这一行。【参考方案10】:
在我的应用程序中,我最初使用了user3122201 的解决方案,效果很好。但是,我希望提升的应用程序使用匿名管道与低权限应用程序进行通信。为此,我需要一个允许继承管道句柄的解决方案。
我想出了下面允许句柄继承的解决方案。
请注意此解决方案与 user3122201 的另一个小区别:以下方法作为同一用户的进程运行,但访问受限,而 user3122201 的方法作为桌面用户的进程运行。
public static class ProcessHelper
/// Runs a process as a non-elevated version of the current user.
public static Process? RunAsRestrictedUser(string fileName, string? args = null)
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
if (!GetRestrictedSessionUserToken(out var hRestrictedToken))
return null;
try
var si = new STARTUPINFO();
var pi = new PROCESS_INFORMATION();
var cmd = new StringBuilder();
cmd.Append('"').Append(fileName).Append('"');
if (!string.IsNullOrWhiteSpace(args))
cmd.Append(' ').Append(args);
if (!CreateProcessAsUser(
hRestrictedToken,
null,
cmd,
IntPtr.Zero,
IntPtr.Zero,
true, // inherit handle
0,
IntPtr.Zero,
Path.GetDirectoryName(fileName),
ref si,
out pi))
return null;
return Process.GetProcessById(pi.dwProcessId);
finally
CloseHandle(hRestrictedToken);
// based on https://***.com/a/16110126/862099
private static bool GetRestrictedSessionUserToken(out IntPtr token)
token = IntPtr.Zero;
if (!SaferCreateLevel(SaferScope.User, SaferLevel.NormalUser, SaferOpenFlags.Open, out var hLevel, IntPtr.Zero))
return false;
IntPtr hRestrictedToken = IntPtr.Zero;
TOKEN_MANDATORY_LABEL tml = default;
tml.Label.Sid = IntPtr.Zero;
IntPtr tmlPtr = IntPtr.Zero;
try
if (!SaferComputeTokenFromLevel(hLevel, IntPtr.Zero, out hRestrictedToken, 0, IntPtr.Zero))
return false;
// Set the token to medium integrity.
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = IntPtr.Zero;
if (!ConvertStringSidToSid("S-1-16-8192", out tml.Label.Sid))
return false;
tmlPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tml));
Marshal.StructureToPtr(tml, tmlPtr, false);
if (!SetTokenInformation(hRestrictedToken,
TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
tmlPtr, (uint)Marshal.SizeOf(tml)))
return false;
token = hRestrictedToken;
hRestrictedToken = IntPtr.Zero; // make sure finally() doesn't close the handle
finally
SaferCloseLevel(hLevel);
SafeCloseHandle(hRestrictedToken);
if (tml.Label.Sid != IntPtr.Zero)
LocalFree(tml.Label.Sid);
if (tmlPtr != IntPtr.Zero)
Marshal.FreeHGlobal(tmlPtr);
return true;
[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;
[StructLayout(LayoutKind.Sequential)]
private struct SID_AND_ATTRIBUTES
public IntPtr Sid;
public uint Attributes;
[StructLayout(LayoutKind.Sequential)]
private struct TOKEN_MANDATORY_LABEL
public SID_AND_ATTRIBUTES Label;
public enum SaferLevel : uint
Disallowed = 0,
Untrusted = 0x1000,
Constrained = 0x10000,
NormalUser = 0x20000,
FullyTrusted = 0x40000
public enum SaferScope : uint
Machine = 1,
User = 2
[Flags]
public enum SaferOpenFlags : uint
Open = 1
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool SaferCreateLevel(SaferScope scope, SaferLevel level, SaferOpenFlags openFlags, out IntPtr pLevelHandle, IntPtr lpReserved);
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken, int dwFlags, IntPtr lpReserved);
[DllImport("advapi32", SetLastError = true)]
private static extern bool SaferCloseLevel(IntPtr hLevelHandle);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private static bool SafeCloseHandle(IntPtr hObject)
return (hObject == IntPtr.Zero) ? true : CloseHandle(hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalFree(IntPtr hMem);
enum TOKEN_INFORMATION_CLASS
/// <summary>
/// The buffer receives a TOKEN_USER structure that contains the user account of the token.
/// </summary>
TokenUser = 1,
/// <summary>
/// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token.
/// </summary>
TokenGroups,
/// <summary>
/// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
/// </summary>
TokenPrivileges,
/// <summary>
/// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects.
/// </summary>
TokenOwner,
/// <summary>
/// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects.
/// </summary>
TokenPrimaryGroup,
/// <summary>
/// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects.
/// </summary>
TokenDefaultDacl,
/// <summary>
/// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information.
/// </summary>
TokenSource,
/// <summary>
/// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token.
/// </summary>
TokenType,
/// <summary>
/// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails.
/// </summary>
TokenImpersonationLevel,
/// <summary>
/// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics.
/// </summary>
TokenStatistics,
/// <summary>
/// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token.
/// </summary>
TokenRestrictedSids,
/// <summary>
/// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token.
/// </summary>
TokenSessionId,
/// <summary>
/// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token.
/// </summary>
TokenGroupsAndPrivileges,
/// <summary>
/// Reserved.
/// </summary>
TokenSessionReference,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag.
/// </summary>
TokenSandBoxInert,
/// <summary>
/// Reserved.
/// </summary>
TokenAuditPolicy,
/// <summary>
/// The buffer receives a TOKEN_ORIGIN value.
/// </summary>
TokenOrigin,
/// <summary>
/// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token.
/// </summary>
TokenElevationType,
/// <summary>
/// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token.
/// </summary>
TokenLinkedToken,
/// <summary>
/// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated.
/// </summary>
TokenElevation,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if the token has ever been filtered.
/// </summary>
TokenHasRestrictions,
/// <summary>
/// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token.
/// </summary>
TokenAccessInformation,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token.
/// </summary>
TokenVirtualizationAllowed,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token.
/// </summary>
TokenVirtualizationEnabled,
/// <summary>
/// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level.
/// </summary>
TokenIntegrityLevel,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set.
/// </summary>
TokenUIAccess,
/// <summary>
/// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy.
/// </summary>
TokenMandatoryPolicy,
/// <summary>
/// The buffer receives the token's logon security identifier (SID).
/// </summary>
TokenLogonSid,
/// <summary>
/// The maximum value for this enumeration
/// </summary>
MaxTokenInfoClass
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean SetTokenInformation(
IntPtr TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation,
UInt32 TokenInformationLength);
const uint SE_GROUP_INTEGRITY = 0x00000020;
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string? lpApplicationName,
StringBuilder? lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string? lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
【讨论】:
以上是关于如何从具有管理员权限的进程中启动没有管理员权限的新进程?的主要内容,如果未能解决你的问题,请参考以下文章