模拟和CurrentUser注册表访问

Jos*_*and 11 c# registry impersonation

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

问题:

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

我最初是在跟踪CodeProject中的模拟示例,示例显示LoadUserProfile()在模拟开始后使用DuplcateToken()通过从原始令牌调用生成的重复令牌发生的调用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()我返回的令牌上启用权限,而是我从AdjustTokenPrivilege()当前用户令牌(在模拟后发生)中添加了两个调用WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token. TokenAccessLevels.AdjustPrivileges并且TokenAccessLevels.Query被指定是因为MSDN上的AdjustTokenPrivilege文档指定在正在调整的令牌上需要它们(我还尝试通过调用OpenProcessToken()使用从中检索到的句柄来获取令牌System.Diagnostics.Process.GetCurrentProcess().Handle但在模拟内部和外部从用户调用时失败与GetCurrentProcess()被抛出接入功能拒绝)

AdjustTokenPrivilege()与之一起使用时返回成功WindowsIdentity...TokenLoadUserProfile()仍导致拒绝访问(还原权限).在这一点上,我不相信这AdjustTokenPrivilege()是在做它的工作,所以我开始确定可用的特权和他们所处的特定令牌的状态,GetTokenInformation()这导致了它自己的一小部分乐趣.在学习了一些新东西后,我能够调用GetTokenInformation()并打印出一系列权限及其当前状态,但结果有些不确定,因为恢复和备份在调用AdjustTokenPrivilege()管理员和模拟管理员之前和之后都显示了0的属性(奇怪的是,三个其他权限在调用时在令牌上从2变为1AdjustTokenPrivilege() 但不是那些实际被调整的值保持在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
Run Code Online (Sandbox Code Playgroud)

arx*_*arx 7

LoadUserProfile文档:

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

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