访问在C#中模拟用户的HKCU注册表子项。

huangapple go评论96阅读模式
英文:

Access the HKCU registry hive for an impersonated user in C#

问题

我需要实现模拟域用户的功能。被模拟的线程需要能够读取/写入模拟用户的HKCU注册表。我可以成功模拟用户,但是当我尝试加载任何注册表键时,我收到一个Win32 "访问被拒绝" 异常。

注意: 这里的意图是提供一个伪模拟的命令行来执行一组特定的操作作为服务帐户。服务帐户可能没有交互式登录权限,因此我需要使用BATCH登录类型。作为测试,我还尝试了INTERACTIVE登录类型,但结果相同。

我按照这个CodeProject文章作为一个通用指南进行操作。以下是我的代码:

partial class Program
{
    [DllImport("advapi32.dll")]
    public static extern int LogonUser(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, SetLastError = true)]
    public static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
    public static extern int RegOpenCurrentUser(int samDesired, out IntPtr phkResult);

    [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);

    [DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool UnloadUserProfile(IntPtr hToken, IntPtr lpProfileInfo);

    [StructLayout(LayoutKind.Sequential)]
    public struct ProfileInfo
    {
        public int dwSize;
        public int dwFlags;
        public string lpUserName;
        public string lpProfilePath;
        public string lpDefaultPath;
        public string lpServerName;
        public string lpPolicyPath;
        public IntPtr hProfile;
    }

    private static string ImpUser = string.Empty;
    private static string ImpDomain = string.Empty;
    private static string FullyQualifiedImpUser
    {
        get
        {
            return $"{ImpDomain}\\{ImpUser}";
        }
    }
    private static SecureString ImpSecret = new SecureString();
    private static bool CurrentlyImpersonating = false;

    private static WindowsIdentity ImpersonatedIdentity = null;
    private static IntPtr Token = IntPtr.Zero;
    private static IntPtr TokenDuplicate = IntPtr.Zero;

    //*** THIS IS THE CORE METHOD ***
    private static void EnterModeImpersonated()
    {
        bool loadSuccess;
        int errCode;

        try
        {
            if (RevertToSelf())
            {

                if (LogonUser(ImpUser, ImpDomain,
                              ImpSecret.Plaintext(), Constants.LOGON32_LOGON_TYPE_BATCH,
                              Constants.LOGON32_PROVIDER_DEFAULT, ref Token) != 0)
                {
                    if (DuplicateToken(Token, Constants.SecurityImpersonation, ref TokenDuplicate) != 0)
                    {
                        ImpersonatedIdentity = new WindowsIdentity(TokenDuplicate);
                        using (WindowsImpersonationContext m_ImpersonationContext = ImpersonatedIdentity.Impersonate())
                        {
                            if (m_ImpersonationContext != null)
                            {

                                #region LoadUserProfile
                                // Load user profile
                                ProfileInfo profileInfo = new ProfileInfo();
                                profileInfo.dwSize = Marshal.SizeOf(profileInfo);
                                profileInfo.lpUserName = ImpUser;
                                profileInfo.dwFlags = 1;

                                //Here is where I die:
                                loadSuccess = LoadUserProfile(TokenDuplicate, ref profileInfo);

                                if (!loadSuccess)
                                {
                                    errCode = Marshal.GetLastWin32Error();
                                    Win32Exception ex = new Win32Exception(errCode);
                                    throw new Exception($"Failed to load profile for {FullyQualifiedImpUser}. Error code: {errCode}", ex);
                                }

                                if (profileInfo.hProfile == IntPtr.Zero)
                                {
                                    errCode = Marshal.GetLastWin32Error();
                                    Win32Exception ex = new Win32Exception(errCode);
                                    throw new Exception($"Failed accessing HKCU registry for {FullyQualifiedImpUser}. Error code: {errCode}", ex);
                                }
                                #endregion

                                CloseHandle(Token);
                                CloseHandle(TokenDuplicate);

                                RegistryAgent.GetRootKeys(profileInfo.hProfile);

                                EnterMode();

                                UnloadUserProfile(TokenDuplicate, profileInfo.hProfile);
                                m_ImpersonationContext.Undo();
                                RegistryAgent.GetRootKeys(Constants.RevertToInvoker);
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("DuplicateToken() failed with error code: " +
                                          Marshal.GetLastWin32Error());
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
            }
        }
        catch (Win32Exception we)
        {
            throw we;
        }
        catch
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        finally
        {
            if (Token != IntPtr.Zero) CloseHandle(Token);
            if (TokenDuplicate != IntPtr.Zero) CloseHandle(TokenDuplicate);

            Console.WriteLine("After finished impersonation: " +
                              WindowsIdentity.GetCurrent().Name);
        }
    }

    //Toggles on impersonation mode
    //Here, we grab the username, domain and password.
    private static bool EnableImpersonation(string userInfo)
    {
        if (userInfo.Contains('\\'))
        {
            string[] parts = Parameter.ImpUser.TextValue.Split('\\');
            ImpUser = parts[1];
            ImpDomain = parts[0];
        }
        else
        {
            ImpUser = userInfo;
            ImpDomain = Environment.UserDomainName;
        }

        //Prompt for the invoker to enter the impersonated account password
        GetSecret();

        if (TryImpersonate())
        {
            CurrentlyImpersonating = true;
        }
        else
        {
            DisableImpersonation();
        }

        return CurrentlyImpersonating;
    }

    //Toggles off impersontation & cleans up
    private static void DisableImpersonation()
    {
        ImpSecret = null;
        ImpersonatedIdentity = null;
        Token = IntPtr.Zero;
        TokenDuplicate = IntPtr.Zero;
        ImpUser = string.Empty;
        ImpDomain = string.Empty;

        CurrentlyImpersonating = false;
    }

    //Implements a console prompt to grab the impersonated account password
    //as a SecureString object
    private static void GetSecret()
    {
        ImpSecret = new SecureString();
        ConsoleKeyInfo key;

        Console.Write("\r\nEnter the password for {FullyQualifiedImpUser}: ");
        do
        {
            key = Console.ReadKey(true);
            if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
            {
                ImpSecret.Append

<details>
<summary>英文:</summary>

I need to implement functionality that impersonates a domain user. The impersonated thread needs to be able to read from/write to HKCU registry hive for the impersonated user. I am able to impersonate the user, but when I attempt to load any registry keys, I receive a Win32 &quot;Access is denied&quot; exception.

**NOTE:** The intention here is to provide a pseudo impersonated command-line to perform a specific set of actions as a service account. The service account may not have interactive logon rights, so I am required to use the BATCH logon type. As a test, I did also try the INTERACTIVE logon type, but the result was the same.

I followed [this CodeProject article][1] as a general guide. Here is what I have:

    partial class Program
    {
        [DllImport(&quot;advapi32.dll&quot;)]
        public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport(&quot;advapi32.dll&quot;, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

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

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

        [DllImport(&quot;advapi32.dll&quot;, CharSet = CharSet.Auto)]
        public static extern int RegOpenCurrentUser(int samDesired, out IntPtr phkResult);

        [DllImport(&quot;userenv.dll&quot;, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);

        [DllImport(&quot;Userenv.dll&quot;, CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool UnloadUserProfile(IntPtr hToken, IntPtr lpProfileInfo);

        [StructLayout(LayoutKind.Sequential)]
        public struct ProfileInfo
        {
            public int dwSize;
            public int dwFlags;
            public string lpUserName;
            public string lpProfilePath;
            public string lpDefaultPath;
            public string lpServerName;
            public string lpPolicyPath;
            public IntPtr hProfile;
        }

        private static string ImpUser = string.Empty;
        private static string ImpDomain = string.Empty;
        private static string FullyQualifiedImpUser
        {
            get
            {
                return $&quot;{ImpDomain}\\{ImpUser}&quot;;
            }
        }
        private static SecureString ImpSecret = new SecureString();
        private static bool CurrentlyImpersonating = false;

        private static WindowsIdentity ImpersonatedIdentity = null;
        private static IntPtr Token = IntPtr.Zero;
        private static IntPtr TokenDuplicate = IntPtr.Zero;

        //*** THIS IS THE CORE METHOD ***
        private static void EnterModeImpersonated()
        {
            bool loadSuccess;
            int errCode;

            try
            {
                if (RevertToSelf())
                {

                    if (LogonUser(ImpUser, ImpDomain,
                                  ImpSecret.Plaintext(), Constants.LOGON32_LOGON_TYPE_BATCH,
                                  Constants.LOGON32_PROVIDER_DEFAULT, ref Token) != 0)
                    {
                        if (DuplicateToken(Token, Constants.SecurityImpersonation, ref TokenDuplicate) != 0)
                        {
                            ImpersonatedIdentity = new WindowsIdentity(TokenDuplicate);
                            using (WindowsImpersonationContext m_ImpersonationContext = ImpersonatedIdentity.Impersonate())
                            {
                                if (m_ImpersonationContext != null)
                                {

                                    #region LoadUserProfile
                                    // Load user profile
                                    ProfileInfo profileInfo = new ProfileInfo();
                                    profileInfo.dwSize = Marshal.SizeOf(profileInfo);
                                    profileInfo.lpUserName = ImpUser;
                                    profileInfo.dwFlags = 1;

                                    //Here is where I die:
                                    loadSuccess = LoadUserProfile(TokenDuplicate, ref profileInfo);

                                    if (!loadSuccess)
                                    {
                                        errCode = Marshal.GetLastWin32Error();
                                        Win32Exception ex = new Win32Exception(errCode);
                                        throw new Exception($&quot;Failed to load profile for {FullyQualifiedImpUser}. Error code: {errCode}&quot;, ex);
                                    }

                                    if (profileInfo.hProfile == IntPtr.Zero)
                                    {
                                        errCode = Marshal.GetLastWin32Error();
                                        Win32Exception ex = new Win32Exception(errCode);
                                        throw new Exception($&quot;Failed accessing HKCU registry for {FullyQualifiedImpUser}. Error code: {errCode}&quot;, ex);
                                    }
                                    #endregion

                                    CloseHandle(Token);
                                    CloseHandle(TokenDuplicate);

                                    RegistryAgent.GetRootKeys(profileInfo.hProfile);

                                    EnterMode();

                                    UnloadUserProfile(TokenDuplicate, profileInfo.hProfile);
                                    m_ImpersonationContext.Undo();
                                    RegistryAgent.GetRootKeys(Constants.RevertToInvoker);
                                }
                            }
                        }
                        else
                        {
                            Console.WriteLine(&quot;DuplicateToken() failed with error code: &quot; +
                                              Marshal.GetLastWin32Error());
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                    }
                }
            }
            catch (Win32Exception we)
            {
                throw we;
            }
            catch
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            finally
            {
                if (Token != IntPtr.Zero) CloseHandle(Token);
                if (TokenDuplicate != IntPtr.Zero) CloseHandle(TokenDuplicate);

                Console.WriteLine(&quot;After finished impersonation: &quot; +
                                  WindowsIdentity.GetCurrent().Name);
            }
        }

        //Toggles on impersonation mode
        //Here, we grab the username, domain and password.
        private static bool EnableImpersonation(string userInfo)
        {
            if (userInfo.Contains(&#39;\\&#39;))
            {
                string[] parts = Parameter.ImpUser.TextValue.Split(&#39;\\&#39;);
                ImpUser = parts[1];
                ImpDomain = parts[0];
            }
            else
            {
                ImpUser = userInfo;
                ImpDomain = Environment.UserDomainName;
            }

            //Prompt for the invoker to enter the impersonated account password
            GetSecret();

            if (TryImpersonate())
            {
                CurrentlyImpersonating = true;
            }
            else
            {
                DisableImpersonation();
            }

            return CurrentlyImpersonating;
        }

        //Toggles off impersontation &amp; cleans up
        private static void DisableImpersonation()
        {
            ImpSecret = null;
            ImpersonatedIdentity = null;
            Token = IntPtr.Zero;
            TokenDuplicate = IntPtr.Zero;
            ImpUser = string.Empty;
            ImpDomain = string.Empty;

            CurrentlyImpersonating = false;
        }

        //Implements a console prompt to grab the impersonated account password
        //as a SecureString object
        private static void GetSecret()
        {
            ImpSecret = new SecureString();
            ConsoleKeyInfo key;

            Console.Write($&quot;\r\nEnter the password for {FullyQualifiedImpUser}: &quot;);
            do
            {
                key = Console.ReadKey(true);
                if (key.Key != ConsoleKey.Backspace &amp;&amp; key.Key != ConsoleKey.Enter)
                {
                    ImpSecret.AppendChar(key.KeyChar);
                    Console.Write(&quot;*&quot;);
                }
                else
                {
                    if (key.Key == ConsoleKey.Backspace &amp;&amp; ImpSecret.Length != 0)
                    {
                        ImpSecret.RemoveAt(ImpSecret.Length - 1);
                        Console.Write(&quot;\b \b&quot;);
                    }
                }
            }
            while (key.Key != ConsoleKey.Enter);
            Console.WriteLine();
        }

        //This method is intended to ensure that the credentials entered
        //for the impersonated user are correct.
        private static bool TryImpersonate()
        {
            IntPtr testToken = IntPtr.Zero;
            int result;

            try
            {
                result = LogonUser(ImpUser, ImpDomain, ImpSecret.Plaintext(), Constants.LOGON32_LOGON_TYPE_BATCH, Constants.LOGON32_PROVIDER_DEFAULT, ref testToken);

                if (result == 0)
                {
                    int errCode = Marshal.GetLastWin32Error();
                    Win32Exception ex = new Win32Exception(errCode);
                    throw new Exception($&quot;Failed to impersonate {FullyQualifiedImpUser}. Error code: {errCode}&quot;, ex);
                }

                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }
        }
    }


I also read [The MSDN documentation for LoadUserProfileA][2] (I didn&#39;t find an article for LoadUserProfile() so I have to assume this is the ultimate COM function being called).  It indicates:
*The token must have TOKEN_QUERY, TOKEN_IMPERSONATE, and TOKEN_DUPLICATE access.*.  I&#39;m wondering if the logon token or duplicated token needs to be created differently in order to include these rights? I wasn&#39;t able to find any documentation on how to manipulate the token rights, though...


  [1]: https://www.codeproject.com/Articles/124981/Impersonating-user-accessing-files-and-HKCU
  [2]: https://learn.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-loaduserprofilea

</details>


# 答案1
**得分**: 2

我成功解决了这个问题。以下是我所做的:

首先,需要公开几个Win32方法

```csharp
[DllImport("advapi32.dll")]
public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

[DllImport("userenv.dll")]
public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegDisablePredefinedCache();

还需要定义一个结构以支持调用LoadUserProfile()

[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
    public int dwSize;
    public int dwFlags;
    public string lpUserName;
    public string lpProfilePath;
    public string lpDefaultPath;
    public string lpServerName;
    public string lpPolicyPath;
    public IntPtr hProfile;
}

我们将在SecureString对象中存储模拟帐户密码,但我们也希望能够轻松访问它作为纯文本。

我使用以下方法在控制台提示下填充SecureString密码,以掩盖密码:

public static SecureString GetPasswordAsSecureString(string prompt)
{
    SecureString pwd = new SecureString();
    ConsoleKeyInfo key;

    Console.Write(prompt + ": ");

    do
    {
        key = Console.ReadKey(true);
        if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
        {
            pwd.AppendChar(key.KeyChar);
            Console.Write("*");
        }
        else
        {
            if (key.Key == ConsoleKey.Backspace && pwd.Length != 0)
            {
                pwd.RemoveAt(pwd.Length - 1);
                Console.Write("\b \b");
            }
        }
    }
    while (key.Key != ConsoleKey.Enter);
    Console.WriteLine();
    return pwd;
}

var impPassword = GetPasswordAsSecureString($"输入{impUser}的密码");

我还建议定义以下扩展方法,以便将SecureString方便地转换为普通字符串,因为我们需要使用的Win32方法之一只接受普通字符串:

public static string ToUnSecureString(this SecureString securePassword)
{
    if (securePassword == null)
    {
        return string.Empty;
    }

    IntPtr unmanagedString = IntPtr.Zero;
    try
    {
        unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
        return Marshal.PtrToStringUni(unmanagedString);
    }
    finally
    {
        Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
    }
}

在与模拟有关的任何其他操作之前,我们需要调用Win32方法RegDisablePredefinedCache()。就我们的目的而言,此方法告诉Windows动态确定查找HKEY_CURRENT_USER注册表蜂巢的位置,而不是使用最初调用进程时的缓存位置(未调用此方法会导致我之前收到的“拒绝访问”异常。模拟用户试图加载调用者帐户的HKCU注册表蜂巢,显然是不允许的):

RegDisablePredefinedCache();

接下来,我们需要在进入模拟线程之前加载该帐户的配置文件,以确保模拟帐户的注册表蜂巢在内存中可用。我们调用LogonUser()LoadUserProfile() COM方法以实现此目的:

// 获取用户的令牌
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_PROVIDER_DEFAULT = 0;

// 我们将使用我们的扩展方法将密码作为普通字符串传递
LogonUser(ImpUser, ImpDomain, ImpPassword.ToUnSecureString(), LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, ref Token);

// 加载用户配置文件
ProfileInfo profileInfo = new ProfileInfo();
profileInfo.dwSize = Marshal.SizeOf(profileInfo);
profileInfo.lpUserName = ImpUser;
profileInfo.dwFlags = 1;
bool loadSuccess = LoadUserProfile(Token, ref profileInfo);

// 检测和优雅处理失败
if (!loadSuccess)
{
    errCode = Marshal.GetLastWin32Error();
    Win32Exception ex = new Win32Exception(errCode);
    throw new Exception($"加载{ImpUser}的配置文件失败。错误代码:{errCode}", ex);
}

if (profileInfo.hProfile == IntPtr.Zero)
{
    errCode = Marshal.GetLastWin32Error();
    Win32Exception ex = new Win32Exception(errCode);
    throw new Exception($"访问{ImpUser}的HKCU注册表失败。错误代码:{errCode}", ex);
}

最后,感谢这个问题留下的一条评论,我发现了一个名为SimpleImpersonation的很棒的NuGet包,它隐藏了与模拟帐户相关的大部分复杂性:

// 请注意,我选择的UserCredentials()构造函数要求密码以SecureString对象的形式传递。
var Credentials = new UserCredentials(impDomain, impUser, impPassword);
Impersonation.RunAsUser(Credentials, LogonType.Batch, () =>
{
    // 在此块内,您可以调用方法,例如
    // Registry.CurrentUser.OpenSubKey()
    // 它们将使用模拟帐户的注册表蜂巢
}
英文:

I was able to resolve this problem. Here's what I did:

First, there are several Win32 methods which need to be exposed:

[DllImport(&quot;advapi32.dll&quot;)]
public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport(&quot;userenv.dll&quot;)]
public static extern bool LoadUserProfile(IntPtr hToken, ref ProfileInfo lpProfileInfo);
[DllImport(&quot;advapi32.dll&quot;, CharSet = CharSet.Auto)]
public static extern int RegDisablePredefinedCache();

You'll also need to define a struct in support of calling LoadUserProfile()

[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
public int dwSize;
public int dwFlags;
public string lpUserName;
public string lpProfilePath;
public string lpDefaultPath;
public string lpServerName;
public string lpPolicyPath;
public IntPtr hProfile;
}

We're going to store the impersonation account password in a SecureString object, but we also want to be able to easily access it as plaintext.

I used the following method to populate the SecureString password, masked, at a console prompt:

public static SecureString GetPasswordAsSecureString(string prompt)
{
SecureString pwd = new SecureString();
ConsoleKeyInfo key;
Console.Write(prompt + @&quot;: &quot;);
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace &amp;&amp; key.Key != ConsoleKey.Enter)
{
pwd.AppendChar(key.KeyChar);
Console.Write(&quot;*&quot;);
}
else
{
if (key.Key == ConsoleKey.Backspace &amp;&amp; pwd.Length != 0)
{
pwd.RemoveAt(pwd.Length - 1);
Console.Write(&quot;\b \b&quot;);
}
}
}
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
return pwd;
}
var impPassword = GetPasswordAsSecureString($&quot;Enter the password for {impUser}&quot;);

I also recommend defining the following extension method in order to conveniently convert a SecureString to a normal string since one of the Win32 methods we need to use will only accept a normal string:

public static string ToUnSecureString(this SecureString securePassword)
{
if (securePassword == null)
{
return string.Empty;
}
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}

Before doing anything else having to do with impersonation, we need to call the Win32 method RegDisablePredefinedCache(). In terms of our purpose, this method tells Windows to dynamically determine where to look for the HKEY_CURRENT_USER registry hive, rather than using the cached location from when the process was initially invoked (Failing to call this method explains the "Access is denied" exception I was receiving previously. The impersonated user was attempting to load the HKCU hive for the invoker's account, which is obviously not allowed)

RegDisablePredefinedCache();

Next, we need to load that account's profile before entering the impersonated thread. This ensures that the impersonated account's registry hive is available in memory. We call the LogonUser() and LoadUserProfile() COM methods in order to accomplish that:

// Get a token for the user
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_PROVIDER_DEFAULT = 0;
//We&#39;ll use our extension method to pass the password as a normal string
LogonUser(ImpUser, ImpDomain, ImpPassword.ToUnSecureString(), LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, ref Token);
// Load user profile
ProfileInfo profileInfo = new ProfileInfo();
profileInfo.dwSize = Marshal.SizeOf(profileInfo);
profileInfo.lpUserName = ImpUser;
profileInfo.dwFlags = 1;
bool loadSuccess = LoadUserProfile(Token, ref profileInfo);
//Detect and handle failure gracefully
if (!loadSuccess)
{
errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($&quot;Failed to load profile for {ImpUser}. Error code: {errCode}&quot;, ex);
}
if (profileInfo.hProfile == IntPtr.Zero)
{
errCode = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(errCode);
throw new Exception($&quot;Failed accessing HKCU registry for {ImpUser}. Error code: {errCode}&quot;, ex);
}

Finally, thanks to one of the comments left on this question, I discovered a nifty nuget package called SimpleImpersonation. This obfuscates away most of the complexity involved with account impersonation:

//Note that UserCredentials() constructor I chose requires the  
//password to be passed as a SecureString object.
var Credentials = new UserCredentials(impDomain, impUser, impPassword);
Impersonation.RunAsUser(Credentials, LogonType.Batch, () =&gt;
{
//Within this bock, you can call methods such as
//Registry.CurrentUser.OpenSubKey()
//and they use the impersonated account&#39;s registry hive
}

huangapple
  • 本文由 发表于 2020年1月4日 01:12:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/59582608.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定