一个powershell的内网端口扫描工具
# PowerShell TCP Port Scanner - PS 5.1+ compatible
# Usage: powershell -ExecutionPolicy Bypass -File windows_scan.ps1
# Or: powershell -ExecutionPolicy Bypass -Command "& 'C:\console\windows_scan.ps1'"
param(
[string]$Target = "192.168.2.10",
[int]$Threads = 50,
[int]$TimeoutMs = 1200,
[int]$BatchSize = 500,
[int]$BatchDelay = 3,
[string]$OutFile = "C:\console\open.txt",
[string]$LogFile = "C:\console\scan_log.txt"
)
# Must be FIRST - redirect all errors to log file
$ErrorActionPreference = "Stop"
$logDir = Split-Path $LogFile -Parent
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
function Write-Log {
param([string]$Msg, [string]$Color)
$line = "[$(Get-Date -Format 'HH:mm:ss')] $Msg"
# Write-Host may be invisible from cmd.exe; always write to file
$line | Out-File -Append -FilePath $LogFile -Encoding UTF8
if ($Color) { try { Write-Host $line -ForegroundColor $Color } catch {} } else { try { Write-Host $line } catch {} }
try { [Console]::WriteLine($line) } catch {}
}
try {
Write-Log "=== PowerShell TCP Scan ==="
Write-Log "Target: $Target | Threads: $Threads | Batch: $BatchSize | Timeout: ${TimeoutMs}ms"
Write-Log "PSVersion: $($PSVersionTable.PSVersion)"
# Check RunspacePool
try {
$testPool = [runspacefactory]::CreateRunspacePool(1, 2)
$testPool.Open()
$testPool.Close()
$testPool.Dispose()
Write-Log "RunspacePool: OK"
} catch {
Write-Log "RunspacePool FAILED: $_"
exit 1
}
# Quick probe
Write-Log "Quick probe $Target`:445 ..."
try {
$quickTcp = New-Object System.Net.Sockets.TcpClient
$quickAr = $quickTcp.BeginConnect($Target, 445, $null, $null)
if ($quickAr.AsyncWaitHandle.WaitOne(3000)) {
$quickTcp.EndConnect($quickAr)
Write-Log "Port 445: OPEN"
} else {
Write-Log "Port 445: timeout (target may be firewalled)"
}
$quickTcp.Close()
$quickTcp.Dispose()
} catch {
Write-Log "Port 445 probe: $_"
}
$allPorts = 1..65535
$open = New-Object System.Collections.ArrayList
$totalBatches = [math]::Ceiling($allPorts.Count / $BatchSize)
$sw = [System.Diagnostics.Stopwatch]::StartNew()
# Script block for single port scan
$scanScript = {
param($ip, $port, $timeout)
$tcp = $null
try {
$tcp = New-Object System.Net.Sockets.TcpClient
$ar = $tcp.BeginConnect($ip, $port, $null, $null)
if ($ar.AsyncWaitHandle.WaitOne($timeout)) {
$tcp.EndConnect($ar)
return $port
}
} catch {
# Connection refused or other error - port closed
} finally {
if ($tcp) {
try { $tcp.Close() } catch {}
try { $tcp.Dispose() } catch {}
}
}
}
for ($batch = 0; $batch -lt $totalBatches; $batch++) {
$startIdx = $batch * $BatchSize
$endIdx = [math]::Min($startIdx + $BatchSize - 1, $allPorts.Count - 1)
$batchPorts = $allPorts[$startIdx..$endIdx]
$pct = [math]::Round(($batch + 1) / $totalBatches * 100, 1)
Write-Log ("Batch {0}/{1} ({2}%) | Ports {3}-{4} | Found: {5} | Elapsed: {6}s" -f
($batch + 1), $totalBatches, $pct,
$batchPorts[0], $batchPorts[-1], $open.Count, [int]$sw.Elapsed.TotalSeconds)
$pool = [runspacefactory]::CreateRunspacePool(1, $Threads)
$pool.Open()
$jobs = @()
foreach ($p in $batchPorts) {
$ps = [powershell]::Create().AddScript($scanScript).AddArgument($Target).AddArgument($p).AddArgument($TimeoutMs)
$ps.RunspacePool = $pool
$jobs += [PSCustomObject]@{ Pipe = $ps; Result = $ps.BeginInvoke() }
if ($jobs.Count % $Threads -eq 0) {
Start-Sleep -Milliseconds 100
}
}
foreach ($j in $jobs) {
try {
$port = $j.Pipe.EndInvoke($j.Result)
if ($port) {
Write-Log "Port $port OPEN" "Green"
$open.Add($port)
}
} catch {
# EndInvoke exception - skip
} finally {
try { $j.Pipe.Dispose() } catch {}
}
}
$pool.Close()
$pool.Dispose()
[GC]::Collect()
if ($batch -lt $totalBatches - 1) {
Start-Sleep -Seconds $BatchDelay
}
}
$sw.Stop()
# Save results
$sorted = $open.ToArray() | Sort-Object { [int]$_ }
$sorted | Out-File $OutFile -Encoding UTF8
Write-Log "=== Done: $($sorted.Count) ports open | $([int]$sw.Elapsed.TotalSeconds)s ==="
Write-Log "Results saved: $OutFile"
Write-Log "Open ports:"
foreach ($p in $sorted) {
Write-Log " $p"
}
if ($sorted.Count -eq 0) {
Write-Log "No open ports found. Try from different host or lower threads/delay."
}
} catch {
Write-Log "FATAL ERROR: $_"
Write-Log "Stack: $($_.ScriptStackTrace)"
exit 1
}
powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File c:\windows\temp\1.ps1
BatchSize 只是分批大小,每个端口的扫描精度由 TimeoutMs(800ms)决定,跟批次大小无关。
改 200 只会让总时间变长(328 批 vs 132 批,多了 196 次 BatchDelay 3 秒 ≈ 多 10 分钟),对准确度没任何提升。
想提高准确度的话,应该调大 TimeoutMs,比如改成 1200ms 甚至 1500ms。代价是每批耗时更久,但丢包/漏端口的概率更低。
浙公网安备 33010602011771号