模拟和CurrentUser注册表访问

环境:Windows XP SP3,C#,. Net 4.0

问题:

我试图在模拟类中添加对模拟用户注册表配置单元的访问权限,并且我根据被模拟的用户类型遇到问题(或者更准确地说,限制似乎是模仿用户)。

我最初遵循CodeProject的模拟示例,该示例显示在使用通过从LogonUser()获取的原始令牌调用DuplcateToken()生成的重复令牌开始模拟后调用LoadUserProfile() LogonUser() 。 我无法让这个示例在我的环境中使用管理员帐户冒充有限的用户(从示例中包含的屏幕截图看来,它似乎是在Windows Vista \ 7系统上完成的,并且没有提供有关涉及的帐户类型)。

LoadUserProfile()的调用抛出了“拒绝访问”的错误。 查看userenv.log显示“LoadUserProfile:无法启用还原权限。”错误c0000022“。 MSDN上的LoadUserProfile文档显示调用进程必须拥有SE_RESTORE_NAME和SE_BACKUP_NAME权限,默认情况下只有Administrators和Backup Operators组的成员具有该权限。 (作为旁注,当我尝试稍后将这两个权限添加到Users组时,我仍然收到拒绝访问但是userenv.log显示“DropClientContext:Client [number]没有足够的权限。错误5”我无法’找到任何信息)

鉴于我冒充的用户没有这些权限,我将调用LoadUserProfile()调用到开始模拟之前,这次加载没有问题,我能够在此测试中读取和写入。 考虑到我发现了我的答案,我创建了帐户类型的条件检查,以便在模拟之前调用LoadUserProfile() ,如果当前用户是管理员的成员,或者等到模仿后如果该成员不是管理员的成员(在后面的例子中,我将依赖具有这些特权的模拟用户)。 不幸的是我错了; 我没有发现我的答案。 当我使用反转角色测试呼叫时(用户>管理员)对LoadUserProfile() )的调用仍然失败,并且Access Denied错误和userenv.log显示相同的“LoadUserProfile:无法启用恢复权限。错误c0000061 ”但是这次是一个不同的错误号。

认为默认情况下可能无法在LogonUser()和\或DuplicateToken()返回的令牌上启用权限,我在从WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token )获取的当前用户令牌(在模拟后发生AdjustTokenPrivilege()上添加了两次AdjustTokenPrivilege()调用。 WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token 。 指定了TokenAccessLevels.AdjustPrivilegesTokenAccessLevels.Query ,因为MSDN上的AdjustTokenPrivilege文档指定在调整的令牌上需要它们(我还尝试使用从System.Diagnostics.Process.GetCurrentProcess().Handle检索的句柄通过调用OpenProcessToken()获取令牌。 System.Diagnostics.Process.GetCurrentProcess().Handle但是当用户在模拟内部和外部调用时失败, GetCurrentProcess()是拒绝访问的函数

WindowsIdentity...Token一起使用时, AdjustTokenPrivilege()成功返回WindowsIdentity...TokenLoadUserProfile()仍然导致拒绝访问(还原权限)。 在这一点上,我不相信AdjustTokenPrivilege()正在做它的工作,所以我开始确定哪些特权是可用的,以及他们使用GetTokenInformation()获得特定令牌的状态,这导致了它自己的一小部分乐趣。 在学习了一些新东西后,我能够调用GetTokenInformation()并打印出一系列权限及其当前状态,但结果有些不确定,因为Restore和Backup在调用AdjustTokenPrivilege()之前和之后都显示了0的属性作为管理员和冒充管理员(奇怪的是,当调用AdjustTokenPrivilege()时,令牌上的其他三个权限从2更改为1,而不是实际调整的值保持为0的其他权限

我删除了对DuplicateToken()的调用,并将其使用的所有位置替换为LogonUser()返回的令牌,以查看这是否有助于测试令牌上的权限LogonUser()DuplicateToken()令牌是相同。 当我最初编写模拟类时,我在调用WindowsImpersonationContext.Impersonate()一直使用主令牌而没有任何问题,并认为值得一试。

在下面提供的代码示例中,我可以在以管理员身份运行时模拟和访问用户的注册表,但不是相反。 任何帮助将不胜感激。

post前编辑:

我也尝试使用RegOpenCurrentUser() API代替LoadUserProfile() ,并成功使用管理员>自我和管理员>用户模拟,但是当从其他管理员帐户或用户RegOpenCurrentUser()模拟管理员时,返回指向HKEY_USERS的指针\ S-1-5-18(不管是什么)而不是实际的帐户蜂巢。 我猜是因为它实际上没有加载,这让我回到LoadUserProfile() ,需要使用LoadUserProfile()

从RegOpenCurrentUser文档(MSDN):

RegOpenCurrentUser使用线程的标记来访问相应的密钥,如果未加载配置文件,则使用默认密钥。

代码片段:

 // Private variables used by class private IntPtr tokenHandle; private PROFILEINFO pInfo; private WindowsImpersonationContext thisUser; private string sDomain = string.Empty; private string sUsername = string.Empty; private string sPassword = string.Empty; private bool bDisposed = false; private RegistryKey rCurrentUser = null; private SafeRegistryHandle safeHandle = null; //Constants used for privilege adjustment private const string SE_RESTORE_NAME = "SeRestorePrivilege"; private const string SE_BACKUP_NAME = "SeBackupPrivilege"; private const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001; private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002; private const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004; private const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000; [StructLayout(LayoutKind.Sequential)] protected struct PROFILEINFO { public int dwSize; public int dwFlags; [MarshalAs(UnmanagedType.LPTStr)] public String lpUserName; [MarshalAs(UnmanagedType.LPTStr)] public String lpProfilePath; [MarshalAs(UnmanagedType.LPTStr)] public String lpDefaultPath; [MarshalAs(UnmanagedType.LPTStr)] public String lpServerName; [MarshalAs(UnmanagedType.LPTStr)] public String lpPolicyPath; public IntPtr hProfile; } protected struct TOKEN_PRIVILEGES { public UInt32 PrivilegeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public LUID_AND_ATTRIBUTES[] Privileges; } [StructLayout(LayoutKind.Sequential)] protected struct LUID_AND_ATTRIBUTES { public LUID Luid; public UInt32 Attributes; } [StructLayout(LayoutKind.Sequential)] protected struct LUID { public uint LowPart; public int HighPart; } // Private API calls used by class [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] protected static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)] protected static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo); [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)] protected static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile); [DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)] protected static extern bool CloseHandle(IntPtr hObject); [DllImport("advapi32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)] protected static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 Zero, IntPtr Null1, IntPtr Null2); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)][return: MarshalAs(UnmanagedType.Bool)] protected static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid); [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] public void Start() { tokenHandle = IntPtr.Zero; // set the pointer to nothing if (!LogonUser(sUsername, sDomain, sPassword, 2, 0, ref tokenHandle)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // end if !LogonUser returned false try { //All of this is for loading the registry and is not required for impersonation to start LUID LuidRestore = new LUID(); LUID LuidBackup = new LUID(); if(LookupPrivilegeValue(null, SE_RESTORE_NAME, ref LuidRestore) && LookupPrivilegeValue(null, SE_BACKUP_NAME, ref LuidBackup)) { //Create the TokenPrivileges array to pass to AdjustTokenPrivileges LUID_AND_ATTRIBUTES[] LuidAndAttributes = new LUID_AND_ATTRIBUTES[2]; LuidAndAttributes[0].Luid = LuidRestore; LuidAndAttributes[0].Attributes = SE_PRIVILEGE_ENABLED; LuidAndAttributes[1].Luid = LuidBackup; LuidAndAttributes[1].Attributes = SE_PRIVILEGE_ENABLED; TOKEN_PRIVILEGES TokenPrivileges = new TOKEN_PRIVILEGES(); TokenPrivileges.PrivilegeCount = 2; TokenPrivileges.Privileges = LuidAndAttributes; IntPtr procHandle = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token; if(AdjustTokenPrivileges(procHandle, false, ref TokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero)) { pInfo = new PROFILEINFO(); pInfo.dwSize = Marshal.SizeOf(pInfo); pInfo.lpUserName = sUsername; pInfo.dwFlags = 1; LoadUserProfile(tokenHandle, ref pInfo); //this is not required to take place if(pInfo.hProfile != IntPtr.Zero) { safeHandle = new SafeRegistryHandle(pInfo.hProfile, true); rCurrentUser = RegistryKey.FromHandle(safeHandle); }//end if pInfo.hProfile }//end if AdjustTokenPrivileges }//end if LookupPrivilegeValue 1 & 2 }catch{ //We don't really care that this didn't work but we don't want to throw any errors at this point as it would stop impersonation }//end try WindowsIdentity thisId = new WindowsIdentity(tokenHandle); thisUser = thisId.Impersonate(); } // end function Start 

从LoadUserProfile文档 :

从Windows XP Service Pack 2(SP2)和Windows Server 2003开始,调用者必须是管理员或LocalSystem帐户。 调用者仅仅模仿管理员或LocalSystem帐户是不够的。

如果您的流程以普通用户身份启动,那么您运气不佳。 您可以启动新进程(在管理员凭据下)以加载配置文件。

我发现在LogonUser()调用中设置的登录类型可能是一个因素。 即使以管理员身份运行,我也无法通过错误,除非我从LOGON32_LOGON_INTERACTIVE切换到LOGON32_LOGON_BATCH 。 您需要确保“作为批处理作业登录”组策略不会干扰。