Powershell:将Registrykey所有者设置为SYSTEM用户

我必须将排除path添加到Windows Defenderregistry项。 我知道有从Windows Defender提供的Cmdlet直接将它们用于这些目的。 但不幸的是,在Windows 7和Powershell v2中,它们不可用。 所以我正在尝试构build一个手动input值到registry项的脚本。 通过在线研究,我已经编写了一个脚本,首先将所有者更改为pipe理员,然后添加该值,最后再将所有者设置为最初的所有者。 我的代码如下:

代码来启用所需的特权:

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 

但直到价值设定部分,一切工作正常。 只有当我尝试再次设置原始的所有者时,我会得到以下例外。 这是我第一次使用Powershell脚本。 而我完全无法理解/解决问题。

  True original Owner to the key "SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" is: "NT AUTHORITY\SYSTEM" Changing owner of Registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths to "Administrators" giving full access to Administrators for key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths Changing owner of Registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths to "NT AUTHORITY\SYSTEM" System.Management.Automation.MethodInvocationException: Exception calling "SetAccessControl" with "1" argument(s): "The security identifier is not allowed to be the owner of this object." ---> System.InvalidOperationException: The security identifier is not allowed to be the owner of this object. at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContex t) at System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle handle, AccessControlSections includeSections, Object exceptionContext) at System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle handle, AccessControlSections includeSections) at System.Security.AccessControl.RegistrySecurity.Persist(SafeRegistryHandle hKey, String keyName) at Microsoft.Win32.RegistryKey.SetAccessControl(RegistrySecurity registrySecurity) at SetAccessControl(Object , Object[] ) at System.Management.Automation.MethodInformation.Invoke(Object target, Object[] arguments) at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] origin alArguments) --- End of inner exception stack trace --- at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] origin alArguments) at System.Management.Automation.ParserOps.CallMethod(Token token, Object target, String methodName, Object[] paramArray, Boolean callStatic, Object valueTo Set) at System.Management.Automation.MethodCallNode.InvokeMethod(Object target, Object[] arguments, Object value) at System.Management.Automation.MethodCallNode.Execute(Array input, Pipe outputPipe, ExecutionContext context) at System.Management.Automation.ParseTreeNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context) at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionC ontext context) 

最后,我发现脚本中缺less了什么。 pipe理员用户需要额外的权限来恢复权限。 只需简单地调用函数enable-privilege SeRestorePrivilege然后再设置原始所有者,即可为当前进程提供所需的权限。