Start a .Net Process as a Different User

2020-01-23 03:40发布

问题:

I want to start a Process with Admin rights. When I run the code below the Process complains saying it needs Admin rights:

public class ImpersonationHelper : IDisposable
{
    IntPtr m_tokenHandle = new IntPtr(0);
    WindowsImpersonationContext m_impersonatedUser;

    #region Win32 API Declarations

    const int LOGON32_PROVIDER_DEFAULT = 0;
    const int LOGON32_LOGON_INTERACTIVE = 2;    //This parameter causes LogonUser to create a primary token.

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
    int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

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

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
    int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

    #endregion

    /// <summary>
    /// Constructor. Impersonates the requested user. Impersonation lasts until
    /// the instance is disposed.
    /// </summary>
    public ImpersonationHelper(string domain, string user, string password)
    {
        // Call LogonUser to obtain a handle to an access token.
        bool returnValue = LogonUser(user, domain, password,
            LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
            ref m_tokenHandle);
        if (false == returnValue)
        {
            int ret = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(ret);
        }

        // Impersonate
        m_impersonatedUser = new WindowsIdentity(m_tokenHandle).Impersonate();
    }

    #region IDisposable Pattern

    /// <summary>
    /// Revert to original user and cleanup.
    /// </summary>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Revert to original user identity
            if (m_impersonatedUser != null)
                m_impersonatedUser.Undo();
        }

        // Free the tokens.
        if (m_tokenHandle != IntPtr.Zero)
            CloseHandle(m_tokenHandle);
    }

    /// <summary>
    /// Explicit dispose.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Destructor
    /// </summary>
    ~ImpersonationHelper()
    {
        Dispose(false);
    }

    #endregion
}

using (new ImpersonationHelper("xxx.blabla.com", "xxxx", "xxxx"))
{
    if (!string.IsNullOrEmpty(txtFilename.Text))
        Process.Start(txtFilename.Text);
}

回答1:

Can you try something like this: Start a new Process as another user

Code sample:

System.Diagnostics.Process proc = new System.Diagnostics.Process();
System.Security.SecureString ssPwd = new System.Security.SecureString();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "filename";
proc.StartInfo.Arguments = "args...";
proc.StartInfo.Domain = "domainname";
proc.StartInfo.UserName = "username";
string password = "user entered password";
for (int x = 0; x < password.Length; x++)
{
    ssPwd.AppendChar(password[x]);
}
password = "";
proc.StartInfo.Password = ssPwd;
proc.Start();


回答2:

Correct usage of SecureString and a few extras:

//You should use SecureString like the following
SecureString password = new SecureString();
password.AppendChar('p');
password.AppendChar('a');
password.AppendChar('s');
password.AppendChar('s');

Process process = new Process();
process.StartInfo.UseShellExecute = false;
//Set the working directory if you don't execute something like calc or iisreset but your own exe in which you want to access some files etc..
process.StartInfo.WorkingDirectory = "workingDirectory";
//Full path (e.g. it can be @"C:\Windows\System32\iisreset.exe" OR you can use only file name if the path is included in Environment Variables..)
process.StartInfo.FileName = @"fileName";
process.StartInfo.Domain = "domain";
process.StartInfo.UserName = "userName";
process.StartInfo.Password = password;
process.Start();

EDIT: I don't know why this answer is voted below 0, maybe a little more explanation is needed. If you'll use this in non-interactive environment (like a web application) and you want to run a process with a user, then you have a few options to use the user's password. You can either read password from a storage or from code. A better way; you can store it encrypted. However, if you plan to use it in plain form (maybe temporarily or just to test something etc.), you can use SecureString in a way I described. The accepted answer doesn't use SecureString in a right way. Reading password into a string from console and then putting it into a SecureString is simply WRONG. The accepted answer does NOT secure that string or something but only cheating it. That was the main motivation for me to add this answer. Check link.