PowerShell: Get Ports to Process Connections / Associations

Current Iteration:

Here is a quick snippet to enable Network Engineers and Systems dudes to gather connection info on local or remote Windows.

# Sample Output

# Checking connections on port(s) 80 443 8080 8443...
# Checking connections on for process name(s): chrome...
# Process names have not been defined. Program now scans 7 processes...

# ProcessName  PID Protocol SourceEndPoint     DestinationEndpoint ConnectionStatus processOwner
# -----------  --- -------- --------------     ------------------- ---------------- ------------
# chrome      5896 TCP      10.10.10.500:51788 10.10.10.500:8080   ESTABLISHED      KIMCONNECT\rambo
# chrome      5896 TCP      10.10.10.500:51789 10.10.10.500:8080   ESTABLISHED      KIMCONNECT\rambo
# chrome      5896 TCP      10.10.10.500:51793 10.25.1.1800:443    ESTABLISHED      KIMCONNECT\rambo
# chrome      5896 TCP      10.10.10.500:51794 10.25.1.1800:443    ESTABLISHED      KIMCONNECT\rambo
# getProcessConnections.ps1
# version 0.0.3
#
# Description:
#   This script will connect to a list of Windows machines to collect processes and their ports utilizations
#   This iteration includes information on the process owner(s)
#   Program has been slightly optimized
#
# Requirements:
#   WinRM connectivity is expected toward the list of computer names

$computerNames="$env:computername"
$processNames='chrome'
$portNumbers=@()

function invokeGetProcessPorts{
    [cmdletbinding()]
    Param(
        [string[]]$computerNames=$env:computername,
        [string[]]$processNames=$null,
        [string[]]$portNumbers=$null
        )
    function getProcessConnections{
        [cmdletbinding()]
        Param(
            [parameter(ValueFromPipeLine=$True)][AllowEmptyCollection()][string[]]$processNames=$null,
            [parameter(ValueFromPipeLine=$True)][AllowEmptyCollection()][string[]]$portNumbers=$null
            )
        # Initialize variables    
        $results=@()
        $psVersionFeasible=$PSVersionTable.PSVersion.Major -ge 4
        $processes=if($processNames){
                write-host "Checking connections on for process name(s): $processNames..." 
                if($psVersionFeasible){
                        try{                
                            get-process $processNames -IncludeUserName # This is only available in PoSh 4.0+ 
                        }catch{
                            write-warning $_
                        }
                    }else{
                        get-process $processNames
                        }
            }else{
                if($psVersionFeasible){               
                    get-process -IncludeUserName
                }else{
                    get-process
                }
            }
        
        if($processes){
            write-host "Checking $($processes.count) processes..." -ForegroundColor Yellow
            # Collecting wmiobjects to be able to invoke .getowner() method
            $processObjects=if(!$psVersionFeasible){Get-WmiObject Win32_Process}else{$null}
            $netStat=if($portNumbers){
                    write-host "Checking connections on port(s) $portNumbers..."
                    netstat -ano|?{$_ -match "\:($($portNumbers -join '|'))\s"}
                }else{
                    write-host 'Port numbers were not defined. Scanning all ports...' -ForegroundColor Yellow
                    Netstat -ano|?{$_ -match "\d$"}
                }
            $previousPid=$previousOwner=$null
            write-verbose "Process ID:"
            foreach($process in $processes){            
                $processId=$process.ID
                write-verbose "$processId"
                $processName=$process.ProcessName
                $processOwner=if($processId -ne $previousPid){
                    $previousOwner=if($processObjects){
                            $processObjects|?{$_.ProcessId -eq $processId}|%{if($_.GetOwner().User){$_.GetOwner().Domain+"\"+$_.GetOwner().User}else{'unknown'}}|select -unique
                        }else{
                            $process.UserName
                        }
                    $previousOwner
                }else{
                    $previousOwner
                }
                $matchedLines = $netStat|findstr $processId
                write-host "Process Id $processId matched $($matchedLines.count) lines"
                foreach($matchedLine in $matchedLines){                    
                    $line = $matchedLine.Split('') | where{$_ -ne ""} # remove empty lines
                    $leftCount = $line[1].LastIndexOf(':')
                    $rightCount = $line[2].LastIndexOf(':')
                    $sourceEndpoint=$line[1].SubString(0,$leftCount)+':'+$line[1].SubString($leftCount+1,($line[1].Length-$leftCount-1))
                    $destinationEndpoint=$line[2].SubString(0,$rightCount)+':'+$line[2].SubString($rightCount+1,($line[2].Length-$rightCount-1))
                    $results += [PSCustomObject]@{              
                        ComputerName = $env:computername
                        ProcessName  = $processName
                        PID = $processId
                        Protocol = $line[0]
                        SourceEndPoint = $sourceEndpoint
                        DestinationEndpoint = $destinationEndpoint
                        #LocalAddress = $line[1].SubString(0,$leftCount)
                        #LocalPort = $line[1].SubString($leftCount+1,($line[1].Length-$leftCount-1))
                        #RemoteAddress = $line[2].SubString(0,$rightCount)
                        #RemotePort = $line[2].SubString($rightCount+1,($line[2].Length-$rightCount-1))
                        ConnectionStatus = $(if(!($line[3] -match '\d')){$line[3]}) # Checking if the connection contains any empty string.
                        processOwner=$processOwner
                    }
                }
                $previousPid=$processId
            }            
        if($results){
            return $results
            }elseif($processNames -and $portNumbers){
                write-host "$processNames not found on $portNumbers" -ForegroundColor Yellow
                return $null                
            }else{
                write-host "No processes matched." -ForegroundColor Yellow
                return $null
            }
        }else{
            write-host "No processes matched." -ForegroundColor Yellow
            return $null
        }
    }

    $results=@()
    foreach ($computerName in $computerNames){
        $session=New-PSSession -ComputerName $computerName
        if($session.state -eq 'Opened'){
            $result=invoke-command -ComputerName $computerName -scriptblock{
                param($importFunc,$x,$y)
                write-host "Executing function on $env:computername"
                [scriptblock]::create($importFunc).invoke($x,$y)
            } -args ${function:getProcessConnections},$processNames,$portNumbers
            if($result){
                $results+=$result
            }else{
                write-host "No matches on $computerName"
            }
            Remove-PSSession $session
        }else{
            write-warning "Unable to connect to $computername"
        }
    }
    return $results
}

$resultArray=invokeGetProcessPorts $computerNames $processNames $portNumbers
$resultArray|ft

Previous Iterations:

# getProcessConnections.ps1
# version 0.0.2
#
# Description:
#   This script will connect to a list of Windows machines to collect processes and their ports utilizations
#   This iteration includes information on the process owner(s)
#   Program needs to be further optimized
#
# Requirements:
#   WinRM connectivity is expected toward the list of computer names

$computerNames=$env:computername
$processNames='chrome'
$portNumbers=@(80,443,8080,8443)
function getProcessConnections{
    [cmdletbinding()]
    Param(
        [parameter(ValueFromPipeLine=$True)][AllowEmptyCollection()][string[]]$processNames=$null,
        [parameter(ValueFromPipeLine=$True)][AllowEmptyCollection()][string[]]$portNumbers=$null
        )
    # Initialize variables    
    $results = @()
    $netStat = if(!$portNumbers){
            write-host 'Port numbers were not defined. Program now scans all ports...' -ForegroundColor Yellow
            Netstat -ano
        }else{
            write-host "Checking connections on port(s) $portNumbers..."
            netstat -ano|?{$_ -match "\:($($portNumbers -join '|'))\s"}
        }
    $processes=if($processNames){
        write-host "Checking connections on for process name(s): $processNames..."         
            Get-Process $processNames
        }else{                
            get-process
        }

    if($processes){
        write-host "Process names have not been defined. Program now scans $($processes.count) processes..." -ForegroundColor Yellow
        foreach($process in $processes){
            $processOwner=Get-WmiObject Win32_Process -Filter "ProcessId='$($process.ID)'"|%{if($_.GetOwner().User){$_.GetOwner().Domain+"\"+$_.GetOwner().User}else{'unknown'}}|select -unique
            $matchedLines = $netStat|findstr $process.ID
            foreach($matchedLine in $matchedLines){                    
                $line = $matchedLine.Split('') | where{$_ -ne ""} # remove empty lines
                $leftCount = $line[1].LastIndexOf(':')
                $rightCount = $line[2].LastIndexOf(':')
                $sourceEndpoint=$line[1].SubString(0,$leftCount)+':'+$line[1].SubString($leftCount+1,($line[1].Length-$leftCount-1))
                $destinationEndpoint=$line[2].SubString(0,$rightCount)+':'+$line[2].SubString($rightCount+1,($line[2].Length-$rightCount-1))
                $results += [PSCustomObject]@{              
                    ProcessName  = $process.Name
                    PID = $process.ID
                    Protocol = $line[0]
                    SourceEndPoint = $sourceEndpoint
                    DestinationEndpoint = $destinationEndpoint
                    #LocalAddress = $line[1].SubString(0,$leftCount)
                    #LocalPort = $line[1].SubString($leftCount+1,($line[1].Length-$leftCount-1))
                    #RemoteAddress = $line[2].SubString(0,$rightCount)
                    #RemotePort = $line[2].SubString($rightCount+1,($line[2].Length-$rightCount-1))
                    ConnectionStatus = $(if(!($line[3] -match '\d')){$line[3]}) # Checking if the connection contains any empty string.
                    processOwner=$processOwner
                }
            }
        }            
    if($results){
        return $results|ft -AutoSize
    }elseif($processNames -and $portNumbers){
        write-host "$processNames not found on $portNumbers" -ForegroundColor Yellow
    }else{
        write-host "No processes matched." -ForegroundColor Yellow
    }
    }else{
        write-host "No processes matched." -ForegroundColor Yellow
        return $null
    }    
}

foreach ($computerName in $computerNames){
        invoke-command -ComputerName $computerName -scriptblock{
        param($importFunc,$x,$y)
        [scriptblock]::create($importFunc).invoke($x,$y)
    } -args ${function:getProcessConnections},$processNames,$portNumbers
}
# getProcessConnections.ps1
# version 0.0.1
# This function is expected to run on a Windows localhost session

function getProcessConnections{
    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$False, ValueFromPipeLine=$True)][AllowEmptyCollection()]
        [string[]]$processNames=$null,
        [string[]]$portNumbers=$null
    )
    Begin{    
        $results = @()
        $netStat = if(!$portNumbers){
                write-host 'Port numbers were not defined. Program now scans all ports...' -ForegroundColor Yellow
                Netstat -ano
            }else{
                write-host "Checking connections on port(s) $portNumbers..."
                netstat -ano|?{$_ -match "\:($($portNumbers -join '|'))\s"}
            }
        $processes=if($processNames){
            write-host "Checking connections on for process name(s): $processNames..."         
                Get-Process $processNames
            }else{
                write-host 'A process name has not been defined. Program now scans all processes...' -ForegroundColor Yellow
                get-process
            }
    }Process{
        if($processes){
            foreach($process in $processes){
                $matchedPorts = $netStat | findstr $process.ID
                foreach($matchedPort in $matchedPorts){                    
                    $line = $matchedPort.Split('') | where{$_ -ne ""} # remove empty lines
                    $leftCount = $line[1].LastIndexOf(':')
                    $rightCount = $line[2].LastIndexOf(':')
                    $sourceEndpoint=$line[1].SubString(0,$leftCount)+':'+$line[1].SubString($leftCount+1,($line[1].Length-$leftCount-1))
                    $destinationEndpoint=$line[2].SubString(0,$rightCount)+':'+$line[2].SubString($rightCount+1,($line[2].Length-$rightCount-1))
                    $results += [PSCustomObject]@{              
                        ProcessName  = $process.Name
                        PID = $process.ID
                        Protocol = $line[0]
                        SourceEndPoint = $sourceEndpoint
                        DestinationEndpoint = $destinationEndpoint
                        #LocalAddress = $line[1].SubString(0,$leftCount)
                        #LocalPort = $line[1].SubString($leftCount+1,($line[1].Length-$leftCount-1))
                        #RemoteAddress = $line[2].SubString(0,$rightCount)
                        #RemotePort = $line[2].SubString($rightCount+1,($line[2].Length-$rightCount-1))
                        ConnectionStatus = $(if(!($line[3] -match '\d')){$line[3]}) # Checking if the connection contains any empty string.
                    }
                }
            }            
        return $results|ft -AutoSize
        }else{
            write-host "No processes matched." -ForegroundColor Yellow
            return $null
        }
    }
}

getProcessConnections -portNumbers 80,443
getProcessConnections -processNames chrome

Leave a Reply

Your email address will not be published. Required fields are marked *