C#:启动进程作为具有硬编码权限的“真正”管理员

时间:2012-06-28 08:57:06

标签: c# .net impersonation runas

我需要使用管理员权限静默安装不同的设置。 我必须硬编码权限,因为用户不知道用户名和密码来安装他们自己的设置。

我尝试了两种不同的方法。

  1. 具有UserName,Password和UseShellExecute = false的ProcessStartInfo。
  2. 用户模拟

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(...);
    
  3. 在两种情况下windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator)都返回false 由于权利不足,我的设置无法运行。

    奇怪的行为:即使使用无效的凭据,LogonUser也始终返回true。

    以下是模拟课程:

    namespace BlackBlade.Utilities
    {
        /// <summary>
        /// Quelle: http://www.blackbladeinc.com/en-us/community/blogs/archive/2009/08/10/runas-in-c.aspx
        /// </summary>
        public class SecurityUtilities
        {
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
    
            public delegate void RunAsDelegate();
    
            public static void RunAs(RunAsDelegate methodToRunAs, string username, string password)
            {
                string userName;
    
                string domain;
                if (username.IndexOf('\\') > 0)
                {
                    //a domain name was supplied
                    string[] usernameArray = username.Split('\\');
                    userName = usernameArray[1];
                    domain = usernameArray[0];
                }
                else
                {
                    //there was no domain name supplied
                    userName = username;
                    domain = ".";
                }
                RunAs(methodToRunAs, userName, password, domain);
            }
    
            public static void RunAs(RunAsDelegate methodToRunAs, string username, string password, string domain)
            {
                IntPtr userToken;
                WindowsIdentity adminIdentity = null;
                WindowsImpersonationContext adminImpersonationContext = null;
    
                try
                {
                    if (LogonUser(username, string.IsNullOrEmpty(domain) ? "." : domain, password, 9, 0, out userToken))
                    {
                        //the impersonation suceeded
                        adminIdentity = new WindowsIdentity(userToken);
                        adminImpersonationContext = adminIdentity.Impersonate();
    
                        // todo: Entfernen.
                        WindowsPrincipal p = new WindowsPrincipal(adminIdentity);
                        MessageBox.Show(p.IsInRole(WindowsBuiltInRole.Administrator).ToString());
    
                        //run the delegate method
                        //methodToRunAs();
                    }
                    else
                        throw new Exception(string.Format("Could not impersonate user {0} in domain {1} with the specified password.", username, domain));
                }
                catch (Exception se)
                {
                    int ret = Marshal.GetLastWin32Error();
                    if (adminImpersonationContext != null)
                        adminImpersonationContext.Undo();
                    throw new Exception("Error code: " + ret.ToString(), se);
                }
                finally
                {
                    //revert to self
                    if (adminImpersonationContext != null)
                        adminImpersonationContext.Undo();
                }
            }
        }
    }
    

3 个答案:

答案 0 :(得分:2)

Add a manifest到您开始使用RunAs请求提升的流程。

编辑:首先,使用已知的管理员凭据启动流程,使用LogonUser / CreateProcessAsUser或使用CreateProcessWithLogon。然后检查是否有真正的管理员权限(可能是UAC已关闭),如有必要,请让此进程(以非提升管理员身份运行)使用ShellExecuteEx using the runas verb启动自身的另一个副本。这是the only way。 UAC明确设计为在没有用户确认的情况下禁止提升。

除非关闭UAC,否则用户必须确认提升。为了获得更好的用户体验(不太可怕的消息框),请获取代码签名证书并签署此可执行文件。

答案 1 :(得分:0)

使用组策略推出MSI或EXE安装程序。

或者使用任务计划程序将安装程序作为本地系统帐户运行。

答案 2 :(得分:0)

您是否尝试过将dwLogonType设置为2而不是9?

http://msdn.microsoft.com/en-us/library/windows/desktop/bb540756(v=vs.85).aspx

这是一个适合我的代码示例:

    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);