想寫這個工具很久了,因緣成熟,花了點時間做出來。

遇到 Windows 在 Listen 某個 Port,若想知道是哪支程式使用中,簡單做法是 netstat -ano | findstr /i "listening" 得到 Process Id (PID),再用 PID 去查程序資料,若 Port 是被 IIS 站台繫結,PID 會指向系統(PID = 4),要用 IIS 管理員或 CLI 查。我想要寫一段 PowerShell 一口氣查完所需資訊,使用時複製貼上執行,一次得到完整答案。

程式原理不複雜,主體是 Get-NetTCPConnection -State Listen 查詢繫結中的 Port 及其 PID,用 Get-Process -Id <PID> 查詢程序名稱及路徑,實測 Get-Process 不太抓得到執行檔路徑,而 IIS 使用的 Port 會掛給 PID=4 System,我的解法是另外呼叫 Get-WmiObject Win32_Process 取得 PID 的程式檔路徑,用Get-ChildItem -Path "IIS:\Sites" 建立站台對繫結 IP 及 Port 的對照表,雙管齊下補足資訊。
註:學到新知識,:: 在 IPv6 代表一串零,等同 IPv4 0.0.0.0,用於 LISTEN 時代表所有本機 IP 位址。

另外,發現 Get-WmiObject Win32_Process 及 Get-ChildItem -Path "IIS:\Sites" 需使用管理者權限執行才能讀取完整資訊,借用之前學過判斷管理者身分技巧,提示而要管理者權限才能查看詳細資訊。

程式範例如下:

$dict = @{}
Get-WmiObject Win32_Process | ForEach-Object {
    $path = $_.ExecutablePath
    if (!$path) {
        $path = $_.Name
    }
    $dict['P' + $_.ProcessId] = $path + ' ' + $_.CommandLine
}
$wp = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
$iisInfo = @{}
if (-Not $wp.IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
    Write-Host "Need administrator privileges to show more information." -ForegroundColor Yellow
}
else {
    Import-Module WebAdministration
    Get-ChildItem -Path "IIS:\Sites" | ForEach-Object {
        $siteName = $_.Name
        $_.Bindings | ForEach-Object {
            $_.Collection | ForEach-Object {
                $p = $_.bindingInformation.Split(':')
                $socket = $p[0].Replace('*', '::') + ':' + $p[1]
                if ($p[2]) {
                    $bindHost = "(Host=$($p[2])) "
                }
                $iisInfo[$socket] += 'IIS:\' + $siteName + $bindHost
            }
        }
    }
}
Get-NetTCPConnection -State Listen | Sort-Object { $_.LocalPort } |
ForEach-Object {
    $process = Get-Process -Id $_.OwningProcess
    $procPath = $process.Path
    $socket = "$($_.LocalAddress):$($_.LocalPort)"
    if (!$procPath) {
        if ($iisInfo[$socket]) {
            $procPath = $iisInfo[$socket]
        }
        else {
            $procPath = $dict['P' + $process.Id]
        }
    }
    [PSCustomObject]@{
        ProcessName = $process.Name
        PID         = $process.Id
        Socket      = "$($_.LocalAddress):$($_.LocalPort)"
        Path        = $procPath
    }
}

實測一下。用一般權限看不到 IIS Port 及檔案路徑:

使用管理者權限執行,資訊會完整一些。

有了這個小工具,未來要查 Port 使用狀況方便多了。

A handy PowerShell script to list all listening port witht process info.


Comments

# by miga

多了一個斷行 else : 無法辨識 'else' 詞彙是否為 Cmdlet

# by miga

喔~不對 是因為我直接貼上powershell的緣故

# by max866

謝謝分享,這對檢查end user 連線問題很有幫助

# by ChrisTorng

Sysinternal 的 TCPView 有這個功能: https://learn.microsoft.com/en-us/sysinternals/downloads/tcpview 。我常用它來強迫關指定連線 (右鍵 - Close Connection)

# by Jeffrey

to ChrisTorng,謝分享。好用的工具軟體不少,但 PowerShell 能深入許多 EXE 工具難以深入的環境,就像某些地形就只有搜救人員到得到了。

Post a comment