为什么通过 Invoke-Command 远程 运行 时 Get-WmiObject 会失败?
Why does Get-WmiObject fail when run remotely via Invoke-Command?
我正在尝试构建一个简单的 "get-ProcessInfo.ps1" 模块,以与名为 Kansa 的 PowerShell (forensics/IR) 框架一起使用。该命令是一个简单的单行代码,它调用 Get-WMIObject win32_process 并将其通过管道传输到 Select-Object。然后 Kansa 应该通过 Export-Csv cmdlet 将数据导出到 Csv。脚本 运行 在我的本地主机上没有问题,但是当通过 Kansa 中的 Invoke-Command cmdlet 远程(在 windows 上)运行 时它失败了。我的错误日志显示每个进程 ID 的 get-ProcessInfo.ps1 "Cannot find a process with the process identifier ####"。远程主机上的其他模块 运行 没有问题,所以我知道我正在以管理员身份进行身份验证。因此,我认为我 运行 遇到了权限错误,或者可能是 Wmi 的身份验证问题。我通过域管理员帐户从 Windows 框中 运行 在 Windows 域中进行此操作。
堪萨斯GitHub:https://github.com/davehull/Kansa
堪萨斯谈话:https://www.youtube.com/watch?v=cQmsT9D0kKI
我已经尝试复制在另一个 Kansa 模块中看到的 WmiObject 调用,但这仍然没有从远程主机生成数据。 - https://github.com/davehull/Kansa/blob/master/Modules/Process/Get-ProcsWMI.ps1
我试图了解 InjectedThreads.ps1 脚本中发生的事情,因为它可以毫无问题地远程使用 WmiObject,但这有点让我头疼。据我所知,WmiObject 听起来像是 "unmanaged"(未经身份验证?/没有从 PowerShell 继承 Kerberos?)- https://github.com/davehull/Kansa/blob/master/Modules/Process/Get-InjectedThreads.ps1
我尝试了 Wmi 身份验证、模拟和特权的多种变体。不幸的是,这仍然没有产生远程数据。 - https://blogs.msmvps.com/richardsiddaway/2011/08/04/authentication-impersonation-and-privileges/
最后,由于 get-WmiObject 在技术上已被弃用,取而代之的是 Get-CIMInstance,我尝试了 Get-CIMInstance cmdlet 的多种变体。
这是我尝试创建的模块中的代码,get-ProcessInfo。ps1
Get-WmiObject win32_process | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, @{Name = 'ParentProcessName';Expression = {(Get-Process -Id $_.ParentProcessId).Name}},Path,CommandLine,@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}}
预期结果应该是一个进程列表及其相关信息,可以在我的本地机器上运行,并且 returns 当 运行 远程通过 Invoke-Command 时没有数据(只是错误)堪萨斯ps1
有人可以为我指出正确的方向吗?这里到底发生了什么,我该如何解决这个问题?
*请注意,此脚本是通过远程主机上的 WinRM(调用命令)运行,因此要求凭据是不可能的,硬编码凭据也是如此。
在您的计算属性中,您假设 ParentProcessId
始终是有效 ID:
@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}
Win32_Process documentation 声明如下:
It is possible that the process identified by ParentProcessId is terminated, so ParentProcessId may not refer to a running process. It is also possible that ParentProcessId incorrectly refers to a process that reuses a process identifier.
这意味着 ParentProcessId
可能指向一个已经终止的进程。在那种情况下 (Get-Process -Id $_.ParentProcessId).Path
将触发错误(因为没有找到例如 528 的进程,因此 (Get-Process -Id $_.ParentProcessId)
将是 $null
并且您正在尝试调用 $null.Name
)。
我在我的机器上测试了你的one-liner,没有收到你上面描述的任何错误。因此我检查了 Kansa 如何调用给定的模块,可以找到 here:
$Job = Invoke-Command -Session $PSSessions -FilePath $Module -ArgumentList $Arguments -AsJob -ThrottleLimit $ThrottleLimit
如您所见,您的模块文件作为远程作业被调用(请参阅 -AsJob
开关)。我问自己,在通过后台调用脚本块时,错误处理是否会有所不同。我发现这个有趣的 Whosebug answer 说明:
Using throw will change the job object's State property to "Failed". ...
这是有道理的,因为与默认的 PowersShell 主机相比,作业不能忽略错误。所以,我将你的命令打包到一个作业中(在我的本地机器上):
$job =start-job -ScriptBlock { Get-WmiObject win32_process | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, @{Name = 'ParentProcessName';Expression = {(Get-Process -Id $_.ParentProcessId).Name}},Path,CommandLine,@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}} }
Wait-Job $job
$result =Receive-Job $job
调用 Receive-Job
时,我在命令行中看到以下错误:
Cannot find a process with the process identifier 528.
At line:1 char:1
+ $result =Receive-Job $job
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (528:Int32) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.GetProcessCommand
+ PSComputerName : localhost
我还检查了 $result
的 $null
:
> $null -eq $result
True
如上所示,错误提到NoProcessFoundForGivenId
,即self-explanatory。如您所见,错误处理取决于代码的调用方式(作为作业或在 PowerShell 主机上)。
你能做什么?
您可以检查ParentProcessId
是否有效。因此,您必须将代码更改为:
Get-WmiObject win32_process | ForEach-Object {
# Create a custom object with the given properties. If Select-Object doesn't find given property it'll create an empty `NoteProperty` with the given name
$processInfo = $_ | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, ParentProcessPath, ParentProcessName
$p = (Get-Process -Id $_.ParentProcessId -ErrorAction SilentlyContinue)
if ($null -ne $p){
# Parent process exists lets fill up the empty properties
$processInfo.ParentProcessName = $p.Name
$processInfo.ParentProcessPath = $p.Path
}
$processInfo # Return this value to the pipeline
}
可能有更复杂的方法来执行 $null
检查 Select-Object
的计算 属性,不幸的是,我不知道它。
如果我们将上面的代码包装在一个作业中,并且 运行 它:
$job = start-job -ScriptBlock { Get-WmiObject win32_process | ForEach-Object {
$processInfo = $_ | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, ParentProcessPath, ParentProcessName
$p = (Get-Process -Id $_.ParentProcessId -ErrorAction SilentlyContinue)
if ($null -ne $p){
$processInfo.ParentProcessName = $p.Name
$processInfo.ParentProcessPath = $p.Path
}
$processInfo # Return this value to the pipeline
}
}
Wait-Job $job
$result = Receive-Job $job
if ($null -ne $result){
$result
}
else {
Write-Error "Job with id $($job.Id) failed"
}
我们将获得所有进程而不会出现任何错误。
希望对您有所帮助。
我正在尝试构建一个简单的 "get-ProcessInfo.ps1" 模块,以与名为 Kansa 的 PowerShell (forensics/IR) 框架一起使用。该命令是一个简单的单行代码,它调用 Get-WMIObject win32_process 并将其通过管道传输到 Select-Object。然后 Kansa 应该通过 Export-Csv cmdlet 将数据导出到 Csv。脚本 运行 在我的本地主机上没有问题,但是当通过 Kansa 中的 Invoke-Command cmdlet 远程(在 windows 上)运行 时它失败了。我的错误日志显示每个进程 ID 的 get-ProcessInfo.ps1 "Cannot find a process with the process identifier ####"。远程主机上的其他模块 运行 没有问题,所以我知道我正在以管理员身份进行身份验证。因此,我认为我 运行 遇到了权限错误,或者可能是 Wmi 的身份验证问题。我通过域管理员帐户从 Windows 框中 运行 在 Windows 域中进行此操作。
堪萨斯GitHub:https://github.com/davehull/Kansa 堪萨斯谈话:https://www.youtube.com/watch?v=cQmsT9D0kKI
我已经尝试复制在另一个 Kansa 模块中看到的 WmiObject 调用,但这仍然没有从远程主机生成数据。 - https://github.com/davehull/Kansa/blob/master/Modules/Process/Get-ProcsWMI.ps1
我试图了解 InjectedThreads.ps1 脚本中发生的事情,因为它可以毫无问题地远程使用 WmiObject,但这有点让我头疼。据我所知,WmiObject 听起来像是 "unmanaged"(未经身份验证?/没有从 PowerShell 继承 Kerberos?)- https://github.com/davehull/Kansa/blob/master/Modules/Process/Get-InjectedThreads.ps1
我尝试了 Wmi 身份验证、模拟和特权的多种变体。不幸的是,这仍然没有产生远程数据。 - https://blogs.msmvps.com/richardsiddaway/2011/08/04/authentication-impersonation-and-privileges/
最后,由于 get-WmiObject 在技术上已被弃用,取而代之的是 Get-CIMInstance,我尝试了 Get-CIMInstance cmdlet 的多种变体。
这是我尝试创建的模块中的代码,get-ProcessInfo。ps1
Get-WmiObject win32_process | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, @{Name = 'ParentProcessName';Expression = {(Get-Process -Id $_.ParentProcessId).Name}},Path,CommandLine,@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}}
预期结果应该是一个进程列表及其相关信息,可以在我的本地机器上运行,并且 returns 当 运行 远程通过 Invoke-Command 时没有数据(只是错误)堪萨斯ps1
有人可以为我指出正确的方向吗?这里到底发生了什么,我该如何解决这个问题?
*请注意,此脚本是通过远程主机上的 WinRM(调用命令)运行,因此要求凭据是不可能的,硬编码凭据也是如此。
在您的计算属性中,您假设 ParentProcessId
始终是有效 ID:
@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}
Win32_Process documentation 声明如下:
It is possible that the process identified by ParentProcessId is terminated, so ParentProcessId may not refer to a running process. It is also possible that ParentProcessId incorrectly refers to a process that reuses a process identifier.
这意味着 ParentProcessId
可能指向一个已经终止的进程。在那种情况下 (Get-Process -Id $_.ParentProcessId).Path
将触发错误(因为没有找到例如 528 的进程,因此 (Get-Process -Id $_.ParentProcessId)
将是 $null
并且您正在尝试调用 $null.Name
)。
我在我的机器上测试了你的one-liner,没有收到你上面描述的任何错误。因此我检查了 Kansa 如何调用给定的模块,可以找到 here:
$Job = Invoke-Command -Session $PSSessions -FilePath $Module -ArgumentList $Arguments -AsJob -ThrottleLimit $ThrottleLimit
如您所见,您的模块文件作为远程作业被调用(请参阅 -AsJob
开关)。我问自己,在通过后台调用脚本块时,错误处理是否会有所不同。我发现这个有趣的 Whosebug answer 说明:
Using throw will change the job object's State property to "Failed". ...
这是有道理的,因为与默认的 PowersShell 主机相比,作业不能忽略错误。所以,我将你的命令打包到一个作业中(在我的本地机器上):
$job =start-job -ScriptBlock { Get-WmiObject win32_process | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, @{Name = 'ParentProcessName';Expression = {(Get-Process -Id $_.ParentProcessId).Name}},Path,CommandLine,@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}} }
Wait-Job $job
$result =Receive-Job $job
调用 Receive-Job
时,我在命令行中看到以下错误:
Cannot find a process with the process identifier 528. At line:1 char:1 + $result =Receive-Job $job + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (528:Int32) [Get-Process], ProcessCommandException + FullyQualifiedErrorId : NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.GetProcessCommand + PSComputerName : localhost
我还检查了 $result
的 $null
:
> $null -eq $result
True
如上所示,错误提到NoProcessFoundForGivenId
,即self-explanatory。如您所见,错误处理取决于代码的调用方式(作为作业或在 PowerShell 主机上)。
你能做什么?
您可以检查ParentProcessId
是否有效。因此,您必须将代码更改为:
Get-WmiObject win32_process | ForEach-Object {
# Create a custom object with the given properties. If Select-Object doesn't find given property it'll create an empty `NoteProperty` with the given name
$processInfo = $_ | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, ParentProcessPath, ParentProcessName
$p = (Get-Process -Id $_.ParentProcessId -ErrorAction SilentlyContinue)
if ($null -ne $p){
# Parent process exists lets fill up the empty properties
$processInfo.ParentProcessName = $p.Name
$processInfo.ParentProcessPath = $p.Path
}
$processInfo # Return this value to the pipeline
}
可能有更复杂的方法来执行 $null
检查 Select-Object
的计算 属性,不幸的是,我不知道它。
如果我们将上面的代码包装在一个作业中,并且 运行 它:
$job = start-job -ScriptBlock { Get-WmiObject win32_process | ForEach-Object {
$processInfo = $_ | Select-Object creationdate,ws,ProcessName,ProcessID,ParentProcessID, ParentProcessPath, ParentProcessName
$p = (Get-Process -Id $_.ParentProcessId -ErrorAction SilentlyContinue)
if ($null -ne $p){
$processInfo.ParentProcessName = $p.Name
$processInfo.ParentProcessPath = $p.Path
}
$processInfo # Return this value to the pipeline
}
}
Wait-Job $job
$result = Receive-Job $job
if ($null -ne $result){
$result
}
else {
Write-Error "Job with id $($job.Id) failed"
}
我们将获得所有进程而不会出现任何错误。
希望对您有所帮助。