将注册表项所有者设置为 SYSTEM 用户

Posted

技术标签:

【中文标题】将注册表项所有者设置为 SYSTEM 用户【英文标题】:Set Registry key owner to SYSTEM user 【发布时间】:2017-12-14 06:42:02 【问题描述】:

我必须向 Windows Defender 注册表项添加排除路径。我知道 Windows Defender 提供了一些 cmdlet,可以直接将它们用于这些目的。但不幸的是,在 Windows 7 和 PowerShell v2 中,它们不可用。所以我正在尝试构建一个脚本,该脚本将手动输入注册表项的值。通过在线研究,我整理了一个脚本,首先将所有者更改为管理员(因为只有 SYSTEM、WinDefend 和 TrustedInstaller 用户可以访问此密钥),然后添加值,最后将所有者设置为初始所有者(在这种情况下系统)再次。我的代码如下:

启用所需权限的代码:

Param([string]$targetPath)

function enable-privilege 
  Param(
    ## The privilege to adjust. This set is taken from
    ## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
    [ValidateSet(
      "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege",
      "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege",
      "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege",
      "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege",
      "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege",
      "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege",
      "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege",
      "SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege",
      "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege",
      "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege",
      "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
    $Privilege,
    ## The process on which to adjust the privilege. Defaults to the current process.
    $ProcessId = $pid,
    ## Switch to disable the privilege, rather than enable it.
    [Switch] $Disable
  )

  ## Taken from P/Invoke.NET with minor adjustments.
  $definition = @'
  using System;
  using System.Runtime.InteropServices;

  public class AdjPriv 
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
      ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
    [DllImport("advapi32.dll", SetLastError = true)]
    internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct TokPriv1Luid 
      public int Count;
      public long Luid;
      public int Attr;
    

    internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
    internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
    internal const int TOKEN_QUERY = 0x00000008;
    internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    public static bool EnablePrivilege(long processHandle, string privilege, bool disable) 
      bool retVal;
      TokPriv1Luid tp;
      IntPtr hproc = new IntPtr(processHandle);
      IntPtr htok = IntPtr.Zero;
      retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
      tp.Count = 1;
      tp.Luid = 0;
      if(disable) 
        tp.Attr = SE_PRIVILEGE_DISABLED;
       else 
        tp.Attr = SE_PRIVILEGE_ENABLED;
      
      retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
      retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
      return retVal;
    
  
  '@

  $processHandle = (Get-Process -id $ProcessId).Handle
  $type = Add-Type $definition -PassThru
  $type[0]::EnablePrivilege($processHandle, $Privilege, $Disable)

我执行更改的代码部分:

function getRegKeyOwner([string]$keyPath)
    $regRights=[System.Security.AccessControl.RegistryRights]::ReadPermissions
    $permCheck=[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree

    $Key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, $permCheck, $regRights)
    $acl = $Key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Owner)
    $owner = $acl.GetOwner([type]::GetType([System.Security.Principal.NTAccount]))
    $key.Close()
    return $owner


function setValueToKey([string]$keyPath, [string]$name, [System.Object]$value, [Microsoft.Win32.RegistryValueKind]$regValueKind)
    $regRights=[System.Security.AccessControl.RegistryRights]::SetValue
    $permCheck=[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree

    $Key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, $permCheck, $regRights)

    "Setting value with properties [name:$name, value:$value, value type:$regValueKind]"
    $Key.SetValue($name, $value, $regValueKind)
    $key.Close()


function changeRegKeyOwner([string]$keyPath, [System.Security.Principal.NTAccount]$user)
    try 
        $regRights=[System.Security.AccessControl.RegistryRights]::TakeOwnership
        $permCheck=[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree
        $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, $permCheck, $regRights)
        # You must get a blank acl for the key b/c you do not currently have access
        $acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None)

        if([string]::IsNullOrEmpty($user))
            $user = [System.Security.Principal.NTAccount]"$env:userdomain\$env:username"
        

        "Changing owner of Registry key: HKEY_LOCAL_MACHINE\$keyPath to `"$user`""
        $acl.SetOwner($user)
        $key.SetAccessControl($acl)
     catch 
        $_.Exception.toString()
        $key.Close()
        return
    
    giveFullControlToUser -userName "$user" -key $key

    $key.Close()


function giveFullControlToUser([String]$userName,[Microsoft.Win32.RegistryKey] $key)
    "giving full access to $userName for key $key"
    # After you have set owner you need to get the acl with the perms so you can modify it.
    $acl = $key.GetAccessControl()
    $rule = New-Object System.Security.AccessControl.RegistryAccessRule ($userName, "FullControl", @("ObjectInherit", "ContainerInherit"), "None", "Allow")
    $acl.SetAccessRule($rule)
    $key.SetAccessControl($acl)


function getAdminUser 
    $windowsKey = "SOFTWARE\Microsoft\Windows"
    return getRegKeyOwner -keyPath $windowsKey


enable-privilege SeTakeOwnershipPrivilege

$exclussionsPathsKey = "SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths"
$adminGroupName = gwmi win32_group -filter "LocalAccount = $TRUE And SID = 'S-1-5-32-544'" |
                  select -expand name

$originalOwner = getRegKeyOwner -keyPath $exclussionsPathsKey

"original Owner to the key `"$exclussionsPathsKey`" is: `"$originalOwner`""
changeRegKeyOwner -keyPath $exclussionsPathsKey -user ([System.Security.Principal.NTAccount]"$adminGroupName")

if (!([string]::IsNullOrEmpty($targetPath))) 
    $valueName = $targetPath
    $vaue = 0
    $regValueKind = [Microsoft.Win32.RegistryValueKind]::DWord
    setValueToKey -keyPath $exclussionsPathsKey -name $valueName -value $vaue -regValueKind $regValueKind


changeRegKeyOwner -keyPath $exclussionsPathsKey -user $originalOwner

但在设置值之前,一切正常。我可以看到注册表项中的值,所有者更改为具有完全权限的“管理员”。只有当我再次尝试设置原始所有者“SYSTEM”时,才会出现以下异常。这是我第一次使用 PowerShell 编写脚本。而且我完全无法理解/解决问题。顺便说一句,如果原始用户是“SYSTEM”以外的任何其他用户,它就可以工作。也许我在这里缺少一些必需的特权。

输出:

真的 键“SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths”的原始所有者是:“NT AUTHORITY\SYSTEM” 更改注册表项的所有者:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths to "Administrators" 授予管理员对密钥 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths 的完全访问权限 更改注册表项的所有者:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths to "NT AUTHORITY\SYSTEM" System.Management.Automation.MethodInvocationException:使用“1”参数调用“SetAccessControl”的异常:“安全标识符不允许成为此对象的所有者。” ---> System.InvalidOperationException:安全标识符不允许成为该对象的所有者。 在 System.Security.AccessControl.NativeObjectSecurity.Persist(字符串名称,SafeHandle 句柄,AccessControlSections includeSections,对象异常上下文) 在 System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle 句柄,AccessControlSections includeSections,对象异常上下文) 在 System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle 句柄,AccessControlSections includeSections) 在 System.Security.AccessControl.RegistrySecurity.Persist(SafeRegistryHandle hKey,字符串 keyName) 在 Microsoft.Win32.RegistryKey.SetAccessControl(RegistrySecurity registrySecurity) 在 SetAccessControl(对象,对象 []) 在 System.Management.Automation.MethodInformation.Invoke(对象目标,对象 [] 参数) 在 System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(对象目标,对象 [] 参数,MethodInformation 方法信息,对象 [] originalArguments) --- 内部异常堆栈跟踪结束 --- 在 System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(对象目标,对象 [] 参数,MethodInformation 方法信息,对象 [] originalArguments) 在 System.Management.Automation.ParserOps.CallMethod(令牌令牌,对象目标,字符串方法名,对象 [] paramArray,布尔 callStatic,对象 valueToSet) 在 System.Management.Automation.MethodCallNode.InvokeMethod(对象目标,对象 [] 参数,对象值) 在 System.Management.Automation.MethodCallNode.Execute(数组输入,管道 outputPipe,ExecutionContext 上下文) 在 System.Management.Automation.ParseTreeNode.Execute(数组输入,管道 outputPipe,ArrayList& resultList,ExecutionContext 上下文) 在 System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode 语句、数组输入、管道输出管道、ArrayList& resultList、ExecutionContext 上下文)

【问题讨论】:

“Windows 7 和 PowerShell v1” 是什么意思? Windows 7 附带 PowerShell v2,并且至少可以运行 PowerShell v4。但即使只使用 PowerShell v2,您也应该能够在注册表路径上使用 Get-AclSet-Acl @AnsgarWiechers 对不起,我的错!这是 Powershell v2。是的,Get-AclSet-Acl 可用。但问题不在于那里。问题是,当我将注册表项的所有者从 SYSTEM 更改为 Administrators 时,它工作正常。但后来我尝试将其设置回 SYSTEM,它抛出了我提供的异常:System.InvalidOperationException:安全标识符不允许成为此对象的所有者。这很有趣,因为 SYSTEM 以前是所有者。 【参考方案1】:

最后,我找到了脚本中缺少的内容。管理员用户需要额外的权限才能恢复权限。只需在设置原始所有者之前简单地调用函数enable-privilege SeRestorePrivilege,即可为当前进程提供所需的权限。

【讨论】:

以上是关于将注册表项所有者设置为 SYSTEM 用户的主要内容,如果未能解决你的问题,请参考以下文章

注册表项无法删除,怎么办?

计算机中所有用户的应用程序的 Windows 注册表项

远程注册表项提取程序PowerShell脚本

Wix 工具集 - 将属性设置为用户配置文件文件夹路径和程序文件

VB6读取注册表项下的所有值

无法在Windows 7下删除/修改注册表项:权限不足