为什么 Test-Connection 会强制枚举重新分析点?
Why does Test-Connection force enumeration of reparse points?
我注意到 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
命令 returns 没有预期的文件,因为 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
可以针对任何计算机 运行,而不仅仅是表现出此行为的本地计算机。
我在计算机 运行ning PowerShell 4.0 和 PowerShell 5.1 上复制了这种行为。谁能解释一下发生了什么?
附加说明:要复制此行为,请确保您使用的是提升的 PowerShell 实例(运行 作为管理员)。如果您使用 PowerShell 的标准实例,您将无权查看 C:\Documents and Settings\
.
要回答似乎忽略了 -Attributes !ReparsePoint
的第一个问题,请在此处查看我的完整 SO 答案和示例:How to prevent recursion through node_modules for gci script。 TLDR 是通过将 -Recurse
参数添加到 Get-ChildItem
意味着它将遍历 -all- 项 first 然后 它将应用过滤。这意味着它将忠实地排除重新分析点,即特定文件夹:C:\Documents and Settings
但由于它得到所有项目 first,它也会 return 该文件夹下的所有内容.例如C:\Documents and Settings\desktop.ini
因为 不是 重分析点,它是 文档 。这就是为什么你不能在没有额外目录级过滤的情况下使用 -Recurse
参数的原因,如果这是你的意图的话。
第二个问题,执行 Test-Connection
改变了 Get-ChildItem
returns,看起来确实像一个错误,看起来这是 PowerShell 的情况而不是"fully" 在 Get-ChildItem
首次执行时提升用户。但与此同时,这可能是 PowerShell 团队有意为之,以消除 returned.
意外重复项的情况。
为了进行调查,让我们以管理员身份打开一个新的 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 : Access to the path 'C:\Documents and Settings' is
denied.
这突出了一个事实,即即使我们 运行 以管理员身份运行 PowerShell 提示符,我们也没有 运行 以足以访问隐藏、重新解析的方式 运行 运行点,系统文件夹,"Documents and Settings"。我们必须记住,"Documents and Settings" 文件夹是旧版( 糟糕的编码器 ;-)与 Windows Vista 等兼容的廉价解决方法,并且是不适合 "daily" 正常使用。如果我们确实使用它,它可能会导致无意中 duplicate/recursive 项目被 returned。即:
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 文件夹的内容,但是由于之前的错误,我们没有 return Documents and Settings 文件夹的内容。这很好而且可能是故意的,因为我们没有得到任何重复的项目。我们只在 "real" 路径上得到了 "real" 项。
如果我们运行接下来是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
会导致不同的结果。较早的 Access Denied 错误消息给了我一个线索,即身份验证似乎是导致差异的原因。让我们关闭 PowerShell 提示符并 re-open 它。让我们 re-run 同样的第一个命令:
$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
....
由此可见,"well known"组S-1-5-32-544
为管理员。请注意模拟级别是 "None"。如果我们 运行 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:
Impersonation 3
The server process can impersonate the client's
security context on its local system. The server cannot impersonate
the client on remote systems.
当我们执行 Test-Connection
时,它需要本地计算机上的网络资源才能执行 ping。为了做到这一点,它透明地将提示从 None
提升到 Impersonate
级别(我们甚至可以看到这是因为令牌号发生了变化)。这具有允许 Get-ChildItem
现在具有访问系统文件文档和设置的提升权限的副作用。即在文件资源管理器中勾选 "show hidden files",现在允许它通过重分析点进行枚举。
当我们远程进入另一台机器时,我们也可以观察到这是原因:
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
....
当我们这样做时,我们看到它 returns 文档和设置,而不需要先执行 Test-Connection
。查看 Principal,我们看到模拟级别设置为 Impersonation
。
最终这告诉我们,造成这种不协调的原因是模拟级别设置。为了访问隐藏的系统文件和 Reparse Point,我们需要将 Impersonation Level 至少设置为 Impersonation
.
我注意到 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
命令 returns 没有预期的文件,因为 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
可以针对任何计算机 运行,而不仅仅是表现出此行为的本地计算机。
我在计算机 运行ning PowerShell 4.0 和 PowerShell 5.1 上复制了这种行为。谁能解释一下发生了什么?
附加说明:要复制此行为,请确保您使用的是提升的 PowerShell 实例(运行 作为管理员)。如果您使用 PowerShell 的标准实例,您将无权查看 C:\Documents and Settings\
.
要回答似乎忽略了 -Attributes !ReparsePoint
的第一个问题,请在此处查看我的完整 SO 答案和示例:How to prevent recursion through node_modules for gci script。 TLDR 是通过将 -Recurse
参数添加到 Get-ChildItem
意味着它将遍历 -all- 项 first 然后 它将应用过滤。这意味着它将忠实地排除重新分析点,即特定文件夹:C:\Documents and Settings
但由于它得到所有项目 first,它也会 return 该文件夹下的所有内容.例如C:\Documents and Settings\desktop.ini
因为 不是 重分析点,它是 文档 。这就是为什么你不能在没有额外目录级过滤的情况下使用 -Recurse
参数的原因,如果这是你的意图的话。
第二个问题,执行 Test-Connection
改变了 Get-ChildItem
returns,看起来确实像一个错误,看起来这是 PowerShell 的情况而不是"fully" 在 Get-ChildItem
首次执行时提升用户。但与此同时,这可能是 PowerShell 团队有意为之,以消除 returned.
为了进行调查,让我们以管理员身份打开一个新的 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 : Access to the path 'C:\Documents and Settings' is denied.
这突出了一个事实,即即使我们 运行 以管理员身份运行 PowerShell 提示符,我们也没有 运行 以足以访问隐藏、重新解析的方式 运行 运行点,系统文件夹,"Documents and Settings"。我们必须记住,"Documents and Settings" 文件夹是旧版( 糟糕的编码器 ;-)与 Windows Vista 等兼容的廉价解决方法,并且是不适合 "daily" 正常使用。如果我们确实使用它,它可能会导致无意中 duplicate/recursive 项目被 returned。即:
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 文件夹的内容,但是由于之前的错误,我们没有 return Documents and Settings 文件夹的内容。这很好而且可能是故意的,因为我们没有得到任何重复的项目。我们只在 "real" 路径上得到了 "real" 项。
如果我们运行接下来是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
会导致不同的结果。较早的 Access Denied 错误消息给了我一个线索,即身份验证似乎是导致差异的原因。让我们关闭 PowerShell 提示符并 re-open 它。让我们 re-run 同样的第一个命令:
$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
....
由此可见,"well known"组S-1-5-32-544
为管理员。请注意模拟级别是 "None"。如果我们 运行 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:
Impersonation 3
The server process can impersonate the client's security context on its local system. The server cannot impersonate the client on remote systems.
当我们执行 Test-Connection
时,它需要本地计算机上的网络资源才能执行 ping。为了做到这一点,它透明地将提示从 None
提升到 Impersonate
级别(我们甚至可以看到这是因为令牌号发生了变化)。这具有允许 Get-ChildItem
现在具有访问系统文件文档和设置的提升权限的副作用。即在文件资源管理器中勾选 "show hidden files",现在允许它通过重分析点进行枚举。
当我们远程进入另一台机器时,我们也可以观察到这是原因:
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
....
当我们这样做时,我们看到它 returns 文档和设置,而不需要先执行 Test-Connection
。查看 Principal,我们看到模拟级别设置为 Impersonation
。
最终这告诉我们,造成这种不协调的原因是模拟级别设置。为了访问隐藏的系统文件和 Reparse Point,我们需要将 Impersonation Level 至少设置为 Impersonation
.