MiGro
MiGro

Reputation: 511

netstat -b equivalent with Powershell

I am looking for equivalent to netstat -b written in Powershell.

Microsoft writes:

Displays the executable involved in creating each connection or listening port. In some cases well-known executables host multiple independent components, and in these cases the sequence of components involved in creating the connection or listening port is displayed. In this case the executable name is in [] at the bottom, on top is the component it called, and so forth until TCP/IP was reached. Note that this option can be time-consuming and will fail unless you have sufficient permissions.

(https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netstat)

For example the EventLog listening port is display with netstat -anb like this, stating that the process name is svchost.exe and the calling component is EventLog.

  TCP    0.0.0.0:49665          0.0.0.0:0              LISTENING
  EventLog
  [svchost.exe]

This Powershell function outputs all listening processes:

function Get-ListeningProcesses {

    Get-NetTCPConnection -State Listen |
    ForEach-Object {
        $ProcessName = (Get-Process -Id $_.OwningProcess).ProcessName
        [PSCustomObject]@{
            LocalAddress = $_.LocalAddress
            LocalPort = $_.LocalPort
            OwningProcess = $_.OwningProcess
            ProcessName = $ProcessName
        }
    }
}

The output will be:

LocalAddress LocalPort OwningProcess ProcessName
------------ --------- ------------- -----------
...
::               49665          1412 svchost    
...

But how can I get the component that started svchost.exe?

Upvotes: 1

Views: 141

Answers (2)

Doug Maurer
Doug Maurer

Reputation: 8868

We can take the desired output from netstat and parse it into objects.

$stats = netstat -anob | out-string

$statlist = [regex]::Matches($stats,'(?s)(TCP|UDP).+?(Can not.+?|])(?=\r?\n)').value

$results = foreach($entry in $statlist ){
    switch -Regex ($entry -split '\r?\n'){
        '(?<Protocol>TCP|UDP)\s+(?<LocalAddr>\S+?)\s+(?<ForeignAddr>\S+?)\s+(?<State>\S+)?\s+?(?<PID>\d+)$' {
            if($current){
                $current.Protocol += $matches.protocol
                $current.LocalAddr += $matches.LocalAddr
                $current.ForeignAddr += $matches.ForeignAddr
                $current.State += $matches.State
                $current.PID += $matches.PID
            }
            else{
                $current = @{}
                'Protocol', 'LocalAddr', 'ForeignAddr', 'State', 'Pid' | ForEach-Object{
                    $current.$_ = @()
                    $current.$_ += $matches.$_
                }
                $current.Parent = ''
                $current.Executable = ''
            }
        }
        '^\s{2}(?<Parent>[\S|^\.|^:]+)$' {
            if($current.Parent){
                $current.Parent = $current.Parent,$matches.parent -join ' -> '
            }
            else{
                $current.Parent = $matches.parent
            }
        }
        '^\s(\[(?<Exe>\S+)\]|(?<Exe>Can not obtain ownership information))'{
            for($i = 0;$i -lt $current.Protocol.Count; $i++){
                [PSCustomObject]@{
                    Protocol    = $current.Protocol[$i]
                    LocalAddr   = $current.LocalAddr[$i]
                    ForeignAddr = $current.ForeignAddr[$i]
                    Executable  = $matches.Exe
                    Parent      = $current.Parent
                    State       = $current.State[$i]
                    ProcessId   = $current.PID[$i]
                }
            }
            $current = $null
        }
    }
}

You can view the data interactively

$results | Out-GridView

Export to csv

$results | Export-CSV path\to\csv

If you know the name of the parent you can filter

PS C:\>     $results | Where-Object Parent -like '*TermService*' | Format-Table *

Protocol LocalAddr    ForeignAddr Executable  Parent      State     ProcessId
-------- ---------    ----------- ----------  ------      -----     ---------
TCP      0.0.0.0:3389 0.0.0.0:0   svchost.exe TermService LISTENING 1612
TCP      [::]:3389    [::]:0      svchost.exe TermService LISTENING 1612
UDP      0.0.0.0:3389 *:*         svchost.exe TermService           1612
UDP      [::]:3389    *:*         svchost.exe TermService           1612

Upvotes: 1

MiGro
MiGro

Reputation: 511

Not exactly what I am was looking for but it serves my needs right now. But the following script lists all public listening processes including the command line.

function Get-ListeningProcesses {

    Get-NetTCPConnection -State Listen | Where-Object {($_.LocalAddress -eq "::" -or $_.LocalAddress -eq "0.0.0.0")} |
    ForEach-Object {
        $ProcessId = $_.OwningProcess
        $ProcessName = (Get-Process -Id $_.OwningProcess).ProcessName
        $CommandLine = (Get-WmiObject Win32_process -filter "ProcessId = '$ProcessId'").CommandLine
        [PSCustomObject]@{
            LocalAddress = $_.LocalAddress
            LocalPort = $_.LocalPort
            RemoteAddress = $_.RemoteAddress
            RemotePort = $_.RemotePort
            OwningProcess = $_.OwningProcess
            ProcessName = $ProcessName
            CommandLine = $CommandLine
        }
    }
}

The script has to be run with elevated rights (as an Administrator).

Upvotes: 1

Related Questions