草庐IT

c# - 模拟和 CurrentUser 注册表访问

coder 2024-05-30 原文

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

问题:

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

我原来是在关注 an impersonation example from CodeProject其中显示了对 LoadUserProfile() 的调用使用通过调用 DuplcateToken() 生成的重复 token 开始模拟后发生来自 LogonUser() 获得的原始 token .我无法在我的环境中使用管理员帐户模拟受限用户的示例(从示例中包含的屏幕截图来看,它似乎是在 Windows Vista\7 系统上完成的,并且没有提供有关涉及的帐户类型)。

调用 LoadUserProfile()抛出“拒绝访问”的错误。查看 userenv.log 显示“LoadUserProfile: failed to enable the restore privilege.error c0000022”这一行。 MSDN 上的 LoadUserProfile 文档显示调用进程必须拥有 SE_RESTORE_NAME 和 SE_BACKUP_NAME 权限,默认情况下只有 Administrators 和 Backup Operators 组的成员拥有这些权限。 (作为旁注,当我稍后尝试将这两个权限添加到用户组时,我仍然收到拒绝访问,但 userenv.log 显示“DropClientContext:客户端 [编号] 没有足够的权限。错误 5”,我不能' t 找到任何信息)

鉴于我模拟的用户没有这些权限,我将调用移至 LoadUserProfile()在开始模拟之前,这次加载没有问题,我能够在这个测试中读写它。以为我已经找到了我的答案,我为帐户类型创建了一个条件检查,以便 LoadUserProfile()如果当前用户是管理员的成员,则在模拟之前调用,或者如果成员不是管理员的成员,则等到模拟之后调用(在后面的实例中,我将依赖具有这些权限的模拟用户)。不幸的是我错了;我还没有发现我的答案。当我测试角色颠倒(用户 > 管理员)的调用时,调用 LoadUserProfile()仍然再次失败,出现拒绝访问错误,并且 userenv.log 显示相同的“LoadUserProfile: failed to enable the restore privilege.error c0000061”,但这次有不同的错误号。

认为对LogonUser()返回的token默认可能没有开启权限和\或 DuplicateToken()我给 AdjustTokenPrivilege() 添加了两个电话在从 WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token 获得的当前用户 token (发生在模拟之后)上.TokenAccessLevels.AdjustPrivilegesTokenAccessLevels.Query之所以被指定,是因为 MSDN 上的 AdjustTokenPrivilege 文档指定正在调整的 token 上需要它们(我还尝试使用从 OpenProcessToken() 检索到的句柄通过调用 System.Diagnostics.Process.GetCurrentProcess().Handle 获取 token ,但是当从用户调用时失败了模拟的内部和外部,GetCurrentProcess() 是抛出访问被拒绝的函数)
AdjustTokenPrivilege()WindowsIdentity...Token 一起使用时成功返回但是 LoadUserProfile()仍然导致拒绝访问(恢复特权)。
在这一点上我不相信 AdjustTokenPrivilege()正在做它的工作,所以我开始使用 GetTokenInformation() 确定特定 token 的可用权限以及它们处于什么状态。这导致了它自己的一小袋乐趣。在学习了一些新东西后,我可以拨打 GetTokenInformation()并打印出权限列表及其当前状态,但结果有些不确定,因为在调用 AdjustTokenPrivilege() 之前和之后,还原和备份都显示属性为 0。作为管理员和冒充管理员(奇怪的是,当调用 AdjustTokenPrivilege() 时, token 上的其他三个权限从 2 变为 1,但实际上没有被调整,其值仍为 0)

我取消了对 DuplicateToken() 的调用并用从 LogonUser() 返回的 token 替换它正在使用的所有地方看看这是否有助于测试 token 的特权LogonUser()DuplicateToken() token 是相同的。当我最初编写模拟类时,我一直在调用 WindowsImpersonationContext.Impersonate() 时使用主要 token 。没有任何问题,并认为值得一试。

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

事前编辑:

我也试过使用 RegOpenCurrentUser() API 代替 LoadUserProfile()并成功使用管理员 > 自我和管理员 > 用户模拟,但是当从另一个管理员帐户或用户模拟管理员时 RegOpenCurrentUser()返回一个指向 HKEY_USERS\S-1-5-18(无论是什么)而不是实际帐户配置单元的指针。我猜是因为它实际上并没有加载,这让我回到了需要使用的地方 LoadUserProfile()
来自 RegOpenCurrentUser 文档 (MSDN):

RegOpenCurrentUser uses the thread's token to access the appropriate key, or the default if the profile is not loaded.



代码片段:
// 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 docs :

Starting with Windows XP Service Pack 2 (SP2) and Windows Server 2003, the caller must be an administrator or the LocalSystem account. It is not sufficient for the caller to merely impersonate the administrator or LocalSystem account.



如果您的流程以普通用户身份开始,那么您就不走运了。您可能会启动一个新进程(在管理员凭据下)来加载配置文件。

关于c# - 模拟和 CurrentUser 注册表访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4392221/

有关c# - 模拟和 CurrentUser 注册表访问的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  3. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  4. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  9. ruby - 有没有办法从 ruby​​ case 语句中访问表达式? - 2

    我想从then子句中访问c​​ase语句表达式,即food="cheese"casefoodwhen"dip"then"carrotsticks"when"cheese"then"#{expr}crackers"else"mayo"end在这种情况下,expr是食物的当前值(value)。在这种情况下,我知道,我可以简单地访问变量food,但是在某些情况下,该值可能无法再访问(array.shift等)。除了将expr移出到局部变量然后访问它之外,是否有直接访问caseexpr值的方法?罗亚附注我知道这个具体示例很简单,只是一个示例场景。 最佳答案

  10. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

随机推荐