Skip to content

Commit 07affc0

Browse files
committed
Rewritten / Code & Performance improved
1 parent bae3925 commit 07affc0

File tree

4 files changed

+281
-408
lines changed

4 files changed

+281
-408
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Documentation/Thumbs.db

README.md

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,37 @@
1-
# PowerShell Async Port-Scanner
1+
# PowerShell - IPv4 Port Scanner
22

3-
Powerful asynchronus Port-Scanner which returns a custom PowerShell-Object with basic informations about the scanned Port-Range include Port-Number, Protocol, Service-Name, Service-Description and Status.
3+
Powerful asynchronus IPv4 Port Scanner for PowerShell.
44

55
## Description
66

7-
This is a powerful asynchronus Port-Scanner working with the PowerShell RunspacePool. You can scan any Port-Range you want. The result will show you all open ports Port-Number, Protocol, Service-Name, Service-Description and Status.
8-
9-
This script also work fine along with my [asychronus IP-Scanner](https://github.com/BornToBeRoot/PowerShell_Async-IPScanner) published on GitHub too. You can easily pipe the output of the IP-Scanner result in this script.
7+
This powerful asynchronus IPv4 Port Scanner allows you to scan every Port-Range you want (500 to 2600 would work). Only TCP-Ports are scanned.
108

11-
![Screenshot of Working Scanner and Result](https://github.com/BornToBeRoot/PowerShell_Async-PortScanner/blob/master/Documentation/ScanPortsAsync_Result.png?raw=true)
9+
The result will contain the Port number, Protocol, Service name, Description and the Status.
1210

13-
## Syntax
11+
![Screenshot](Documentation/Images/New-IPv4PortScan.png?raw=true "New-IPv4PortScan")
1412

15-
```powershell
16-
.\ScanPortsAsync.ps1 [-ComputerName] <String> [[-StartPort] <Int32>] [[-EndPort] <Int32>] [[-Threads] <Int32>] [[-UpdateListFromIANA]] [[-Force]] [<CommonParameters>]
17-
```
13+
To reach the best possible performance, this script uses a [RunspacePool](https://msdn.microsoft.com/en-US/library/system.management.automation.runspaces.runspacepool(v=vs.85).aspx). As you can see in the following screenshot, the individual tasks are distributed across all cpu cores:
1814

19-
## Example
15+
![Screenshot](Documentation/Images/New-IPv4PortScan_CPUusage.png?raw=true "CPU usage")
2016

21-
Scan a specific Port-Range (1-500)
17+
If you are looking for a module... you can find it [here](https://github.com/BornToBeRoot/PowerShell)!
2218

23-
```powershell
24-
.\ScanPortsAsync.ps1 -ComputerName 192.168.1.100 -StartPort 1 -EndPort 500 | Format-Table
25-
```
26-
27-
You may want to update the official "Service Name and Transport Protocol Port Number Registry" from IANA... Just add the parameter "-UpdateListFromIANA".
28-
29-
```powershell
30-
.\ScanPortsAsync.ps1 -ComputerName 172.16.2.5 -UpdateListFromIANA
31-
```
32-
If your PC has enough power, you can use more threads at the same time
19+
## Syntax
3320

3421
```powershell
35-
.\ScanPortsAsync.ps1 -ComputerName test-pc01 -Threads 250
22+
.\New-IPv4PortScan.ps1 [-ComputerName] <String> [[-StartPort] <Int32>] [[-EndPort] <Int32>] [[-Threads] <Int32>] [[-Force]] [[-UpdateList]] [<CommonParameters>]
3623
```
3724

38-
## Output
25+
## Example
3926

4027
```powershell
28+
PS> .\New-IPv4PortScan.ps1 -ComputerName fritz.box -EndPort 500
29+
4130
Port Protocol ServiceName ServiceDescription Status
4231
---- -------- ----------- ------------------ ------
4332
21 tcp ftp File Transfer Protocol [Control] open
4433
53 tcp domain Domain Name Server open
4534
80 tcp http World Wide Web HTTP open
4635
139 tcp netbios-ssn NETBIOS Session Service open
4736
445 tcp microsoft-ds Microsoft-DS open
48-
```
49-
50-
and if no port list is available (should never happend, because it's uploaded on Github)
51-
52-
```powershell
53-
Port Protocol Status
54-
---- -------- ------
55-
21 tcp open
56-
53 tcp open
57-
80 tcp open
58-
139 tcp open
59-
445 tcp open
60-
```
61-
62-
## Offical Port List
63-
64-
* [Service Name and Transport Protocol Port Number Registry - IANA.org](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml)
37+
```

Scripts/New-IPv4PortScan.ps1

Lines changed: 266 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,23 @@
1111
Powerful asynchronus IPv4 Port Scanner
1212
1313
.DESCRIPTION
14+
This powerful asynchronus IPv4 Port Scanner allows you to scan every Port-Range you want (500 to 2600 would work). Only TCP-Ports are scanned.
1415
16+
The result will contain the Port number, Protocol, Service name, Description and the Status.
17+
1518
.EXAMPLE
19+
.\New-IPv4PortScan.ps1 -ComputerName fritz.box -EndPort 500
1620
17-
.EXAMPLE
21+
Port Protocol ServiceName ServiceDescription Status
22+
---- -------- ----------- ------------------ ------
23+
21 tcp ftp File Transfer Protocol [Control] open
24+
53 tcp domain Domain Name Server open
25+
80 tcp http World Wide Web HTTP open
26+
139 tcp netbios-ssn NETBIOS Session Service open
27+
445 tcp microsoft-ds Microsoft-DS open
1828
1929
.LINK
20-
https://github.com/BornToBeRoot/PowerShell_IPv4PortScanner
30+
https://github.com/BornToBeRoot/PowerShell_IPv4PortScanner/blob/master/README.md
2131
#>
2232

2333
[CmdletBinding()]
@@ -40,8 +50,8 @@ param(
4050

4151
[Parameter(
4252
Position=3,
43-
HelpMessage='Maximum number of threads at the same time (Default=100)')]
44-
[Int32]$Threads=100,
53+
HelpMessage='Maximum number of threads at the same time (Default=500)')]
54+
[Int32]$Threads=500,
4555

4656
[Parameter(
4757
Position=4,
@@ -102,8 +112,51 @@ Begin{
102112
{
103113
Rename-Item -Path $XML_PortList_BackupPath -NewName $XML_PortList_Path
104114
}
115+
116+
$_.Exception.Message
105117
}
106118
}
119+
120+
# Function to assign service with port
121+
function AssignServiceWithPort
122+
{
123+
param(
124+
$Result
125+
)
126+
127+
Begin{
128+
129+
}
130+
131+
Process{
132+
$Service = [String]::Empty
133+
$Description = [String]::Empty
134+
135+
foreach($XML_Node in $XML_PortList.Registry.Record)
136+
{
137+
if(($Result.Protocol -eq $XML_Node.protocol) -and ($Result.Port -eq $XML_Node.number))
138+
{
139+
$Service = $XML_Node.name
140+
$Description = $XML_Node.description
141+
break
142+
}
143+
}
144+
145+
$NewResult = [pscustomobject] @{
146+
Port = $Result.Port
147+
Protocol = $Result.Protocol
148+
ServiceName = $Service
149+
ServiceDescription = $Description
150+
Status = $Result.Status
151+
}
152+
153+
return $NewResult
154+
}
155+
156+
End{
157+
158+
}
159+
}
107160
}
108161

109162
Process{
@@ -113,17 +166,225 @@ Process{
113166
}
114167
elseif(-Not([System.IO.File]::Exists($XML_PortList_Path)))
115168
{
116-
Write-Host 'No xml-file to assign service with port found! Use the parameter "-UpdateList" to download the latest version from IANA.org. This warning doesn`t affect the scanning procedure.'
169+
Write-Host 'No xml-file to assign service with port found! Use the parameter "-UpdateList" to download the latest version from IANA.org. This warning doesn`t affect the scanning procedure.' -ForegroundColor Yellow
117170
}
118171

172+
# Check if it is possible to assign service with port --> import xml-file
119173
if([System.IO.File]::Exists($XML_PortList_Path))
120174
{
121175
$AssignServiceWithPort = $true
176+
177+
$XML_PortList = [xml](Get-Content -Path $XML_PortList_Path)
122178
}
123179
else
124180
{
125181
$AssignServiceWithPort = $false
126182
}
183+
184+
# Validate Port-Range
185+
if($StartPort -gt $EndPort)
186+
{
187+
Write-Host "Invalid Port-Range... Check your input!" -ForegroundColor Red
188+
return
189+
}
190+
191+
# Check if host is reachable
192+
Write-Verbose "Test if host is reachable..."
193+
if(-not(Test-Connection -ComputerName $ComputerName -Count 2 -Quiet))
194+
{
195+
Write-Host "$ComputerName is not reachable!" -ForegroundColor Red
196+
197+
if($Force -eq $false)
198+
{
199+
do {
200+
$Answer = Read-Host "Would you like to continue? (perhaps only ICMP is blocked) [yes|no]"
201+
202+
} while("yes","y","no","n" -notcontains $Answer)
203+
204+
if("no","n" -contains $Answer)
205+
{
206+
return
207+
}
208+
}
209+
}
210+
211+
$PortsToScan = ($EndPort - $StartPort)
212+
213+
Write-Verbose "Scanning range from $StartPort to $EndPort ($PortsToScan Ports)"
214+
Write-Verbose "Running with max $Threads threads"
215+
216+
# Check if ComputerName is already an IPv4-Address, if not... try to resolve it
217+
$IPv4Address = [String]::Empty
218+
219+
if([bool]($ComputerName -as [IPAddress]))
220+
{
221+
$IPv4Address = $ComputerName
222+
}
223+
else
224+
{
225+
# Get IP from Hostname (IPv4 only)
226+
try{
227+
$AddressList = @(([System.Net.Dns]::GetHostEntry($ComputerName)).AddressList)
228+
229+
foreach($Address in $AddressList)
230+
{
231+
if($Address.AddressFamily -eq "InterNetwork")
232+
{
233+
$IPv4Address = $Address.IPAddressToString
234+
break
235+
}
236+
}
237+
}
238+
catch{ } # Can't get IPAddressList
239+
240+
if([String]::IsNullOrEmpty($IPv4Address))
241+
{
242+
Write-Host "Could not get IPv4-Address for $ComputerName. (Try to enter an IPv4-Address instead of the Hostname)" -ForegroundColor Red
243+
return
244+
}
245+
}
246+
247+
# Scriptblock --> will run in runspaces (threads)...
248+
[System.Management.Automation.ScriptBlock]$ScriptBlock = {
249+
Param(
250+
$IPv4Address,
251+
$Port
252+
)
253+
254+
try{
255+
$Socket = New-Object System.Net.Sockets.TcpClient($IPv4Address,$Port)
256+
257+
if($Socket.Connected)
258+
{
259+
$Status = "Open"
260+
$Socket.Close()
261+
}
262+
else
263+
{
264+
$Status = "Closed"
265+
}
266+
}
267+
catch{
268+
$Status = "Closed"
269+
}
270+
271+
$Result = [pscustomobject] @{
272+
Port = $Port
273+
Protocol = "tcp"
274+
Status = $Status
275+
}
276+
277+
return $Result
278+
}
279+
280+
Write-Verbose "Setting up RunspacePool..."
281+
282+
# Create RunspacePool and Jobs
283+
$RunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $Threads, $Host)
284+
$RunspacePool.Open()
285+
[System.Collections.ArrayList]$Jobs = @()
286+
287+
Write-Verbose "Setting up Jobs..."
288+
289+
#Set up job for each port...
290+
foreach($Port in $StartPort..$EndPort)
291+
{
292+
$ScriptParams =@{
293+
IPv4Address = $IPv4Address
294+
Port = $Port
295+
}
296+
297+
# Catch when trying to divide through zero
298+
try {
299+
$Progress_Percent = (($Port - $StartPort) / $PortsToScan) * 100
300+
}
301+
catch {
302+
$Progress_Percent = 100
303+
}
304+
305+
Write-Progress -Activity "Setting up jobs..." -Id 1 -Status "Current Port: $Port" -PercentComplete ($Progress_Percent)
306+
307+
# Create mew job
308+
$Job = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddParameters($ScriptParams)
309+
$Job.RunspacePool = $RunspacePool
310+
311+
$JobObj = [pscustomobject] @{
312+
RunNum = $Port - $StartPort
313+
Pipe = $Job
314+
Result = $Job.BeginInvoke()
315+
}
316+
317+
# Add job to collection
318+
[void]$Jobs.Add($JobObj)
319+
}
320+
321+
Write-Verbose "Waiting for jobs to complete & starting to process results..."
322+
323+
# Total jobs to calculate percent complete, because jobs are removed after they are processed
324+
$Jobs_Total = $Jobs.Count
325+
326+
# Process results, while waiting for other jobs
327+
Do {
328+
# Get all jobs, which are completed
329+
$Jobs_ToProcess = $Jobs | Where-Object {$_.Result.IsCompleted}
330+
331+
# If no jobs finished yet, wait 500 ms and try again
332+
if($Jobs_ToProcess -eq $null)
333+
{
334+
Write-Verbose "No jobs completed, wait 500ms..."
335+
336+
Start-Sleep -Milliseconds 500
337+
continue
338+
}
339+
340+
# Get jobs, which are not complete yet
341+
$Jobs_Remaining = ($Jobs | Where-Object {$_.Result.IsCompleted -eq $false}).Count
342+
343+
# Catch when trying to divide through zero
344+
try {
345+
$Progress_Percent = 100 - (($Jobs_Remaining / $Jobs_Total) * 100)
346+
}
347+
catch {
348+
$Progress_Percent = 100
349+
}
350+
351+
Write-Progress -Activity "Waiting for jobs to complete... ($($Threads - $($RunspacePool.GetAvailableRunspaces())) of $Threads threads running)" -Id 1 -PercentComplete $Progress_Percent -Status "$Jobs_Remaining remaining..."
352+
353+
Write-Verbose "Processing $(if($Jobs_ToProcess.Count -eq $null){"1"}else{$Jobs_ToProcess.Count}) job(s)..."
354+
355+
# Processing completed jobs
356+
foreach($Job in $Jobs_ToProcess)
357+
{
358+
# Get the result...
359+
$Job_Result = $Job.Pipe.EndInvoke($Job.Result)
360+
$Job.Pipe.Dispose()
361+
362+
# Remove job from collection
363+
$Jobs.Remove($Job)
364+
365+
# Check if result is null --> if not, return it
366+
if($Job_Result -ne $null -and $Job_Result.Status -eq "Open")
367+
{
368+
if($AssignServiceWithPort)
369+
{
370+
AssignServiceWithPort -Result $Job_Result
371+
}
372+
else
373+
{
374+
$Job_Result
375+
}
376+
}
377+
}
378+
379+
} While ($Jobs.Count -gt 0)
380+
381+
Write-Verbose "Closing RunspacePool and free resources..."
382+
383+
# Close the RunspacePool and free resources
384+
$RunspacePool.Close()
385+
$RunspacePool.Dispose()
386+
387+
Write-Verbose "Script finished at $(Get-Date)"
127388
}
128389

129390
End{

0 commit comments

Comments
 (0)