环境:Windows XP SP3,C#,.NET 4.0模拟和CurrentUser注册表访问
问题:
我试图访问在模拟类添加到一个模拟的用户注册表配置单元和我遇到基于被模拟的用户类型的问题(或者更准确地说,该限制似乎在模拟用户上)。
我本来以下an impersonation example from CodeProject这表明以LoadUserProfile()
呼叫发生后的模拟使用从LogonUser()
获得的原始凭证througha调用生成DuplcateToken()
重复令牌启动。我无法得到这个例子在我的环境中工作从一个管理员帐户冒充一个有限的用户(从示例中包含的屏幕截图中看起来好像它是在Windows Vista \ 7系统上完成的,没有提供关于涉及的账户类型)。
对LoadUserProfile()
的调用抛出了“访问被拒绝”的错误。查看userenv.log显示行“LoadUserProfile:未能启用恢复特权。error c0000022”。 MSDN上的LoadUserProfile文档显示,调用进程必须拥有SE_RESTORE_NAME和SE_BACKUP_NAME权限,默认情况下只有Administrators和Backup Operators组的成员具有权限。 (作为一个侧面说明,当我试图将这两个特权添加到用户组后,我仍然收到拒绝访问,但userenv.log显示“DropClientContext:Client [number]没有足够的权限。没有找到任何信息)
鉴于用户我模仿没有这些特权,我把电话转移到LoadUserProfile()
直到开始模拟,这次它没有问题的加载,我能够读取并在此测试中写入。考虑到我发现了我的答案,我创建了一个条件检查帐户的类型,以便在模拟之前调用LoadUserProfile()
,前提是当前用户是管理员的成员,或者如果成员不是管理员的成员,后面的例子我会依靠具有这些特权的模拟用户)。不幸的是我错了;我没有发现我的答案。当我使用反向角色(用户>管理员)测试呼叫时,对LoadUserProfile()
的呼叫仍然失败并显示Access Denied错误,并且userenv.log显示相同的“LoadUserProfile:未能启用恢复权限”错误c0000061“这次有不同的错误号码。
认为的特权可能不会被默认的令牌来启用返回从LogonUser()
和\或DuplicateToken()
我添加了两个呼叫AdjustTokenPrivilege()
在当前用户的令牌(以模拟后的地方)从WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token
获得。 TokenAccessLevels.AdjustPrivileges
和TokenAccessLevels.Query
被指定,因为在MSDN上AdjustTokenPrivilege的说明文件,他们需要在令牌被调整(我也尝试过使用从System.Diagnostics.Process.GetCurrentProcess().Handle
检索的手柄获得通过调用该令牌OpenProcessToken()
但未能从用户调用时内部和假冒的外侧GetCurrentProcess()
被抛出接入功能拒绝)
AdjustTokenPrivilege()
与WindowsIdentity...Token
使用,但仍然LoadUserProfile()
导致拒绝访问(恢复特权)时成功返回。 在这一点上,我不相信AdjustTokenPrivilege()
是做这件事的,所以我着手确定什么样的特权可用,以及他们在特定的令牌中处于什么样的状态,这导致了它自己的一小部分乐趣。学习了一些新东西后,我可以拨打GetTokenInformation()
并打印一份特权列表及其当前状态,但结果有些不确定,因为恢复和备份在呼叫AdjustTokenPrivilege()
之前和之后都显示属性为0,同时作为管理员和模拟管理员(奇怪的是,当调用AdjustTokenPrivilege()
时令牌上的其他三个特权从2变为1,但不是实际调整值保持在0的那个特权)
我删除了呼叫DuplicateToken()
并将其全部替换与LogonUser()
返回的令牌一起使用,以查看这将有助于测试令牌上的权限,LogonUser()
和DuplicateToken()
令牌是相同的。当我最初编写模拟课程时,我一直在使用WindowsImpersonationContext.Impersonate()
中的主令牌时没有任何问题,并且认为它值得一试。
在我以下提供的代码示例中,我能够以管理员身份运行时模拟和访问用户的注册表,但不能以其他方式运行。任何帮助将不胜感激。
岗前编辑:
我使用RegOpenCurrentUser()
API代替LoadUserProfile()
也试过,并与管理员>自和管理员>用户模仿成功,但无论从另一个管理员帐户或冒充管理员时用户RegOpenCurrentUser()
返回一个指向HKEY_USERS \ S-1-5-18(永远是这样)的指针,而不是实际的帐户配置单元。我猜,因为它不是实际加载这使我回到原点与需要使用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
我不知道我昨天做了什么,它正在工作,但今天RegOpenCurrentUser返回默认值,无论它从哪个帐户运行(除了自我模仿,因为配置单元已经加载),所以据我所知除非加载用户配置文件,否则RegOpenCurrentUser毫无价值。 – JoshHetland 2010-12-09 16:05:58