Port scan subnets with PSnmap for PowerShell

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

Here goes my fairly polished attempt at a PowerShell nmap-like program that port scans subnets using CIDR notation or a pre-generated list of IP addresses or computer names. It uses efficient runspaces for concurrency (but Linux nmap should be way faster - sad face). This script should definitely replace this old port scan script that I wrote in a former life, eons ago. It also uses PSipcalc under the hood.

It has now been published to the PowerShell gallery (see the download section) and feedback is welcome there. It has been out in the wild for a couple of years now and not much feedback has been produced. It seems people are pretty happy with it?

When you run it, it will first perform a ping sweep of the specified hosts/IPs/networks - without giving any feedback - the progress bar comes when DNS lookups and port scans begin. Only alive hosts will be port scanned, unless you specify the parameter -ScanOnPingFail, which will make it scan the port(s) on all hosts regardless of ping status.

You can use the -Verbose parameter - to have your screen flooded with activity most of the time.

This program is not perfect for runs against single hosts. There is some overhead to support faster execution against many hosts. But really it's just a matter of 3.5 seconds of sleep time plus otherwise minimal overhead, even with a single host.

To just check for an open port on a single host, you can use something like the code below. This also has the added bonus of using an asynchronous call with a specified timeout in milliseconds ("3000" here).

#$computer, $port = $args[0,1] # assign values to these
$mysock = new-object net.sockets.tcpclient
$IAsyncResult = [IAsyncResult] $mysock.BeginConnect($computer, $port, $null, $null)
measure-command { $succ = $iasyncresult.AsyncWaitHandle.WaitOne(3000, $true) } | % totalseconds
$succ
$mysock.Connected
$mysock.Close()
$mysock.Dispose()

I'm adding that the real Linux utility nmap's ping sweep fails to detect most of my Windows hosts on my home LAN, and only finds 11 "alive" hosts, whereas my PSnmap seemingly finds 21, including many more Windows hosts. In my limited experience with port scanning, I've found that results can vary slightly from time to time, and from host to host you scan from, and depending on the utility you use. I've already seen some discrepancies between nmap and my script.

Superficially tested with PowerShell versions 2, 3, 4 and 5.


Screenshot examples of PSnmap

Here's me scanning 192.168.1.0/24 and some hosts specified again with names to check reverse DNS functionality, and filtering out only those that respond to ping, meaning they were scanned (also without the -ScanOnPingFail parameter).

We can see how 639 (382 port/DNS + 257 pings) port scans, pings and DNS lookups are finished in 27 seconds.

PSnmap-example-in-progress.png

PSnmap-example-done-with-LAN.png

Download PSnmap

  • PSnmap.zip - v1.2 as of 2017-06-08. PSnmap packaged as a module together with PSipcalc. Exports Invoke-PSipcalc (alias PSipcalc) and Invoke-PSnmap (alias PSnmap).

Earlier versions: File:PSnmap.zip.

  • 2017-06-08: Uploaded v1.2. Replaced instances of int64 with decimal to support x86 platforms, but I forgot that I use the method ToInt64(). Will look into it later.
  • 2016-08-06: Uploaded v1.1. Added sorting of results correctly by IP / computer name using some cleverness. Added throttling of pings since it seemed to be in an infinite loop once during testing, and this made that go away (so did cutting the /23 into two /24's and doing two runs). Possibly breaking change: Changed so multiple IPs or DNS names are in an array rather than a semicolon-joined string. I think it's better design in retrospect. Updated the built-in help with examples.

Old non-modularized versions of required script files:

  • PSnmap.ps1.txt - right-click and download (v1.0 - use the module for the latest version).
  • PSipcalc.ps1.txt - right-click and download. You also need this PSipcalc script in the same folder as PSnmap.ps1 as it is used internally by PSnmap.ps1 (I have now also packaged it as a module - see above).

Earlier versions (if any): File:PSnmap.ps1.txt.

If you have Windows Management Framework 5 or higher (WMF 5 is available for Windows 7 and up), you can install my PSnmap module from the PowerShell gallery, a Microsoft-driven project and online repository for scripts.

To install with WMF 5 and up (to get the latest PSnmap module version available), simply run this command (requires an internet connection):

Install-Module -Name PSnmap

Parameters for PSnmap

The lazy way to document here:

    # CIDR, IP/subnet, IP, or DNS/NetBIOS name.
    [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string[]] $ComputerName,

    # Port or ports to check.
    [int[]] $Port,

    # Perform a DNS lookup.
    [switch] $Dns,

    # Scan all hosts even if ping fails.
    [switch] $ScanOnPingFail,

    # Number of concurrent threads.
    [int] $ThrottleLimit = 32,

    # Do not display progress with Write-Progress.
    [switch] $HideProgress,

    # Timeout in seconds. Causes problems if too short. 30 as a default seems OK.
    [int] $Timeout = 30,

    # Port connect timeout in milliseconds. 5000 as a default seems sane.
    [int] $PortConnectTimeoutMs = 5000,

    # Do not display the end summary with start and end time, using Write-Host.
    [switch] $NoSummary