functions/Process.ps1
# returns a tuple (PowerSponse object and 2nd the execution output) Function Invoke-PsExec() { [CmdletBinding(SupportsShouldProcess=$True)] param ( [string[]] $ComputerName, [string] $Program, [string] $CommandLine, [switch] $AsSystem = $false, [switch] $CopyProgramToRemoteSystem = $false, [switch] $ForceCopyProgramToRemoteSystem = $false ) $Function = $MyInvocation.MyCommand Write-Verbose "Entering $Function" $returnobject = @() $RemoteRegistryStarted = $false $WhatIfPassed = $false $Status = "" $Reason = "" $ReturnValue = $null Write-Verbose "Using PsExec for starting '$Program' on '$ComputerName'." $Arguments = "Program: $Program, CommandLine: $CommandLine, AsSystem: $AsSystem, CopyFile: $CopyProgramToRemoteSystem, ForceCopy: $ForceCopyProgramToRemoteSystem" $ComputerName = Get-Target -ComputerName:$(if ($ComputerName){$ComputerName}) if ($PSBoundParameters.ContainsKey('whatif') -and $PSBoundParameters['whatif'].ispresent) { $WhatIfPassed = $true } Write-Verbose "whatif: $WhatIfPassed" # Enable and start RemoteRegistry try { # Enable RemoteRegistry for psexec $params = @{ 'method' = "external"; 'ComputerName' = $ComputerName; 'WhatIf' = $WhatIfPassed; 'OnlineCheck' = $false } $err = Enable-RemoteRegistry @params $returnobject += $err $params = @{ 'ComputerName' = $ComputerName; 'method' = "external"; 'WhatIf' = $WhatIfPassed; 'Name' = "RemoteRegistry"; 'OnlineCheck' = $false } $srr = Start-Service @params $returnobject += $srr $RemoteRegistryStarted = ($srr.status -match "pass") } catch { $Status = "fail" $Reason = "Error while enabling RemoteRegistry" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason } if ($pscmdlet.ShouldProcess($ComputerName, "Execute $Program $CommandLine")) { if ($RemoteRegistryStarted) { Write-Progress -Activity "Running $Function" -Status "Run '$Program $CommandLine' on $ComputerName..." $CommandLinePsExec = "-accepteula -nobanner " $CommandLinePsExec += "\\$ComputerName " if ($AsSystem) { $CommandLinePsExec += "-s " } if ($CopyProgramToRemoteSystem) { $CommandLinePsExec += "-c " } if ($ForceCopyProgramToRemoteSystem) { $CommandLinePsExec += "-f " } $CommandLinePsExec += "`"$Program`" $CommandLine" Write-Verbose "Using CommandLine for PsExec: $CommandLinePsExec" $params = @{ 'Binary' = "PsExec.exe"; 'CommandLine' = $CommandLinePsExec } $ReturnValue = (Start-Process @params) if ($ReturnValue.exitcode -eq 0) { $Status = "pass" $Reason = "See stdout/stderr in return value." } else { $Status = "fail" $Reason = "Error running '$Program $CommandLine' on $ComputerName. Wrong arguments or could be due to permissions. See stdout/stderr from return value." } } else { $Status = "fail" $Reason = "RemoteRegistry could not be started." } } #no whatif given else { $Status = "pass" $Reason = "Not executed - started with -WhatIf" } #whatif used # Stop and Disable RemoteRegistry if ($RemoteRegistryStarted -or $WhatIfPassed) { try { Write-Verbose "Cleanup RemoteRegistry on $ComputerName" $srr = Stop-Service -ComputerName $ComputerName -method external -Name "RemoteRegistry" -WhatIf:$WhatIfPassed -OnlineCheck:$false $returnobject += $srr $drr = Disable-RemoteRegistry -method external -ComputerName $ComputerName -WhatIf:$WhatIfPassed -OnlineCheck:$false $returnobject += $drr } catch { $Status = "fail" $Reason = "Error while disabling RemoteRegistry" } } $returnobject += New-PowerSponseObject -Function $Function -ComputerName $ComputerName -Arguments $Arguments -Status $Status -Reason $Reason # returnobject with all powersponse objects and return value from psexec call $returnobject,$ReturnValue Write-Verbose "Leaving $($MyInvocation.MyCommand)" } Function Get-Process() { [CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName='ByName')] param ( [string[]] $ComputerName, [string] $ComputerList, [ValidateSet("WMI", "External", "WinRM")] [string] $Method = "WinRM", [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"), [System.Management.Automation.Runspaces.PSSession[]] $Session=$Null, [System.Management.Automation.PSCredential] $Credential=$Null, [Parameter(ParameterSetName='ByName')] [string] $Name, [Parameter(ParameterSetName='ByPid')] [int] $Id, [ValidateSet("OnlyPid", "OnlyName", "All")] [string] $OutputFormat = "all" ) $Function = $MyInvocation.MyCommand Write-Verbose "Entering $Function" $returnobject = @() $res = "" $Arguments = $(if ($Name) { "Name $($Name)" } else { "Pid: $($Id)" } ) Write-Progress -Activity "Running $Function" -Status "Initializing..." if (!$Name -and !$Id) { $Reason = "no process name or process id, please use -Name or -Id" $Status = "fail" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments } elseif ($Name -and $Id) { $Status = "fail" $Reason = "both process name or process id given" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments } else { $targets = Get-Target -ComputerList:$(if ($ComputerList){$ComputerList}) -ComputerName:$(if ($ComputerName){$ComputerName}) foreach ($target in $targets) { Write-Progress -Activity "Running $Function" -Status "Processing $target..." if ($pscmdlet.ShouldProcess($target, "Get process with method $($Method)")) { if (!(Test-Connection $target -Quiet -Count 1)) { Write-Verbose "$target is offline" $Status = "fail" $Reason = "offline" } else { if ($Method -match "wmi") { Write-Verbose "Using WMI" if (($target -eq "localhost") -and $Credential) { $Status = "fail" $Reason = "localhost and WMI and credential not working" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target Continue } try { if ($Name) { $res = Get-WmiObject Win32_Process -ComputerName $target -Credential $Credential -ErrorAction Stop | ? {$_.name -match $Name -or $_.ExecutablePath -match $Name} } else { $res = Get-WmiObject Win32_Process -ComputerName $target -Credential $Credential -ErrorAction stop | ? {$_.Processid -eq $Id} } if (!$res) { $Status = "pass" $Reason = "no process found" } # not used currently to distinguish between one or multiple processes #elseif ((($res | measure).count -gt 1 )) #{ # $Status = "fail" # $Reason = "multiple processes found" #} else { $Status = "pass" $Reason = @() foreach ($proc in $res) { if ($OutputFormat -match "OnlyPid") { $Reason += "$($proc.ProcessId)" } elseif ($OutputFormat -match "OnlyName") { $Reason += "$($proc.ProcessName)" } else { $Reason += "$($proc.ProcessId) ; $($proc.ProcessName) ; $($proc.ExecutablePath)" } } } } # wmi call Successfull catch { $Status = "fail" $Reason = "error while connecting to remote host" } } elseif ($Method -match "winrm") { Write-Verbose "Using WinRM - Name: $Name, Id: $Id" try { $params = @{ 'ea' = 'SilentlyContinue' } if ($target -ne "localhost") { $params += @{ 'ComputerName' = $target 'SessionOption' = (New-PSSessionOption -NoMachineProfile) } } if ($Credential) { $params += @{ 'Credential' = $Credential } } if ($Name) { $params += @{ 'ScriptBlock' = {Microsoft.PowerShell.Management\get-process -ea SilentlyContinue} } $res = invoke-command @params $res = $res | ? { $_.ProcessName -match "$Name" -or $_.Path -match "$Name" } | select Id,ProcessName,Path } else { $params += @{ 'ScriptBlock' = {param($p1) Microsoft.PowerShell.Management\get-process -Id $p1 -ea SilentlyContinue} 'ArgumentList' = $Id } $res = invoke-command @params } if (!$res) { $Status = "pass" $Reason = "no process found" } else { $Status = "pass" $Reason = @() foreach ($proc in $res) { if ($OutputFormat -match "OnlyPid") { $Reason += "$($proc.Id)" } elseif ($OutputFormat -match "OnlyName") { $Reason += "$($proc.ProcessName)" } else { $Reason += "$($proc.Id) ; $($proc.ProcessName) ; $($proc.Path)" } } } } catch { $Status = "fail" $Reason = "error while connecting to remote host" } } elseif ($Method -match "external") { Write-Verbose "Using ExternalTools / Sysinternals" Write-Verbose "BinPath: $BinPath" if (!(Test-Path -Path "$BinPath\pslist.exe")) { $Status = "fail" $Reason = "Binary pslist not found." } elseif (!(Test-Path -Path "$BinPath\psservice.exe")) { $Status = "fail" $Reason = "Binary psservice not found." } else { # Enable RemoteRegistry for the target try { $res = Enable-RemoteRegistry -Method external -ComputerName $target -OnlineCheck:$false if ($res.status -match "pass") { Start-Service -ComputerName $target -Method external -Name "RemoteRegistry" -OnlineCheck:$false $RemoteRegistryStarted = $true # todo pslist - check if multiple processes are running if ($Name) { $proc = Start-Process "pslist.exe" "-accepteula -nobanner \\$target $Name" } else { $proc = Start-Process "pslist.exe" "-accepteula -nobanner \\$target $Id" } if ($proc.stdout | select-string "not" -quiet) { $Reason = "Process not found" $Status = "fail" } else { Write-Verbose "Process found" # todo if needed use # http://stackoverflow.com/questions/28287700/how-to-pipe-elapsed-time-or-other-output-from-pslist $MultipleProcesses = (($proc.stdout | measure -Line).Lines -gt 4) Write-Verbose "Process count: $($MultipleProcesses)" $Status = "pass" #$val = ($stdout.split("`n") | select -skip 2] $Reason = $proc.stdout } if ($proc.ExitCode -eq 0) { Write-Verbose "Successfully executed pslist on $target" } else { Write-Verbose "Error while running pslist on $target" } } # Enable-RemoteRegistry pass else { Write-Verbose "Error while enabling RemoteRegistry" $Reason = "Error while enabling RemoteRegistry" $Status = "fail" } } # no exception during enabling RemoteRegistry catch { Write-Verbose "Error while enabling RemoteRegistry" $Reason = "Error while enabling RemoteRegistry" $Status = "fail" } try { # Cleanup if ($RemoteRegistryStarted) { Write-Verbose "Cleanup RemoteRegistry" Stop-Service -ComputerName $target -Method external -Name "RemoteRegistry" -OnlineCheck:$false Disable-RemoteRegistry -Method external -ComputerName $target -OnlineCheck:$false } } catch { Write-Verbose "Error while disabling RemoteRegistry" #fix when process found but disabling failed, info accordi $Reason = "Error while disabling RemoteRegistry" $Status = "fail" } } #Binaries available } #UseExternal } #target online $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target } #whatif } #each target } #parameters ok $returnobject Write-Verbose "Leaving $($MyInvocation.MyCommand)" } # # Returns an object with ExitCode, stdout and stderr # # Used to start specified tool on remote host (e.g. md5deep, autoruns). Check # Kansa and Invoke-LiveResponse first for this task. Function Start-Process() { [CmdletBinding(SupportsShouldProcess=$True)] param ( [string] $Binary, [string] $CommandLine = "", [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin") ) $Function = $MyInvocation.MyCommand Write-Verbose "Entering $Function" $return = @() $Arguments = "BinPath: $BinPath, Binary: $Binary, CommandLine: $CommandLine" Write-Verbose "Arguments: $Arguments" if (!$Binary) { $Status = "fail" $Reason = "Binary $Binary not given." write-output $Reason return } if (Test-Path "$BinPath\$Binary") { $Path = "$BinPath\$Binary" } else { $Path = $Binary } Write-Verbose $Path if ($pscmdlet.ShouldProcess("Execute Binary $Path with CommandLine $CommandLine")) { $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "$Path" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = "$CommandLine" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo try { $p.Start() | Out-Null # todo fix timeout $p.WaitForExit(5000) | Out-Null $stdout = $p.StandardOutput.ReadToEnd() $stderr = $p.StandardError.ReadToEnd() $info = [ordered]@{ ExitCode = $p.ExitCode stdout = $stdout stderr = $stderr } } catch { $info = [ordered]@{ ExitCode = 1 stdout = "" stderr = "Error starting $Binary, binary not found in path or bin folder" } } $ret = New-Object -TypeName PSObject -Property $info $return += $ret } #whatif $return Write-Verbose "Leaving $($MyInvocation.MyCommand)" } # Start-Process Function Stop-Process() { [CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName='ByName')] param ( [string[]] $ComputerName, [string] $ComputerList, [ValidateSet("WMI", "External", "WinRM")] [string] $Method = "WinRM", [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"), [System.Management.Automation.Runspaces.PSSession[]] $Session=$Null, [System.Management.Automation.PSCredential] $Credential=$Null, [switch] $NoRemoteRegistry, [boolean] $OnlineCheck = $false, [Parameter(ParameterSetName='ByName')] [string] $Name, [Parameter(ParameterSetName='ByPid')] [int] $Id, [switch] $StopAll ) $Function = $MyInvocation.MyCommand Write-Verbose "Entering $Function" $returnobject = @() $Arguments = $(if ($Name) { "Name $($Name)" } else { "Pid: $($Id)" } ) $Arguments += ", StopAll: $StopAll" $Arguments += ", Onlinecheck: $OnlineCheck" Write-Verbose "Arguments: $Arguments" if ($PSBoundParameters.ContainsKey('whatif') -and $PSBoundParameters['whatif'].ispresent) { $WhatIfPassed = $true } else { $WhatIfPassed = $false } Write-Verbose "whatif: $WhatIfPassed" if ($PSBoundParameters.ContainsKey('NoRemoteRegistry') -and $PSBoundParameters['NoRemoteRegistry']) { $NoRemoteRegistry = $true } else { $NoRemoteRegistry = $false } Write-Verbose "NoRemoteRegistry $NoRemoteRegistry" if (!$Name -and !$Id) { $Status = "fail" $Reason = "no process name or process id" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments } elseif ($Name -and $Id) { $Status = "fail" $Reason = "both process name or process id given" $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments } else { $targets = Get-Target -ComputerList:$(if ($ComputerList){$ComputerList}) -ComputerName:$(if ($ComputerName){$ComputerName}) foreach ($target in $targets) { Write-Progress -Activity "Running $Function" -Status "Processing $target..." if ($pscmdlet.ShouldProcess($target, "Stop Process with method $($Method)")) { $IsLocalhost = ($target -match "localhost") if (!$IsLocalhost -and $OnlineCheck -and !(Test-Connection $target -Quiet -Count 2)) { Write-Verbose "$target is offline" $Status = "fail" $Reason = "offline" } else { if ($Method -match "wmi") { Write-Verbose "Using WMI" if (($target -eq "localhost") -and $Credential) { $Status = "fail" $Reason = "localhost and WMI and credential not working" $returnobject += New-PowerSponseObject -Function $function -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target Continue } if ($Name) { $res = Get-WmiObject Win32_Process -ComputerName $target -Credential $Credential | ? {$_.name -match $Name} } else { $res = Get-WmiObject Win32_Process -ComputerName $target -Credential $Credential | ? {$_.Processid -eq $Id} } if (!$res) { $Status = "pass" $Reason = "no process" } elseif ((($res | measure).count -gt 1 ) -and !$StopAll) { $Status = "fail" $Reason = "multiple processes without -stopall" } else { $Reason = "Process(es) stopped: " foreach ($proc in $res) { $rval = $proc.terminate().returnvalue if ($rval -eq 0) { $Status = "pass" $Reason += "PID:$($proc.ProcessId)($($proc.ProcessName)) " } elseif ($rval -eq 2) { $Status = "fail" $Reason += "$($proc.ProcessPid)(Access denied)" } else { $Status = "fail" $Reason += "$($proc.ProcessPid)(Unspecified error)" } } } } elseif ($Method -match "winrm") { Write-Verbose "Using WinRM - Name: $Name, Id: $Id" if ($Name) { $ret = Get-Process -ComputerName $target -Method WinRM -Name $Name $returnobject += $ret } else { $ret = Get-Process -ComputerName $target -Method WinRM -Id $Id $returnobject += $ret } if ($ret -and ($ret.reason -match "no process found")) { $Status = "pass" $Reason = "no process" Continue } elseif ($ret -and ($ret.reason | measure).count -gt 1 -and !$StopAll) { $Status = "fail" $Reason = "trying to kill multiple processes without -stopall" } else { $processes = ( $ret.reason | foreach { ($_ -split ";")[0] } ) $params = @{ 'ea' = 'SilentlyContinue' } if ($target -ne "localhost") { $params += @{ 'ComputerName' = $target 'SessionOption' = (New-PSSessionOption -NoMachineProfile) } } if ($Credential) { $params += @{ 'Credential' = $Credential } } $params += @{ 'ScriptBlock' = {param($p1) Microsoft.PowerShell.Management\stop-process -Id $p1 -force -PassThru -ea stop} } $Reason = @() foreach ($proc in $processes) { try { $res = invoke-command @params -ArgumentList $proc $Status = "pass" $Reason += "$proc($($res.ProcessName))" } catch { $Status = "fail" $Reason += "can't kill process $($proc): $($_.Exception.Message)" } } } } elseif ($Method -match "external") { Write-Verbose "Using ExternalTools / Sysinternals" Write-Verbose "BinPath: $BinPath" if (!(Test-Path -Path "$BinPath\pskill.exe")) { $Status = "fail" $Reason = "Binary pskill not found." } elseif (!(Test-Path -Path "$BinPath\pslist.exe")) { $Status = "fail" $Reason = "Binary pslist not found." } elseif (!(Test-Path -Path "$BinPath\psservice.exe")) { $Status = "fail" $Reason = "Binary psservice not found." } else { # Enable RemoteRegistry for the target try { if (!$IsLocalhost -and !$NoRemoteRegistry -or (!$IsLocalhost -and $WhatIfPassed)) { $err = Enable-RemoteRegistry -Method external -ComputerName $target -OnlineCheck:$false -WhatIf:$WhatIfPassed $returnobject += $err $srr = Start-Service -ComputerName $target -Method external -Name "RemoteRegistry" -OnlineCheck:$false -WhatIf:$WhatIfPassed $returnobject += $srr $RemoteRegistryStarted = ($srr.status -match "pass") } else { # assume RemoteRegistry is already started $RemoteRegistryStarted = $true } # pslist - check if multiple processes are running if ($Name) { if ($IsLocalhost) { $proc = Start-Process pslist.exe -CommandLine "-accepteula -nobanner $Name" } else { $proc = Start-Process pslist.exe -CommandLine "-accepteula -nobanner \\$target $Name" } } else { if ($IsLocalhost) { $proc = Start-Process pslist.exe -CommandLine "-accepteula -nobanner $Id" } else { $proc = Start-Process pslist.exe -CommandLine "-accepteula -nobanner \\$target $Id" } } if ($proc.stdout | select-string "not" -quiet) { $Reason = "Process not found" $Status = "fail" } elseif ($proc.stdout -match "Cannot connect" -or $proc.stdout -match "failed") { $Reason = "Failed to connected to remote host" $Status = "fail" } else { Write-Verbose "Process found" $MultipleProcesses = (($proc.stdout | measure -Line).Lines -gt 4) Write-Verbose "Process count: $($MultipleProcesses)" if ($MultipleProcesses -and !$StopAll) { $Reason = "multiple processes found, please use -stopall to stop all processes" $Status = "fail" } else { # pskill if ($Name) { if ($IsLocalhost) { $proc = Start-Process pskill.exe -CommandLine "-accepteula -nobanner $Name" } else { $proc = Start-Process pskill.exe -CommandLine "-accepteula -nobanner \\$target $Name" } } else { if ($IsLocalhost) { $proc = Start-Process pskill.exe -CommandLine "-accepteula -nobanner -t $Id" } else { $proc = Start-Process pskill.exe -CommandLine "-accepteula -nobanner -t \\$target $Id" } } if (($proc.stderr).Contains("verweigert") -or ($proc.stderr).Contains("denied")) { $Reason = "Access denied while stopping" $Status = "fail" } elseif (!($proc.stdout).Contains("Error") -and ($proc.stdout).contains("killed")) { $Reason = "Process stopped." $Status = "pass" } else { $Reason = "Unspecified error." $Status = "fail" } if ($proc.ExitCode -eq 0) { Write-Verbose "Successfully executed pskill on $target" } else { Write-Verbose "Error while running pskill on $target" } } } } catch { Write-Verbose "Error while enabling RemoteRegistry" $Reason = "Error while enabling RemoteRegistry" $Status = "fail" } try { if (!$IsLocalhost -and !$NoRemoteRegistry -and $RemoteRegistryStarted -or (!$IsLocalhost -and $WhatIfPassed)) { Write-Verbose "Cleanup RemoteRegistry" $srr = Stop-Service -ComputerName $target -Method external -Name "RemoteRegistry" -OnlineCheck:$false -WhatIf:$WhatIfPassed $returnobject += $srr $drr = Disable-RemoteRegistry -Method external -ComputerName $target -OnlineCheck:$false -WhatIf:$WhatIfPassed $returnobject += $drr } } catch { Write-Verbose "Error while disabling RemoteRegistry" $Reason = "Error while disabling RemoteRegistry" $Status = "fail" } } #Binaries found } #external } #online $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments -ComputerName $target } # whatif } # each target } # params ok $returnobject Write-Verbose "Leaving $($MyInvocation.MyCommand)" } #Stop-Process |