65.9K
CodeProject 正在变化。 阅读更多。
Home

C#.NET 中的完整模拟演示

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (10投票s)

2010年11月5日

CPOL

2分钟阅读

viewsIcon

121722

downloadIcon

3584

这是一个 C# 中的完整模拟演示,模拟一个用户并访问其文件和 HKCU 注册表项。

引言

模拟是指线程使用与拥有该线程的进程不同的安全信息来执行的能力。通常,服务器应用程序中的线程会模拟客户端。这允许服务器线程代表该客户端执行操作以访问服务器上的对象或验证对客户端自己的对象的访问权限。

背景

在某些情况下,我们需要模拟另一个 Windows 帐户,并在该用户的会话下进行一些工作,例如

  • 企业 ASP.NET Web 应用程序提供服务器管理员访问服务器的权限,具有某些特定权限;服务器管理员在页面上输入他们的 NT 帐户信息(域\帐户 + 密码),我们需要获取 WinNT 访问令牌,然后模拟该服务器用户,以便我们获得其特定权限并执行只有该帐户才能做的事情。
  • 我们开发了一个 Windows 服务,它需要定期访问 Internet,但如果特定用户设置了 Sock5 代理来访问 Internet,那么您的 Windows 服务需要知道 Sock5 代理信息以便它可以访问 Internet,并且您必须模拟此用户并读取设置。

功能

我创建了一个本地用户 TempUser,它属于“Administrators”(确保至少使用 TempUser 登录一次)。我使用我自己的帐户登录,并模拟 TempUser 并做两件事

  1. 创建文件夹 C:\TempFolder,修改其默认权限;只有 TempUser 拥有它的完全控制权。我将在模拟后在此文件夹下创建一个文本文件以证明模拟成功

    Tempfolder1

    注意:将 TempUser 设置为唯一所有者后,我的当前帐户无法访问此文件夹,除非进行权限提升(我禁用 UAC;如果启用 UAC,则会弹出一个提示窗口并要求管理员确认)。

    Tempfolder2

    此外,我尝试在我的帐户下以编程方式访问此文件夹,并且抛出了一个 UnauthorizedAccessException 异常!

    Unauthorized

  2. 然后,我访问 TempUserHKEY_CURRENT_USER 并进行注册表项创建、读取和删除

代码片段

我们需要调用三个非常流行的 Win32 API:LogonUserDuplicateTokenRevertToSelf

[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);

///
/// A process should call the RevertToSelf function after finishing
/// any impersonation begun by using the DdeImpersonateClient,
/// ImpersonateDdeClientWindow, ImpersonateLoggedOnUser,
/// ImpersonateNamedPipeClient, ImpersonateSelf,
/// ImpersonateAnonymousToken or SetThreadToken function.
/// If RevertToSelf fails, your application continues to run in the context
/// of the client, which is not appropriate.
/// You should shut down the process if RevertToSelf fails.
/// RevertToSelf Function:
/// http://msdn.microsoft.com/en-us/library/aa379317(VS.85).aspx
///
/// A boolean value indicates the function succeeded or not.
[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);

一个重要的提示:为了访问 HKCU,我们需要调用另一个 Win32 API,LoadUserProfile以获取 TempUser 下 HKCU 的句柄。下面的代码,如突出显示的第 45 和 49 行所示,在调用 LoadUserProfile 之后,hProfile 将被设置为 HKCU 的句柄

[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
    ///
    /// Specifies the size of the structure, in bytes.
    ///
    public int dwSize;

    ///
    /// This member can be one of the following flags: PI_NOUI or PI_APPLYPOLICY
    ///
    public int dwFlags;

    ///
    /// Pointer to the name of the user.
    /// This member is used as the base name
    /// of the directory in which to store a new profile.
    ///
    public string lpUserName;

    ///
    /// Pointer to the roaming user profile path.
    /// If the user does not have a roaming profile, this member can be NULL.
    ///
    public string lpProfilePath;

    ///
    /// Pointer to the default user profile path. This member can be NULL.
    ///
    public string lpDefaultPath;

    ///
    /// Pointer to the name of the validating domain controller, in NetBIOS format.
    /// If this member is NULL, the Windows NT 4.0-style policy will not be applied.
    ///
    public string lpServerName;

    ///
    /// Pointer to the path of the Windows NT 4.0-style policy file. This member can be NULL.
    ///
    public string lpPolicyPath;

    ///
    /// Handle to the HKEY_CURRENT_USER registry key.
    ///
    public IntPtr hProfile;
}

    [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);

执行模拟的代码:

WindowsIdentity m_ImpersonatedUser;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
const int SecurityImpersonation = 2;
const int TokenType = 1;

try
{
    if (RevertToSelf())
    {
        Console.WriteLine("Before impersonation: " +
                          WindowsIdentity.GetCurrent().Name);

        String userName = "TempUser";
        IntPtr password = GetPassword();

        if (LogonUser(userName, Environment.MachineName, 
                      "!@#$QWERasdf", LOGON32_LOGON_INTERACTIVE,
                      LOGON32_PROVIDER_DEFAULT, ref token) != 0)
        {
            if (DuplicateToken(token, SecurityImpersonation, ref tokenDuplicate) != 0)
            {
                m_ImpersonatedUser = new WindowsIdentity(tokenDuplicate);
                using (m_ImpersonationContext = m_ImpersonatedUser.Impersonate())
                {
                    if (m_ImpersonationContext != null)
                    {
                        Console.WriteLine("After Impersonation succeeded: " + 
                            Environment.NewLine +
                            "User Name: " +
                            WindowsIdentity.GetCurrent(
                               TokenAccessLevels.MaximumAllowed).Name +
                            Environment.NewLine +
                            "SID: " +
                            WindowsIdentity.GetCurrent(
                               TokenAccessLevels.MaximumAllowed).User.Value);

                        #region LoadUserProfile
                        // Load user profile
                        ProfileInfo profileInfo = new ProfileInfo();
                        profileInfo.dwSize = Marshal.SizeOf(profileInfo);
                        profileInfo.lpUserName = userName;
                        profileInfo.dwFlags = 1;
                        Boolean loadSuccess = 
                                LoadUserProfile(tokenDuplicate, ref profileInfo);

                        if (!loadSuccess)
                        {
                            Console.WriteLine("LoadUserProfile() failed with error code: " +
                                              Marshal.GetLastWin32Error());
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }

                        if (profileInfo.hProfile == IntPtr.Zero)
                        {
                            Console.WriteLine(
                                "LoadUserProfile() failed - HKCU handle " + 
                                "was not loaded. Error code: " +
                                Marshal.GetLastWin32Error());
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                        #endregion

                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);

                        // Do tasks after impersonating successfully
                        AccessFileSystem();

                        // Access HKCU after loading user's profile
                        AccessHkcuRegistry(profileInfo.hProfile);

                        // Unload user profile
                        // MSDN remarks
                        // http://msdn.microsoft.com/en-us/library/bb762282(VS.85).aspx
                        // Before calling UnloadUserProfile you should
                        // ensure that all handles to keys that you have opened in the
                        // user's registry hive are closed. If you do not
                        // close all open registry handles, the user's profile fails
                        // to unload. For more information, see Registry Key
                        // Security and Access Rights and Registry Hives.
                        UnloadUserProfile(tokenDuplicate, profileInfo.hProfile);

                        // Undo impersonation
                        m_ImpersonationContext.Undo();
                    }
                }
            }
            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);
}

AccessFileSystem 方法

private static void AccessFileSystem()
{
    // Access file system %appdata% will be "C:\Users\TempUser\appdata\Roaming"
    String appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
    File.AppendAllText("C:\\TempFolder\\Temp.txt", "some text...");
}

AccessHkcuRegistry 方法:

private static void AccessHkcuRegistry(IntPtr hkcuHandle)
{
    // Access registry HKCU
    using (SafeRegistryHandle safeHandle = 
              new SafeRegistryHandle(hkcuHandle, true))
    {
        using (RegistryKey tempUserHKCU = RegistryKey.FromHandle(safeHandle))
        {
            // Unum all sub keys under tempuser's HKCU
            String[] keys = tempUserHKCU.GetSubKeyNames();

            // Create a new sub key under tempuser's HKCU
            using (RegistryKey tempKeyByWayne = 
                   tempUserHKCU.CreateSubKey("TempKeyByWayne"))
            {
                // Ensure priviledge
                //RegistrySecurity registrySecurity = new RegistrySecurity();
                //RegistryAccessRule accessRule = 
                //   new RegistryAccessRule(Environment.MachineName + "\\" + userName,
                //       RegistryRights.TakeOwnership,
                //       InheritanceFlags.ContainerInherit,
                //       PropagationFlags.None,
                //       AccessControlType.Allow);
                //registrySecurity.SetAccessRule(accessRule);
                //tempKeyByWayne.SetAccessControl(registrySecurity);

                // Create a new String value under created TempKeyByWayne subkey
                tempKeyByWayne.SetValue("StrType", 
                     "TempContent", RegistryValueKind.String);

                // Read the value
                using (RegistryKey regKey = 
                         tempUserHKCU.OpenSubKey("TempKeyByWayne"))
                {
                    String valueContent = regKey.GetValue("StrType") as String;
                    Console.WriteLine(valueContent);
                }

                // Delete created TempKeyByWayne subkey
                tempUserHKCU.DeleteSubKey("TempKeyByWayne");
                tempKeyByWayne.Close();
            }
        }
    }
}

模拟结果和验证

Temp.txt 创建

TempFolder

"TempKeyByWayne" 在 HKCU 下创建

Registry

参考文献

编码愉快 :)

© . All rights reserved.