PowerShell: Chicken Scratch Ports Scanner

Although there are optimized applications to perform this task such as Angry Port Scanner, NMAP, Advanced Port Scanner, etc, I believe that PowerShell is convenient and this implementation is adequate for practical use. This bypasses the need to install additional applications on the host machine.

# checkPorts.ps1
# Version 0.0.3
# This function uses portqry to test connections toward a list of remote servers.

$remoteComputers=@(
  'server1',
  'server2'
)

$ports=@( 
    # RPC client-server communication
    [PSCustomObject] @{protocol='tcp';port=135},

    # SMB 2.0 & CIFS
    # [PSCustomObject] @{protocol='udp';port=137}, # Netapps SMB1/CIFS Legacy NetBios/Name resolution
    # [PSCustomObject] @{protocol='udp';port=138}, # Netapps SMB1/CIFS Legacy NetBios/Name resolution
    [PSCustomObject] @{protocol='tcp';port=139}, # Netapps CIFS NetBios/Name resolution
    [PSCustomObject] @{protocol='tcp';port=445},
  
    # WinRM
    [PSCustomObject] @{protocol='tcp';port=5985}
    #[PSCustomObject] @{protocol='tcp';port=5986},

    # MS SQL Default
    #[PSCustomObject] @{protocol='tcp';port=1433},
  
    # Dynamic port range
    #[PSCustomObject] @{protocol='tcp';port=49152},
    #[PSCustomObject] @{protocol='tcp';port=65535}
)
  
function checkPorts($servers,$ports){
  function includePortQry{
      if (!(Get-Command portqry.exe -ErrorAction SilentlyContinue)){
          if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
          Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))}
          choco install portqry -y
          if (Get-Command portqry.exe -ErrorAction SilentlyContinue){return $true}else{return $false}
      }else{
          return $true
      }
  }
  $portQryExists=includePortQry
  if(!$portQryExists){
    write-warning "Unable to proceed without portqry"
    return $false
  }
  $results=@()
  foreach ($remoteComputer in $servers){
    #$ip=[System.Net.Dns]::GetHostAddresses($remoteComputer).IPAddressToString|select -first 1
    write-host "Now scanning $remoteComputer"
    $result=@()
    foreach ($item in $ports){
        $port=$item.port
        $protocol=$item.protocol        
        $reachable=if($port -ne 135){                
                $command={
                    param($remoteComputer,$protocol,$port)
                    $command="portqry -n $remoteComputer -p $protocol -e $port|find ': LISTENING'"
                    invoke-expression $command
                }
                $jobId=(Start-Job $command -Args $remoteComputer,$protocol,$port).Id
                $startCount=0;$waitSeconds=5
                do{
                    $jobStatus=(get-job -id $jobId).State
                    if($jobStatus -eq 'Completed'){
                        $jobResult=receive-job $jobId
                    }else{
                        if($startCount++ -eq $waitSeconds){
                            $jobStatus='Completed'
                            $null=remove-job -id $jobId -force
                            $jobResult=$false
                        }else{
                            sleep 1
                        }                        
                    }
                }until($jobStatus -eq 'Completed')
                [bool]($jobResult)
            }else{
                [bool](portqry -n $remoteComputer -p $protocol -e $port|find 'Total endpoints found: ')
            }       
        write-host "$port/$protocol : $reachable"
        $result+=[PSCustomObject]@{
            computername=$remoteComputer
            port=$port
            protocol=$protocol
            reachable=$reachable
        }
    }
    #$resultString=$results.GetEnumerator()|sort-object {[int]$_.Name}|out-string
    $results+=$result
    $resultString=$result|sort-object -property port|out-string
    write-host $resultString
  }
  return $results
}

$scanResults=checkPorts $remoteComputers $ports
write-output $scanResults
# checkAllPorts.ps1
# Version 0.02
# This function scans all ports at the target and return an Array of sorted dictionary entries
# Multi-threading has been included in this iteration

function checkAllPorts($target='portquiz.net',[int16[]]$ports,$maxThreads=8){
    
    function connectionTest($computerName='portquiz.net',$port,$timeout,$verbose){
        $result=@{}
        function Check-NetConnection($computername,$port,$timeout=1000,$verbose=$false) {
                $tcp = New-Object System.Net.Sockets.TcpClient;
                try {
                    $connect=$tcp.BeginConnect($computername,$port,$null,$null)
                    $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
                    if(!$wait){
                        $null=$tcp.EndConnect($connect)
                        $tcp.Close()
                        if($verbose){
                            Write-Host "Connection Timeout" -ForegroundColor Red
                            }
                        Return $false
                    }else{
                        $error.Clear()
                        $null=$tcp.EndConnect($connect) # Dispose of the connection to release memory
                        if(!$?){
                            if($verbose){
                                write-host $error[0].Exception.Message -ForegroundColor Red
                                }
                            $tcp.Close()
                            return $false
                            }
                        $tcp.Close()
                        Return $true
                    }
                } catch {
                    return $false
                }
        }
        $success=Check-NetConnection $computerName $port
        $result."$port"=$success
        return $result
    }

    function getResults($wait=$false){
        if($results){Clear-Variable results}
        while(!$results){
            $results=if($wait){Get-Job|Receive-Job -wait}else{Get-Job|Receive-Job}
            }
        remove-job -State Completed
        if($results){
            $results|%{
                IF ($_.Values -eq $true){
                    Write-Host "$($_.Keys)`t: open" -ForegroundColor Green
                }else{
                    Write-Host "$($_.Keys)`t: closed" -ForegroundColor Red
                    }
                }
            return $results
        }else{
            return $false
            }
    }

    $allResults=@{}
    if($ports){
        $allPorts=$ports
    }else{
        $allPorts = 1..65535
        }

    $i=0
    foreach ($port in $allPorts){
        if($i++ -lt $maxThreads){
            $null=start-job -ScriptBlock{
                param($connectionTest,$target,$port)
                return [ScriptBlock]::Create($connectionTest).invoke($target,$port)
                } -Args ${function:connectionTest},$target,$port
        }else{
            do{
            $xHashes=getResults
            $xHashes|%{$allResults+=$_}
            $i=(get-job -state Running).count
            }until($i -lt $maxThreads)
            $null=start-job -ScriptBlock{
                param($connectionTest,$target,$port)
                return [ScriptBlock]::Create($connectionTest).invoke($target,$port)
                } -Args ${function:connectionTest},$target,$port
        }
    }

    if((get-job -state Running).count){
        $xHashes=getResults -wait $true
        $xHashes|%{$allResults+=$_}
        get-job|remove-job
        }
    return $allResults.getenumerator()|Sort-Object {[int]$_.Name}
}

$portScanResult=checkAllPorts -port $(1..20)
# checkAllPorts.ps1
# This function scans all ports at the target and return a hash table with the results

function checkAllPorts($target='portquiz.net',$ports){
    function Check-NetConnection($computername,$port,$timeout=200,$verbose=$false) {
            $tcp = New-Object System.Net.Sockets.TcpClient;
            try {
                $connect=$tcp.BeginConnect($computername,$port,$null,$null)
                $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
                if(!$wait){
                    $null=$tcp.EndConnect($connect)
                    $tcp.Close()
                    if($verbose){
                        Write-Host "Connection Timeout" -ForegroundColor Red
                        }
                    Return $false
                }else{
                    $error.Clear()
                    $null=$tcp.EndConnect($connect) # Dispose of the connection to release memory
                    if(!$?){
                        if($verbose){
                            write-host $error[0].Exception.Message -ForegroundColor Red
                            }
                        $tcp.Close()
                        return $false
                        }
                    $tcp.Close()
                    Return $true
                }
            } catch {
                return $false
            }
    }
    $results=@{}
    if($ports){
        $allPorts=$ports
    }else{
        $allPorts = 1..65535
        }
    foreach ($port in $allPorts) {
        $isPortOpen=Check-NetConnection -computername $target -Port $port
        $results."$port"=$isPortOpen
        IF ($isPortOpen){
            Write-Host "$port`t: open" -ForegroundColor Green
        }else{
            Write-Host "$port`t: closed" -ForegroundColor Red
            }
    }
    return $results
}

Leave a Reply

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