functions/Process.ps1

Function Invoke-PsExec()
{
    [CmdletBinding(SupportsShouldProcess=$True)]
    param
    (
        [ValidateSet("External")]
        [string] $Method = "External",
        [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"),
        [string] $Binary,
        [string] $CommandLine
    )

    Throw "not implemented yet"
    return

    # check binary availablity

    # enable RemoteRegistry
    # start RemoteRegistry
    
    # execute supplied command
    $ret = Start-Process psexec.exe -CommandLine "-nobanner -accepteula $Binary $CommandLine"
    if ( $ret.ExitCode -eq 0 )
    {
        $ret.stdout
        $ret.stderr
    }

    # stop RemoteRegistry
    # disable RemoteRegistry
}

Function Get-Process()
{
    [CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName='BySearchString')]
    param
    (
        [string[]] $ComputerName,

        [string] $ComputerList,

        [ValidateSet("WMI", "External")]
        [string] $Method = "WMI",

        [string] $BinPath = $(Join-Path -Path $ModuleRoot -ChildPath "\bin"),

        [System.Management.Automation.Runspaces.PSSession[]] $Session=$Null,

        [System.Management.Automation.PSCredential] $Credential=$Null,

        [Parameter(ParameterSetName='BySearchString')]
        [string] $SearchString,

        [Parameter(ParameterSetName='ByPid')]
        [int] $Pid,

        [ValidateSet("OnlyPid", "OnlyName", "All")]
        [string] $OutputFormat = "all"
    )

    $Function = $MyInvocation.MyCommand
    Write-Verbose "Entering $Function"

    $returnobject = @()

    $Arguments = $(if ($SearchString) { "SearchString $($SearchString)" } else { "Pid: $($Pid)" } )

    Write-Progress -Activity "Running $Function" -Status "Initializing..."

    if (!$SearchString -and !$Pid)
    {
        $Reason = "no process name or process id"
        $Status = "fail"
        $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments
    }
    elseif ($SearchString -and $Pid)
    {
        $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 ($SearchString)
                            {
                                $res = Get-WmiObject Win32_Process -ComputerName $target -Credential $Credential -ErrorAction Stop | ? {$_.name -match $SearchString -or $_.ExecutablePath -match $SearchString}
                            }
                            else
                            {
                                $res = Get-WmiObject Win32_Process -ComputerName $target -Credential $Credential -ErrorAction stop | ? {$_.Processid -eq $Pid}
                            }

                            if (!$res)
                            {
                                $Status = "fail"
                                $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")
                    {
                    }
                    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 ($SearchString)
                                    {
                                        $proc = Start-Process "pslist.exe" "-accepteula -nobanner \\$target $SearchString"
                                    }
                                    else
                                    {
                                        $proc = Start-Process "pslist.exe" "-accepteula -nobanner \\$target $Pid"
                                    }

                                    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")]
        [string] $Method = "WMI",

        [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 = $true,

        [Parameter(ParameterSetName='ByName')]
        [string] $Name,

        [Parameter(ParameterSetName='ByPid')]
        [int] $Pid,

        [switch] $StopAll

    )

    $Function = $MyInvocation.MyCommand
    Write-Verbose "Entering $Function"

    $returnobject = @()

    $Arguments = $(if ($Name) { "Name $($Name)" } else { "Pid: $($Pid)" } )
    $Arguments += ", StopAll: $StopAll"
    $Arguments += ", Onlinecheck: $OnlineCheck"
    Write-Verbose "Arguments: $Arguments"

    if ($PSBoundParameters.ContainsKey('whatif') -and $PSBoundParameters['whatif'])
    {
        $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 !$Pid)
    {
         $Status = "fail"
         $Reason = "no process name or process id"
         $returnobject += New-PowerSponseObject -Function $Function -Status $Status -Reason $Reason -Arguments $Arguments
    }
    elseif ($Name -and $Pid)
    {
         $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 $Pid}
                        }

                        if (!$res)
                        {
                            $Status = "fail"
                            $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")
                    {
                    }
                    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 $Pid"
                                    }
                                    else
                                    {
                                        $proc = Start-Process pslist.exe -CommandLine "-accepteula -nobanner \\$target $Pid"
                                    }
                                }

                                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 $Pid"
                                            }
                                            else
                                            {
                                                $proc = Start-Process pskill.exe -CommandLine "-accepteula -nobanner -t \\$target $Pid"
                                            }
                                        }

                                        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