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

时间:2012-06-23 12:17:57

标签: c# windows administrator

我正在为应用程序创建一个自动更新程序。应用程序由用户启动,无需管理员权限即可运行。 autoupdater以管理员权限启动,并在下载新文件之前终止应用程序。

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

那么,如何让autoupdater以当前用户而不是管理员的身份启动应用程序呢?

我尝试过使用以下内容:

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

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

有什么建议吗?

10 个答案:

答案 0 :(得分:12)

您想要实现的目标不能轻易完成,也不受支持。但是,可以使用少量的黑客攻击。 Aaron Margosis写了一篇描述一种技巧的article

引用相关部分,您需要执行以下步骤:

  
      
  1. 在当前令牌中启用SeIncreaseQuotaPrivilege
  2.   
  3. 获取代表桌面shell的HWND(GetShellWindow)
  4.   
  5. 获取与该窗口关联的进程的进程ID(PID)(GetWindowThreadProcessId)
  6.   
  7. 打开该流程(OpenProcess)
  8.   
  9. 从该进程获取访问令牌(OpenProcessToken)
  10.   
  11. 使用该令牌制作主令牌(DuplicateTokenEx)
  12.   
  13. 使用该主令牌(CreateProcessWithTokenW)
  14. 启动新进程   

本文包含一些演示C ++源代码的下载链接,从中可以很容易地转换为C#。

答案 1 :(得分:10)

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

答案 2 :(得分:6)

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

另一种选择是使用OpenProcessOpenProcessTokenDuplicateToken获取应用程序安全令牌的副本,然后再将其删除。然后,您可以使用CreateProcessAsUser在原始上下文中重新启动应用程序。

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

答案 3 :(得分:4)

这是我多年前看到的一个非常古老的问题,现在又重新审视过了。自从它的第一个谷歌搜索结果...我会在这里发布我的答案。

我发现的所有解决方案都非常复杂和荒谬。多年来,我偶然发现了一个我从未见过的解决方案,直到现在还没有真正分享过。

代码非常简单......基本上我们正在编写一个批处理文件,其中包含您要运行的进程的可执行文件的名称/路径,以及您想要的任何参数。然后我们使用批处理文件的路径生成一个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。

答案 4 :(得分:3)

codeplex上有一个名为User Account Control Helpers的项目。

该项目提供了一个与UAC机制交互的库。

在图书馆中,您会找到一个名为UserAccountControl的课程。班级 有一个名为CreateProcessAsStandardUser的静态方法来启动a 从具有标准用户权限的高级流程进行处理。

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

有关详细信息,请阅读Aaron Margosis中的以下博客文章。

答案 5 :(得分:2)

我遇到了类似的问题。在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();

答案 6 :(得分:2)

实用的VB.NET代码,用于从提升的父进程启动具有默认shell权限的进程

func eventCallBack(proxy: CGEventTapProxy, type: CGEventType, var event: CGEvent, refcon: UnsafeMutablePointer<Void>) -> Unmanaged<CGEvent>? {

    //this line is what causes the memory leak
    if var e: NSEvent? = NSEvent(CGEvent: event) { 
       e = nil //according to docs, this should decrease reference count to event
    }

    return Unmanaged.passUnretained(event!)
}

LaunchProcess类用法

#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

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

答案 7 :(得分:0)

答案 8 :(得分:0)

正如作者所描述的那样,这个问题并没有解决。 我现在遇到同样的问题我想描述一下我是如何解决这个问题的。

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

这似乎很愚蠢,但似乎没办法。

您可以看到描述如何在Windows任务计划程序/

上创建新任务的this link

答案 9 :(得分:0)

在我的应用中,我最初使用了user3122201的解决方案,效果很好。但是,我希望提升权限的应用程序使用匿名管道与低权限应用程序进行通信。为此,我需要一个允许继承管道句柄的解决方案。

我想出了以下允许句柄继承的解决方案。

请注意该解决方案与user3122201的另一个小区别:以下方法与同一用户一样作为进程运行,但是访问受限,而user3122201的方法作为桌面用户运行。

innerHTML