将注册表项所有者设置为 SYSTEM 用户
Set Registry key owner to SYSTEM user
我必须向 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
但在值设置部分之前,一切正常。我可以看到注册表项中的值,所有者已更改为 "Administrators" 并具有完全权限。只有当我再次尝试设置原始所有者 "SYSTEM" 时,才会出现以下异常。这是我第一次使用 PowerShell 编写脚本。而我完全无法understand/solve这个问题。顺便说一句,如果原始用户是 "SYSTEM" 以外的任何其他用户,它就可以工作。也许我在这里缺少一些必需的特权。
输出:
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 exceptionContext)
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[] originalArguments)
--- End of inner exception stack trace ---
at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] originalArguments)
at System.Management.Automation.ParserOps.CallMethod(Token token, Object target, String methodName, Object[] paramArray, Boolean callStatic, Object valueToSet)
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, ExecutionContext context)
终于,我找到了脚本中缺少的内容。管理员用户需要额外的权限才能恢复权限。只需简单调用 enable-privilege SeRestorePrivilege
函数,然后再设置原所有者,即可为当前进程提供所需的权限。
我必须向 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
但在值设置部分之前,一切正常。我可以看到注册表项中的值,所有者已更改为 "Administrators" 并具有完全权限。只有当我再次尝试设置原始所有者 "SYSTEM" 时,才会出现以下异常。这是我第一次使用 PowerShell 编写脚本。而我完全无法understand/solve这个问题。顺便说一句,如果原始用户是 "SYSTEM" 以外的任何其他用户,它就可以工作。也许我在这里缺少一些必需的特权。
输出:
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 exceptionContext) 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[] originalArguments) --- End of inner exception stack trace --- at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] originalArguments) at System.Management.Automation.ParserOps.CallMethod(Token token, Object target, String methodName, Object[] paramArray, Boolean callStatic, Object valueToSet) 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, ExecutionContext context)
终于,我找到了脚本中缺少的内容。管理员用户需要额外的权限才能恢复权限。只需简单调用 enable-privilege SeRestorePrivilege
函数,然后再设置原所有者,即可为当前进程提供所需的权限。