我注意到PowerShell中的一些我无法解释的行为,但我希望别人可以这样做。
如果我想从驱动器C:\
构建文件对象列表,我想忽略快捷方式文件夹(重新分析点),例如C:\Documents and Settings\
。以下命令效果很好:
$FileList = @(Get-ChildItem -Path C:\ -Recurse -Force -Attributes !ReparsePoint);
$FileList | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
Where-Object
命令不会按预期返回任何文件,因为C:\Documents and Settings\
是重新分析点。
但是,如果我先运行Test-Connection
命令,则Get-ChildItem
命令会忽略-Attributes !ReparsePoint
参数,并且会遍历C:\Documents and Settings\
。
Test-Connection -Computer MyComputer;
$FileList = @(Get-ChildItem -Path C:\ -Recurse -Force -Attributes !ReparsePoint);
$FileList | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
在这种情况下,Where-Object
命令会显示很多文件。请注意,Test-Connection
可以针对任何计算机运行,而不仅仅是本地计算机才能显示此行为。
我在运行PowerShell 4.0和PowerShell 5.1的计算机上重复了这种行为。有人可以解释一下发生了什么吗?
附加说明:要复制此行为,请确保您使用的是PowerShell的提升实例(以管理员身份运行)。如果您使用PowerShell的标准实例,则无权查看C:\Documents and Settings\
。
答案 0 :(得分:3)
要回答第一个似乎忽略-Attributes !ReparsePoint
的问题,请在以下示例中查看我的完整SO答案,并提供示例:How to prevent recursion through node_modules for gci script。 TLDR就是通过在-Recurse
中添加Get-ChildItem
参数,意味着它将迭代 -all-项目 first then 它将应用过滤。这意味着它将忠实地排除“重新分析点”,即排除特定的文件夹:C:\Documents and Settings
,但由于它先是所有项目,因此它也将返回该文件夹下的所有内容。例如C:\Documents and Settings\desktop.ini
,因为这不是 解析点,而是文档。因此,如果您打算这样做,则无法在没有附加目录级别过滤的情况下使用-Recurse
参数。
执行Test-Connection
会更改Get-ChildItem
返回的第二个问题,它看起来像是一个bug,看起来像是PowerShell在没有“完全”提升用户权限的情况下Get-ChildItem
首先执行。但是与此同时,PowerShell团队可能有意消除意外的重复项目被退回的情况。
要进行调查,我们以管理员身份打开一个新的PowerShell提示符。我将代码稍作更改以包含一个-Depth 1
,只是为了使其更容易遍历代码而不必枚举我的整个C:
驱动器;-),但是,它仍然可以说明问题:>
$FileList = @(Get-ChildItem -Path C:\ -Depth 1 -Recurse -Force -Attributes !ReparsePoint);
$FileList | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
运行此命令时,错误消息的第一个线索是:
Get-ChildItem:访问路径“ C:\ Documents and Settings”为 否认。
这凸显了一个事实,即使我们以管理员身份运行PowerShell提示符,但运行方式却不足以“提升”访问隐藏的,重解析点,系统文件夹,“文档和设置”。我们必须记住,“文档和设置”文件夹是与Windows Vista等兼容的旧版(坏代码 ;-)兼容性的一种廉价解决方案,而不是“日常”正常使用用法。而且,如果我们确实使用它,可能会导致意外的重复/递归项被退回。即:
PS C:\> $FileList | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
PS C:\>
PS C:\> $FileList | Where-Object {$_.DirectoryName -like "*Users*"};
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 2018-09-15 1:31 AM 174 desktop.ini
我们看到已经获得了Users文件夹的内容,但是由于先前的错误,我们没有返回Documents and Settings文件夹的内容。这很好,可能是故意的,因为我们没有得到任何重复的物品。我们只有“真实”路径上的“真实”项目。
如果接下来运行Test-Connection
命令,然后再执行同一命令(使用不同的变量名以消除混淆):
Test-Connection -Computer MyComputer;
$FileList2 = @(Get-ChildItem -Path C:\ -Depth 1 -Recurse -Force -Attributes !ReparsePoint);
$FileList2 | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
我们注意到两件事:
。
PS C:\> $FileList2 | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
Directory: C:\Documents and Settings
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 2018-09-15 1:31 AM 174 desktop.ini
PS C:\> $FileList2 | Where-Object {$_.DirectoryName -like "*Users*"};
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 2018-09-15 1:31 AM 174 desktop.ini
这是无意的。不需要从“重新分析点”路径返回项目。如果我们将这些信息提供给其他脚本任务,例如Remove-Item
,那么我们可能会遇到意想不到的问题。
现在,让我们继续研究为什么看似无害的命令Test-Connection
会导致不同的结果。较早的访问被拒绝错误消息给我一个线索,即身份验证周围的某些问题似乎引起了这种差异。让我们关闭PowerShell提示,然后重新打开。让我们重新运行相同的第一个命令:
$FileList = @(Get-ChildItem -Path C:\ -Depth 1 -Recurse -Force -Attributes !ReparsePoint);
$FileList | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
让我们检查凭据以确保我们以管理员身份运行:
PS C:\> $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
PS C:\> $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
True
实际上,我们正在以管理员身份运行提示,所以这不是问题。让我们进一步了解身份:
PS C:\> $currentPrincipal.Identities
AuthenticationType : Kerberos
ImpersonationLevel : None
IsAuthenticated : True
IsGuest : False
IsSystem : False
IsAnonymous : False
Name : Contoso\HAL9256
Owner : S-1-5-32-544
....
Token : 3076
....
由此,我们可以看到“知名”组S-1-5-32-544
为管理员。请注意模拟级别为“无”。如果我们运行Test-Connection
和相同的命令:
Test-Connection -Computer D4700;
$FileList2 = @(Get-ChildItem -Path C:\ -Depth 1 -Recurse -Force -Attributes !ReparsePoint);
$FileList2 | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
并获取主要对象:
PS C:\> $currentPrincipal2 = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
PS C:\> $currentPrincipal2.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
True
这证明我们仍在以管理员身份运行。现在让我们看一下身份:
PS C:\> $currentPrincipal2.Identities
AuthenticationType : Kerberos
ImpersonationLevel : Impersonation
IsAuthenticated : True
IsGuest : False
IsSystem : False
IsAnonymous : False
Name : Contoso\HAL9256
Owner : S-1-5-32-544
....
Token : 4500
....
这次,我们看到模拟级别为Impersonation
。来自Impersonation Level Enum Docs:
冒充他人3
服务器进程可以模拟客户端的 其本地系统上的安全上下文。服务器无法模拟 远程系统上的客户端。
执行Test-Connection
时,它需要本地计算机上的网络资源才能执行ping操作。为了做到这一点,它透明地将提示从Impersonate
提升到None
级别(我们甚至可以看到这是因为令牌号更改了)。这具有使Get-ChildItem
现在具有提升的权限来访问系统文件“文档和设置”的副作用。即就像在文件资源管理器中选中“显示隐藏的文件”,现在允许它通过“重新解析点”进行枚举。
我们还可以观察到这是我们远程访问另一台计算机时的原因:
PS C:\> Enter-PSSession -ComputerName RemoteMachine -Credential (Get-Credential) -Authentication Kerberos
[RemoteMachine]: PS C:\Users\HAL9256\Documents> $FileList = @(Get-ChildItem -Path C:\ -Depth 1 -Recurse -Force -Attributes !ReparsePoint);
[RemoteMachine]: PS C:\Users\HAL9256\Documents> $FileList | Where-Object {$_.DirectoryName -like "*Documents and Settings*"};
Directory: C:\Documents and Settings
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 7/16/2016 7:21 AM 174 desktop.ini
[RemoteMachine]: PS C:\Users\HAL9256\Documents>
[RemoteMachine]: PS C:\Users\HAL9256\Documents> $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
[RemoteMachine]: PS C:\Users\HAL9256\Documents> $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
True
[RemoteMachine]: PS C:\Users\HAL9256\Documents>
[RemoteMachine]: PS C:\Users\HAL9256\Documents> $currentPrincipal.Identities
AuthenticationType : Kerberos
ImpersonationLevel : Impersonation
IsAuthenticated : True
IsGuest : False
IsSystem : False
IsAnonymous : False
Name : Contoso\HAL9256
Owner : S-1-5-32-544
....
Token : 4420
....
这样做时,我们看到它返回文档和设置,而无需首先执行Test-Connection
。查看主体,我们看到模拟级别设置为Impersonation
。
最终,这告诉我们造成这种不一致的原因是模拟级别设置。为了访问隐藏的系统文件和Reparse Point,我们需要将“模拟级别”至少设置为Impersonation
。