通过Invoke-Command远程运行时,为什么Get-WmiObject失败?

时间:2019-07-12 21:52:16

标签: powershell invoke-command get-wmiobject

我正在尝试构建一个简单的“ get-ProcessInfo.ps1”模块,以与称为Kansa的PowerShell(取证/ IR)框架一起使用。该命令是一个简单的衬套,它调用Get-WMIObject win32_process并将其通过管道传输到Select-Object。然后,Kansa应该通过Export-Csv cmdlet将数据导出到Csv。该脚本在我的本地主机上运行没有问题,但是当通过Kansa中的Invoke-Command cmdlet远程运行(在Windows上)时,该脚本失败。我的错误日志显示每个进程ID的get-ProcessInfo.ps1“找不到具有进程标识符####的进程”。其他模块可以在远程主机上正常运行,因此我知道我正在以管理员身份进行身份验证。因此,我认为我遇到了权限错误,或者可能是Wmi的身份验证问题。我正在Windows域中通过域管理员帐户在Windows域中运行此程序。

Kansa GitHub:https://github.com/davehull/Kansa Kansa Talk:https://www.youtube.com/watch?v=cQmsT9D0kKI

我试图复制WmiObject调用,如在另一个Kansa模块中所见,但这仍然没有从远程主机产生数据。 -https://github.com/davehull/Kansa/blob/master/Modules/Process/Get-ProcsWMI.ps1

我试图了解InjectedThreads.ps1脚本中发生了什么,因为它远程使用WmiObject没问题,但是有点麻烦。据我了解,听起来WmiObject是“不受管的”(未经身份验证?/不是从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}}

预期结果应该是在我的本地计算机上工作的进程及其相关信息的列表,并且当通过Kansa.ps1中的Invoke-Command远程运行时不返回任何数据(只是错误)

有人可以为我指明正确的方向,以及这里发生的事情以及如何解决该问题?

*请注意,此脚本是通过WinRM(Invoke-Command)在远程主机上运行的,因此不需要凭据,因此硬编码凭据也不是问题。

1 个答案:

答案 0 :(得分:1)

在您的计算属性中,您假设ParentProcessId始终是有效ID:

@{Name = 'ParentProcessPath';Expression = {(Get-Process -Id $_.ParentProcessId).Path}

Win32_Process documentation指出以下内容:

  

ParentProcessId标识的进程可能会终止,因此ParentProcessId可能不会引用正在运行的进程。 ParentProcessId也可能错误地引用了重用进程标识符的进程。

这意味着ParentProcessId可能指向一个已经终止的进程。在那种情况下,(Get-Process -Id $_.ParentProcessId).Path将触发错误(因为未找到例如528的进程,因此(Get-Process -Id $_.ParentProcessId)将是$null,而您正试图调用$null.Name)。 / p>

我在机器上测试了您的单缸纸,但没有收到如上所述的任何错误。因此,我检查了Kansa如何调用给定的模块,该模块可以在here中找到:

$Job = Invoke-Command -Session $PSSessions -FilePath $Module -ArgumentList $Arguments -AsJob -ThrottleLimit $ThrottleLimit

您可以看到您的模块文件被作为远程作业调用(请参见-AsJob开关)。我问自己,通过后台调用脚本块时,错误处理是否可能有所不同。我发现这个有趣的StackOverflow answer指出:

  

使用throw会将作业对象的State属性更改为“ 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时,在命令行上看到以下错误:

  

找不到带有进程标识符528的进程。   在第1行:char:1   + $结果=接收工作$工作   + ~~~~~~~~~~~~~~~~~~~~~~~~~       + CategoryInfo:ObjectNotFound:(528:Int32)[Get-Process],ProcessCommandException       + FullyQualifiedErrorId:NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.GetProcessCommand       + PSComputerName:本地主机

我还检查了$result中的$null

> $null -eq $result
True

从上面可以看到,错误提到NoProcessFoundForGivenId,这是不言自明的。如您所见,错误处理取决于代码的调用方式(作为Job或在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"
}

我们将获得所有流程而没有任何错误。

希望有帮助。