为什么Test-Connection强制重新分析点的枚举?

时间:2017-12-13 21:01:26

标签: powershell get-childitem directory-listing

我注意到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\

1 个答案:

答案 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*"};

我们注意到两件事:

  1. 我们没有收到任何拒绝访问错误消息。
  2. 我们得到了更多(重复)的物品:

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