環境: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