我正在尝试编写一个脚本,该脚本将遍历每个注册表项并检查其权限。我创建了一个满足所有要求的测试注册表项,并且应该将其打印到控制台,但是每当我运行此脚本时,我总是会得到很多空白行。我相信这是我尝试遍历注册表项及其返回格式的方式。
$ErrorActionPreference = "SilentlyContinue"
Get-ChildItem -LiteralPath "HKLM:\" -Recurse | % {
$output = Get-Acl $_.Name | format-list | Out-String
$output = $output -split "`r`n"
ForEach ($line in $output) {
If ($line -contains " BUILTIN\Users Allow FullControl") {
echo $_.Name
}
}
}
下面的脚本回显代码的“成功”部分:
$output = Get-Acl "HKLM:\SOFTWARE\testkey" | format-list | Out-String
$output = $output -split "`r`n"
ForEach ($line in $output) {
If ($line -contains " BUILTIN\Users Allow FullControl") }
echo "success"
}
}
我希望第一个脚本将遍历所有HKLM密钥并返回“ testkey”,但它将向控制台回显大量空行。
答案 0 :(得分:2)
我的代码仍然存在很多问题,但是首先我要弄清楚为什么您会看到空白行。
当您使用$output = Get-Acl $_.Name
时,请花一些时间来了解$_
的含义。这是ForEach-Item
正在处理的“当前项”(您使用的别名是%
),在这种情况下,这是从Get-ChildItem
返回的注册表项。
此对象包含您要引用的属性.Name
,但是如果查看该属性值,则会看到它是一个注册表项名称,因为regedit倾向于引用它,即{ {1}},这不是在PowerShell中调用它的方式。
然后,当您致电HKEY_LOCAL_MACHINE\Key
时,Get-Acl $_.Name
并不知道您要寻找什么。注意在“成功”示例中,如何使用Get-Acl
给出了注册表项的完全限定路径;这是一个PSDrive,它引用PSProvider来类似于文件系统浏览注册表,并且其全名是HKLM:\
,从而使条目的完整路径为:
Microsoft.PowerShell.Core\Registry
。 uck!
顺便说一下,您没有看到此错误的原因是因为您将Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Key
设置为$ErrorActionPreference
,所以错误被忽略并且没有发送到屏幕。通常,您不应设置该设置。
不过,与其尝试解析和/或连接字符串,不如查看注册表项对象的全部属性,并查看是否可以使用某些东西:
SilentlyContinue
就像哈!有一个名为Get-ChildItem HKLM:\ -Recurse | Select -First 1 | Format-List *
的属性,看起来像具有完全限定的路径名!
PSPath
成功! (有关详情,请参见下文)
所以现在我要绕道而行,谈谈其他一些事情。我知道,这很漫长,但是请忍受我。
首先,是您使用Get-ChildItem HKLM:\ -Recurse | Select -First 1 | % { Get-Acl $_.PSPath }
。开始使用PowerShell时,通常会查看命令的 output 并查看其 format ,这与您使用其他shell一样,很常见,以便弄清楚如何解析或处理或以其他方式处理给定的信息。
这几乎总是一个错误。 PowerShell的主要功能之一是大量使用在命令之间传递的完整对象。这意味着具有属性和方法的完整.Net类。
认识到您在屏幕上看到的只是底层对象的格式化渲染非常重要。它可能不会显示所有内容,它可能会被截断,最重要的是,它不需要解析为文本。
回到您的用法,使用任何| Format-List | Out-String
命令时都要记住这一点:它旨在更改屏幕上对象的显示格式 。它不可逆地丢失原始对象的数据,并且实际上只能用于显示目的,通常是用户在控制台上而不是脚本中。
如果查看Format-
的输出,可能会认为将其解析为文本最容易,但是请记住,它是一个对象。相反,请看一下这种方法。您可以从任何目录中的PowerShell在PowerShell中运行它:
Get-Acl
(注意与以前的区别,我将其保留在管道中,使用$acl = Get-ChildItem HKLM:\ -Recurse | Select-Object -First 1 -ExpandProperty PSPath | Get-Acl
仅给我一个属性,然后将其通过管道直接传递到Select-Object
中,而无需使用{{1}进行迭代})
现在看Get-Acl
。您可能会以表格格式看到它,将其截断,因此可以使用ForEach-Object
再次查看它,并查看它包含的更多属性。使用此输出指导编码。您的脚本正在“访问”列表中寻找条目,因此请尝试以下操作:
$acl
现在,您会看到一个新对象列表,每个对象都有自己的属性。这些是访问规则,并且包含与您需要的属性相对应的各个属性。它是它们的数组,因此您可以使用名为$acl | Format-List
的命令对它们进行过滤(您可能已经看到了其别名$acl.Access
或Where-Object
),但首先要检查这些对象:< / p>
Where
看起来?
是它自己的类型$acl.Access[1].IdentityReference
# ^ looks good for getting the identity you want to compare against
$acl.Access[1].RegistryRights
# ^ this one's tricky, looks like a string, but it's not
# check its type and other members
$acl.Access[1].RegistryRights | Get-Member
的对象,但是它似乎没有有用的属性。您可能还会注意到此属性的一些值看起来像逗号分隔的字符串。
这是一个枚举(枚举),基本上只是一堆带有名称的数字值,因此它们在代码中更有意义。这种特殊类型是位域,可以一次设置多个值。
在控制台中查看类型的快速提示,对枚举非常有用:类型名称(类)在PowerShell中用方括号.RegistryRights
表示,例如,System.Security.AccessControl.RegistryRights
或完全限定[]
。您可以使用[string]
而不是[System.String]
访问类型本身的成员。您可以轻松地制表完整的类型,因此开始键入:::
然后按 TAB ,现在键入.
并再次按 TAB 。您可以开始循环浏览这些值。
由于该类型可以有多个值,因此可以使用由[RegistryRigh
枚举提供的便捷方法(您可能在::
的输出中看到)来确定它是否包含特定值。值。
HasFlag
我要掩饰一下,但是Get-Member
是一回事:一个枚举。
现在,您可以将其与身份一起找到所需的内容:
$acl.Access[1].RegistryRights.HasFlag([System.Security.AccessControl.RegistryRights]::FullControl)
.Access.AccessControlType
现在包含与您的条件相匹配的每个条目(应为1或0个条目,但大于1则无关紧要)。
我们将其与原始代码放在一起:
$UsersFullControlEntries = $acl.Access | Where-Object -FilterScript {
$_.IdentityReference -eq 'BUILTIN\Users' -and
$_.RegistryRights.HasFlag([System.Security.AccessControl.RegistryRights]::FullControl) -and
$_.AccessControlType.HasFlag([System.Security.AccessControl.AccessControlType]::Allow)
}
但是我们可以在这里做更多的事情。您的整个迭代过程都是寻找符合特定条件的项目,正如我们前面所看到的,$UsersFullControlEntries
就是这么做的!它的脚本块应返回布尔值$desiredRights = [System.Security.AccessControl.RegistryRights]::FullControl
$desiredControlType = [System.Security.AccessControl.AccessControlType]::Allow
# this is helping formatting
Get-ChildItem -LiteralPath "HKLM:" -Recurse | % {
$acl = $_.PSPath | Get-Acl
$UsersFullControlEntries = $acl.Access | Where-Object -FilterScript {
$_.IdentityReference -eq 'BUILTIN\Users' -and
$_.RegistryRights.HasFlag($desiredRights) -and
$_.AccessControlType.HasFlag($desiredControlType)
}
if ($UsersFullControlEntries) {
echo $_.Name
}
}
/ Where-Object
,而不是返回输出,如果该值为$true
,则为原始对象({{ 1}})将沿管道传递。因此,我们可以将$false
替换为$true
,以获得更好的语法:
$_
现在不需要 ForEach-Object
,该值直接从脚本块中返回,直接解释为true / false,而无需编写条件,现在将原始对象返回给调用者(不是Where-Object
属性),但这更好;您应该努力保留它,以便可以尽可能长时间地使用最完整的对象。在任何时候,您都可以引用或扩展返回对象的$desiredRights = [System.Security.AccessControl.RegistryRights]::FullControl
$desiredControlType = [System.Security.AccessControl.AccessControlType]::Allow
# this is helping formatting
Get-ChildItem -LiteralPath "HKLM:" -Recurse | Where-Object {
$acl = $_.PSPath | Get-Acl
$acl.Access | Where-Object -FilterScript {
$_.IdentityReference -eq 'BUILTIN\Users' -and
$_.RegistryRights.HasFlag($desiredRights) -and
$_.AccessControlType.HasFlag($desiredControlType)
}
}
属性,但是如果需要,您还可以使用所有其他属性。
再进行一次优化,现在让我们摆脱该$UsersFullControlEntries
变量,而使其成为.Name
内部的一个管道:
.Name
希望这为您提供了一些见识,想法和工具,以继续探索PowerShell!