websec80

  博客园  :: 首页  :: 新随笔  :: 联系 ::  :: 管理

一个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。代价是每批耗时更久,但丢包/漏端口的概率更低。

 

posted on 2026-05-26 16:40  websec80  阅读(8)  评论(0)    收藏  举报