├── .readme
├── ClearARP.png
├── ContextMenuExport.png
├── CopyItemToClip.png
├── DoubleClickPopup.png
├── HTMLexample-export.png
├── IPScanner.png
├── MonitorMode.png
├── PortScan.png
├── ScanContextSubnet.png
└── ToggleModes.png
├── IPScanner.cmd
├── LICENSE
├── README.md
├── cmd-wmic-version
└── IPScanner.cmd
└── console-crossplatform
└── IPScanner.ps1
/.readme/ClearARP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/ClearARP.png
--------------------------------------------------------------------------------
/.readme/ContextMenuExport.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/ContextMenuExport.png
--------------------------------------------------------------------------------
/.readme/CopyItemToClip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/CopyItemToClip.png
--------------------------------------------------------------------------------
/.readme/DoubleClickPopup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/DoubleClickPopup.png
--------------------------------------------------------------------------------
/.readme/HTMLexample-export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/HTMLexample-export.png
--------------------------------------------------------------------------------
/.readme/IPScanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/IPScanner.png
--------------------------------------------------------------------------------
/.readme/MonitorMode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/MonitorMode.png
--------------------------------------------------------------------------------
/.readme/PortScan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/PortScan.png
--------------------------------------------------------------------------------
/.readme/ScanContextSubnet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/ScanContextSubnet.png
--------------------------------------------------------------------------------
/.readme/ToggleModes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/illsk1lls/IPScanner/c408fda7b4569ccbb6a80f820f07bd4ab7abf39f/.readme/ToggleModes.png
--------------------------------------------------------------------------------
/IPScanner.cmd:
--------------------------------------------------------------------------------
1 | <# :: Hybrid CMD / Powershell Launcher - Rename file to .CMD to Autolaunch with console settings (Double-Click) - Rename to .PS1 to run as Powershell script without console settings
2 | @ECHO OFF
3 | SET "0=%~f0"&SET "LEGACY={B23D10C0-E52E-411E-9D5B-C09FDF709C7D}"&SET "LETWIN={00000000-0000-0000-0000-000000000000}"&SET "TERMINAL={2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}"&SET "TERMINAL2={E12CFF52-A866-4C77-9A90-F570A7AA2C6B}"
4 | POWERSHELL -nop -c "Get-WmiObject -Class Win32_OperatingSystem | Select -ExpandProperty Caption | Find 'Windows 11'">nul
5 | IF ERRORLEVEL 0 (
6 | SET isEleven=1
7 | >nul 2>&1 REG QUERY "HKCU\Console\%%%%Startup" /v DelegationConsole
8 | IF ERRORLEVEL 1 (
9 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationConsole /t REG_SZ /d "%LETWIN%" /f>nul
10 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationTerminal /t REG_SZ /d "%LETWIN%" /f>nul
11 | )
12 | FOR /F "usebackq tokens=3" %%# IN (`REG QUERY "HKCU\Console\%%%%Startup" /v DelegationConsole 2^>nul`) DO (
13 | IF NOT "%%#"=="%LEGACY%" (
14 | SET "DEFAULTCONSOLE=%%#"
15 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationConsole /t REG_SZ /d "%LEGACY%" /f>nul
16 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationTerminal /t REG_SZ /d "%LEGACY%" /f>nul
17 | )
18 | )
19 | )
20 | START /MIN "" POWERSHELL -nop -c "iex ([io.file]::ReadAllText('%~f0'))">nul
21 | IF "%isEleven%"=="1" (
22 | IF DEFINED DEFAULTCONSOLE (
23 | IF "%DEFAULTCONSOLE%"=="%TERMINAL%" (
24 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationConsole /t REG_SZ /d "%TERMINAL%" /f>nul
25 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationTerminal /t REG_SZ /d "%TERMINAL2%" /f>nul
26 | ) ELSE (
27 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationConsole /t REG_SZ /d "%DEFAULTCONSOLE%" /f>nul
28 | REG ADD "HKCU\Console\%%%%Startup" /v DelegationTerminal /t REG_SZ /d "%DEFAULTCONSOLE%" /f>nul
29 | )
30 | )
31 | )
32 | EXIT
33 | #>if($env:0){$PSCommandPath="$env:0"}
34 | ###POWERSHELL BELOW THIS LINE###
35 |
36 | # Hide Console - Show GUI Only - Only works for Legacy console
37 | Add-Type -MemberDefinition '[DllImport("User32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);' -Namespace Win32 -Name Functions
38 | $closeConsoleUseGUI=[Win32.Functions]::ShowWindow((Get-Process -Id $PID).MainWindowHandle,0)
39 |
40 | # Allow Single Instance Only
41 | $AppId = 'Simple IP Scanner'
42 | $singleInstance = $false
43 | $script:SingleInstanceEvent = New-Object Threading.EventWaitHandle $true,([Threading.EventResetMode]::ManualReset),"Global\$AppId",([ref] $singleInstance)
44 | if (-not $singleInstance){
45 | $shell = New-Object -ComObject Wscript.Shell
46 | $shell.Popup("$AppId is already running!",0,'ERROR:',0x0) | Out-Null
47 | Exit
48 | }
49 |
50 | # Check if .NET Framework version is at least 3.0, which is required for WPF applications
51 | $frameworks = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse | Get-ItemProperty -Name Version -EA 0 | Where-Object { $_.PSChildName -Match '^(?!S)\p{L}'} | Select-Object -ExpandProperty Version
52 | $highestVersion = $frameworks | ForEach-Object { [version]$_ } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum
53 | if ($highestVersion -lt [version]'3.0') {
54 | $dotnetchecker = New-Object -ComObject Wscript.Shell
55 | $dotnetchecker.Popup("dotNET 3.0 or higher is required!",0,'ERROR:',0x0) | Out-Null
56 | Exit
57 | }
58 |
59 | # GUI Main Dispatcher
60 | function Update-uiMain(){
61 | $Main.Dispatcher.Invoke([Windows.Threading.DispatcherPriority]::Background, [action]{})
62 | }
63 |
64 | function Update-Progress {
65 | param ($value, $text)
66 | $Progress.Value = $value
67 | $BarText.Text = $text
68 | Update-uiMain
69 | }
70 |
71 | # Find gateway
72 | $route = Get-NetRoute -DestinationPrefix 0.0.0.0/0 | Select-Object -First 1
73 | $global:gateway = $route.NextHop
74 | $gatewayParts = $global:gateway -split '\.'
75 | $global:gatewayPrefix = (($gatewayParts[0..2] -join '.') + '.')
76 |
77 | # Store the original gateway prefix for reset functionality
78 | $originalGatewayPrefix = $global:gatewayPrefix
79 |
80 | # Initialize RunspacePool
81 | $SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
82 | $RunspacePool = [runspacefactory]::CreateRunspacePool(1, [System.Environment]::ProcessorCount, $SessionState, $Host)
83 | $RunspacePool.Open()
84 |
85 | # Get Host Info
86 | function Get-HostInfo {
87 | param(
88 | [string]$gateway,
89 | [string]$gatewayPrefix,
90 | [string]$originalGatewayPrefix
91 | )
92 | $getHostInfoScriptBlock = {
93 | param(
94 | [string]$gateway,
95 | [string]$gatewayPrefix,
96 | [string]$originalGatewayPrefix
97 | )
98 | # Get Hostname
99 | $hostName = [System.Net.Dns]::GetHostName()
100 |
101 | # Check internet connection and get external IP
102 | $ProgressPreference = 'SilentlyContinue'
103 | try {
104 | $ncsiCheck = Invoke-RestMethod "http://www.msftncsi.com/ncsi.txt"
105 | if ($ncsiCheck -eq "Microsoft NCSI") {
106 | $externalIP = Invoke-RestMethod "http://ifconfig.me/ip"
107 | } else {
108 | $externalIP = "No Internet or Redirection"
109 | }
110 | } catch {
111 | $externalIP = "No Internet or Error"
112 | }
113 | $ProgressPreference = 'Continue'
114 |
115 | # Use the passed gateway and gatewayPrefix
116 | $internalIP = (Get-NetIPAddress | Where-Object {
117 | $_.AddressFamily -eq 'IPv4' -and
118 | $_.InterfaceAlias -ne 'Loopback Pseudo-Interface 1' -and
119 | $_.IPAddress -like "$originalGatewayPrefix*"
120 | }).IPAddress
121 |
122 | # Get current adapter
123 | $adapter = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object {
124 | $_.InterfaceAlias -match 'Ethernet|Wi-Fi' -and
125 | $_.IPAddress -like "$originalGatewayPrefix*"
126 | }).InterfaceAlias
127 |
128 | # Get MAC address
129 | $myMac = (Get-NetAdapter -Name $adapter).MacAddress -replace '-', ':'
130 |
131 | # Get domain
132 | $domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain
133 |
134 | # Init ARP cache data
135 | $arpInit = Get-NetNeighbor | Where-Object {($_.State -eq "Reachable" -or $_.State -eq "Stale") -and ($_.IPAddress -like "$gatewayPrefix*") -and -not $_.IPAddress.Contains(':')} | Select-Object -Property IPAddress, LinkLayerAddress
136 |
137 | # Mark empty as unknown
138 | $variables = @('hostName', 'externalIP', 'internalIP', 'gateway', 'domain')
139 | foreach ($item in $variables) {
140 | if (-not (Get-Variable -Name $item -ValueOnly)) {
141 | Set-Variable -Name $item -Value 'Unknown'
142 | }
143 | }
144 |
145 | return @{
146 | 'hostName' = $hostName;
147 | 'externalIP' = $externalIP;
148 | 'internalIP' = $internalIP;
149 | 'gateway' = $gateway;
150 | 'gatewayPrefix' = $gatewayPrefix;
151 | 'adapter' = $adapter;
152 | 'myMac' = $myMac;
153 | 'domain' = $domain;
154 | 'arpInit' = $arpInit;
155 | }
156 | }
157 |
158 | $getHostInfoThread = [powershell]::Create().AddScript($getHostInfoScriptBlock)
159 | $getHostInfoThread.AddArgument($global:gateway)
160 | $getHostInfoThread.AddArgument($global:gatewayPrefix)
161 | $getHostInfoThread.AddArgument($originalGatewayPrefix)
162 | $getHostInfoThread.RunspacePool = $RunspacePool
163 | $getHostInfoAsync = $getHostInfoThread.BeginInvoke()
164 | $getHostInfoAsync.AsyncWaitHandle.WaitOne()
165 | $hostInfoResults = $getHostInfoThread.EndInvoke($getHostInfoAsync)
166 | $global:hostName = $hostInfoResults.hostName
167 | $global:externalIP = $hostInfoResults.externalIP
168 | $global:internalIP = $hostInfoResults.internalIP
169 | $global:gateway = $hostInfoResults.gateway
170 | $global:gatewayPrefix = $hostInfoResults.gatewayPrefix
171 | $global:adapter = $hostInfoResults.adapter
172 | $global:myMac = $hostInfoResults.myMac
173 | $global:domain = $hostInfoResults.domain
174 | $global:arpInit = $hostInfoResults.arpInit
175 | Update-Progress 0 'Scanning'
176 | $getHostInfoThread.Dispose()
177 | }
178 |
179 | # Send packets across subnet (progress values adjusted for proper display)
180 | function Scan-Subnet {
181 | $progressCounter = 0
182 |
183 | 1..254 | ForEach-Object {
184 | $progressCounter++
185 | $percentComplete = [math]::Min([math]::Round(($progressCounter / 240) * 100), 100)
186 | Test-Connection -ComputerName "$global:gatewayPrefix$_" -Count 1 -AsJob | Out-Null
187 | if($percentComplete -ge 100){
188 | Update-Progress -value $percentComplete -text "Listening"
189 | } else {
190 | Update-Progress -value $percentComplete -text "Sending Packets"
191 | }
192 | }
193 | Update-Progress -value 100 -text "Listening"
194 | Get-Job | Wait-Job -ErrorAction Stop | Out-Null
195 | $results = Get-Job | Receive-Job -ErrorAction Stop
196 | $global:successfulPings = @($results | Where-Object { $_.StatusCode -eq 0 } | Select-Object -ExpandProperty Address)
197 | Get-Job | Remove-Job -Force
198 | }
199 |
200 | # Create peer list
201 | function List-Machines {
202 | Update-Progress 0 'Identifying Devices'
203 |
204 | # Convert IP Addresses from string to int by each section
205 | $arpOutput = $arpInit | Where-Object { $_.IPAddress -match "^\d+\.\d+\.\d+\.\d+$" } | Sort-Object -Property { $ip = $_.IPAddress; [version]($ip) }
206 |
207 | $self = 0
208 | $myLastOctet = [int]($internalIP -split '\.')[-1]
209 |
210 | # Get Vendor via Mac (thanks to u/mprz)
211 | $ProgressPreference = 'SilentlyContinue'
212 | $tryMyVendor = (irm "https://www.macvendorlookup.com/api/v2/$($myMac.Replace(':','').Substring(0,6))" -Method Get).Company
213 | $ProgressPreference = 'Continue'
214 | $myVendor = if($tryMyVendor){$tryMyVendor.substring(0, [System.Math]::Min(35, $tryMyVendor.Length))} else {'Unable to Identify'}
215 |
216 | # Cycle through ARP table to populate initial ListView data and start async lookups
217 | $totalItems = ($arpOutput.Count - 1)
218 |
219 | # First, add all known ARP entries and hostnames
220 | foreach ($line in $arpOutput) {
221 | $ip = $line.IPAddress
222 | $mac = $line.LinkLayerAddress.Replace('-',':')
223 | $quickNameLookup = ((Resolve-DnsName -Name $ip -DnsOnly -ErrorAction SilentlyContinue).NameHost)
224 | if(-not $quickNameLookup){$quickNameLookup = 'Resolving...'}
225 | $name = if ($ip -eq $internalIP) {"$hostName (This Device)"} else {"$quickNameLookup"}
226 | $vendor = if ($ip -eq $internalIP) {$myVendor} else {'Identifying...'}
227 |
228 | # Determine if the IP was pingable
229 | $pingResult = $ip -in $global:successfulPings
230 |
231 | # Format and display
232 | $item = [pscustomobject]@{
233 | 'MACaddress' = $mac;
234 | 'Vendor' = $vendor;
235 | 'IPaddress' = $ip;
236 | 'HostName' = $name;
237 | 'Ping' = $pingResult;
238 | 'PingImage' = Create-GradientEllipse -isPingSuccessful $pingResult
239 | }
240 | $listView.Items.Add($item)
241 | }
242 |
243 | # Now add entries for successful pings not in ARP data, and add self
244 | $successfulPingsNotInARP = $global:successfulPings | Where-Object { $_ -notin $arpOutput.IPAddress }
245 | foreach ($ip in $successfulPingsNotInARP) {
246 | if ($global:gatewayPrefix -ne $originalGatewayPrefix) {
247 | $mac = 'Unreachable'
248 | } else {
249 | $mac = [MacAddressResolver]::GetMacFromIP($ip)
250 | }
251 | if ($ip -eq $internalIP) {
252 | $item = [pscustomobject]@{
253 | 'MACaddress' = $myMac;
254 | 'Vendor' = $myVendor;
255 | 'IPaddress' = $internalIP;
256 | 'HostName' = "$hostName (This Device)";
257 | 'Ping' = $true;
258 | 'PingImage' = Create-GradientEllipse -isPingSuccessful $true
259 | }
260 | $listView.Items.Add($item)
261 | } else {
262 | $item = [pscustomobject]@{
263 | 'MACaddress' = $mac;
264 | 'Vendor' = $vendor;
265 | 'IPaddress' = $ip;
266 | 'HostName' = 'Resolving...';
267 | 'Ping' = $true;
268 | 'PingImage' = Create-GradientEllipse -isPingSuccessful $true
269 | }
270 | $listView.Items.Add($item)
271 | }
272 | }
273 |
274 | # Sort ListView items by IP address in ascending order
275 | $sortedItems = $listView.Items | Sort-Object -Property {[version]$_.IPaddress}
276 | $listView.Items.Clear()
277 | $sortedItems | ForEach-Object { $listView.Items.Add($_) }
278 | $listView.Items.Refresh()
279 |
280 | if ($totalItems -ge 21) {
281 | $hostNameColumn.Width = 270
282 | }
283 | $global:totalCount = $listView.Items.Count
284 | $TotalListed.Text = "$totalCount devices found"
285 | Update-uiMain
286 | }
287 |
288 | # Background Vendor Lookup
289 | function processVendors {
290 | $runspace = [runspacefactory]::CreateRunspace()
291 | $runspace.Open()
292 | $vendorLookup = [powershell]::Create()
293 | $vendorLookup.Runspace = $runspace
294 |
295 | $lookupBlock = {
296 | param ($listView, $internalIP)
297 |
298 | $vendorJobs = @{}
299 |
300 | # Process found devices
301 | foreach ($item in $listView.Items) {
302 | $ip = $item.IPaddress
303 | $mac = $item.MACaddress
304 | if ($ip -ne $internalIP) {
305 | if($item.Vendor -eq 'Identifying...'){
306 | $vendorJob = Start-Job -ScriptBlock {
307 | param($mac)
308 | $ProgressPreference = 'SilentlyContinue'
309 | $response = (irm "https://www.macvendorlookup.com/api/v2/$($mac.Replace(':','').Substring(0,6))" -Method Get)
310 | $ProgressPreference = 'Continue'
311 | if([string]::IsNullOrEmpty($response.Company)){
312 | return $null
313 | } else {
314 | return $response
315 | }
316 | } -ArgumentList $mac
317 | $vendorJobs[$ip] = $vendorJob
318 | do {
319 | # Limit maximum vendor tasks and process
320 | foreach ($ipCheck in @($vendorJobs.Keys)) {
321 | if ($vendorJobs[$ipCheck].State -eq "Completed") {
322 | $result = Receive-Job -Job $vendorJobs[$ipCheck]
323 | $vendorResult = if ($result -and $result.Company) {
324 | $result.Company.substring(0, [System.Math]::Min(30, $result.Company.Length))
325 | } else {
326 | 'Unable to Identify'
327 | }
328 | foreach ($it in $listView.Items) {
329 | if ($it.IPaddress -eq $ipCheck) {
330 | $it.Vendor = $vendorResult
331 | }
332 | }
333 | $vendorJobs.Remove($ipCheck)
334 | }
335 | }
336 | Start-Sleep -Milliseconds 50
337 | } while ($vendorJobs.Count -ge 5)
338 | }
339 | }
340 | }
341 |
342 | # Process remaining tasks
343 | while ($vendorJobs.Count -ge 1) {
344 | # Process vendor tasks
345 | foreach ($ipCheck in @($vendorJobs.Keys)) {
346 | if ($vendorJobs[$ipCheck].State -eq "Completed") {
347 | $result = Receive-Job -Job $vendorJobs[$ipCheck]
348 | $vendorResult = if ($result -and $result.Company) {
349 | $result.Company.substring(0, [System.Math]::Min(30, $result.Company.Length))
350 | } else {
351 | 'Unable to Identify'
352 | }
353 | foreach ($it in $listView.Items) {
354 | if ($it.IPaddress -eq $ipCheck) {
355 | $it.Vendor = $vendorResult
356 | }
357 | }
358 | $vendorJobs.Remove($ipCheck)
359 | }
360 | }
361 | Start-Sleep -Milliseconds 50
362 | }
363 |
364 | # Clean up jobs
365 | Remove-Job -Job $vendorJobs.Values -Force
366 | }
367 |
368 | # Script block params
369 | $null = $vendorLookup.AddScript($lookupBlock).AddArgument($listView).AddArgument($internalIP)
370 |
371 | $asyncResult = $vendorLookup.BeginInvoke()
372 |
373 | # Cleanup
374 | $vendorLookup.EndInvoke($asyncResult)
375 | $vendorLookup.Dispose()
376 | $runspace.Close()
377 | $runspace.Dispose()
378 | }
379 |
380 | # Background Hostname Lookup
381 | function processHostnames {
382 | $hostnameLookupThread = [powershell]::Create().AddScript({
383 | param ($listView, $internalIP, $RunspacePool, $gatewayPrefix, $originalGatewayPrefix)
384 |
385 | $pingItems = @()
386 | $nonPingItems = @()
387 |
388 | # Separate items into pingable and non-pingable
389 | foreach ($item in $listView.Items) {
390 | if ($item.Ping -eq $true -and $item.IPaddress -ne $internalIP) {
391 | $pingItems += $item
392 | } elseif ($item.IPaddress -ne $internalIP) {
393 | $nonPingItems += $item
394 | }
395 | }
396 |
397 | # Hostname resolution with timeout
398 | $timeout = if ($gatewayPrefix -ne $originalGatewayPrefix) { 4500 } else { 3000 }
399 | $resolveScript = {
400 | param ($ip, $timeout)
401 | $dnsTask = [System.Net.Dns]::GetHostEntryAsync($ip)
402 | $timeoutTask = [System.Threading.Tasks.Task]::Delay($timeout)
403 |
404 | $task = [System.Threading.Tasks.Task]::WhenAny($dnsTask, $timeoutTask)
405 | $task.Wait()
406 | $result = $task.Result
407 |
408 | if ($result -eq $dnsTask -and $dnsTask.Status -eq [System.Threading.Tasks.TaskStatus]::RanToCompletion) {
409 | return [PSCustomObject]@{IP = $ip; HostName = $dnsTask.Result.HostName}
410 | } else {
411 | return [PSCustomObject]@{IP = $ip; HostName = "Unable to Resolve"}
412 | }
413 | }
414 |
415 | # Setup separate RunspacePool
416 | $iss = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
417 | $rsHost = [runspacefactory]::CreateRunspace($iss)
418 | $rsHost.Open()
419 | $rsPool = [runspacefactory]::CreateRunspacePool(1, 10, $rsHost, $RunspacePool.ApartmentState)
420 | $rsPool.Open()
421 |
422 | # Start hostNameJobs - responses first
423 | $hostNameJobs = @()
424 | foreach ($item in $pingItems) {
425 | if($item.Hostname -eq 'Resolving...'){
426 | $hostNameJob = [powershell]::Create().AddScript($resolveScript).AddArgument($item.IPaddress).AddArgument($timeout)
427 | $hostNameJob.RunspacePool = $rsPool
428 | $hostNameJobHandle = $hostNameJob.BeginInvoke()
429 | $hostNameJobs += [PSCustomObject]@{
430 | Pipeline = $hostNameJob
431 | Handle = $hostNameJobHandle
432 | IP = $item.IPaddress
433 | }
434 | }
435 | }
436 | foreach ($item in $nonPingItems) {
437 | if($item.Hostname -eq 'Resolving...'){
438 | $hostNameJob = [powershell]::Create().AddScript($resolveScript).AddArgument($item.IPaddress).AddArgument($timeout)
439 | $hostNameJob.RunspacePool = $rsPool
440 | $hostNameJobHandle = $hostNameJob.BeginInvoke()
441 | $hostNameJobs += [PSCustomObject]@{
442 | Pipeline = $hostNameJob
443 | Handle = $hostNameJobHandle
444 | IP = $item.IPaddress
445 | }
446 | }
447 | }
448 |
449 | # Process hostNameJobs
450 | while ($hostNameJobs.Count -gt 0) {
451 | for ($i = $hostNameJobs.Count - 1; $i -ge 0; $i--) {
452 | $hostNameJob = $hostNameJobs[$i]
453 | if ($hostNameJob.Handle.IsCompleted) {
454 | $result = $hostNameJob.Pipeline.EndInvoke($hostNameJob.Handle)
455 | foreach ($it in $listView.Items) {
456 | if ($it.IPaddress -eq $hostNameJob.IP) {
457 | $it.HostName = $result.HostName
458 | break
459 | }
460 | }
461 | $hostNameJob.Pipeline.Dispose()
462 | $hostNameJobs.RemoveAt($i)
463 | }
464 | }
465 | Start-Sleep -Milliseconds 10
466 | }
467 |
468 | # Cleanup
469 | $rsPool.Close()
470 | $rsPool.Dispose()
471 | $rsHost.Close()
472 | $rsHost.Dispose()
473 |
474 | }, $true).AddArgument($listView).AddArgument($internalIP).AddArgument($RunspacePool).AddArgument($global:gatewayPrefix).AddArgument($originalGatewayPrefix)
475 | $hostnameLookupThread.RunspacePool = $RunspacePool
476 | $hostnameScan = $hostnameLookupThread.BeginInvoke()
477 | }
478 |
479 | # Portscan
480 | function Test-Port {
481 | param (
482 | [string]$computer,
483 | [int]$port,
484 | [int]$timeout = 5
485 | )
486 | $tcp = New-Object System.Net.Sockets.TcpClient
487 | try {
488 | $result = $tcp.BeginConnect($computer, $port, $null, $null)
489 | $success = $result.AsyncWaitHandle.WaitOne($timeout, $true)
490 | if ($success) {
491 | return "Port`: $port is open"
492 | }
493 | else {
494 | return $null
495 | }
496 | }
497 | catch {
498 | return $null
499 | }
500 | finally {
501 | $tcp.Close()
502 | }
503 | }
504 |
505 | # Check common ports
506 | function CheckConnectivity {
507 | param (
508 | [string]$selectedhost
509 | )
510 | # Disable all buttons for 'This Device'
511 | if ($selectedhost -match $internalIP) {
512 | @('btnRDP', 'btnWebInterface', 'btnShare') | ForEach-Object {
513 | Get-Variable $_ -ValueOnly | ForEach-Object {
514 | $_.IsEnabled = $false
515 | $_.Visibility = 'Collapsed'
516 | }
517 | }
518 | $btnNone.IsEnabled = $true
519 | $btnNone.Visibility = 'Visible'
520 | return
521 | }
522 | $global:tryToConnect = $selectedhost -replace ' (This Device)', ''
523 |
524 | # Find the item in ListView based on IP or HostName
525 | $selectedItem = $listView.Items | Where-Object {
526 | $_.IPaddress -eq $tryToConnect -or $_.HostName -eq $selectedhost
527 | } | Select-Object -First 1
528 |
529 | # Check connectivity for different protocols
530 | $ports = @{
531 | HTTP = 80
532 | HTTPS = 443
533 | SMBv2 = 445
534 | SMB = 139
535 | RDP = 3389
536 | }
537 | $results = @{}
538 | foreach ($protocol in $ports.Keys) {
539 | $results[$protocol] = Test-Port -computer $tryToConnect -port $ports[$protocol] -timeout 200
540 | }
541 |
542 | # Update button states based on connectivity results
543 | $btnShare.IsEnabled = ($results.SMBv2 -or $results.SMB) -and $HostName -ne $tryToConnect
544 | $btnShare.Visibility = if ($btnShare.IsEnabled) { 'Visible' } else { 'Collapsed' }
545 |
546 | if ($btnShare.Visibility -eq 'Visible') {$btnWebInterface.Margin = "0,0,25,0"} else {$btnWebInterface.Margin = "0,0,0,0"}
547 | $btnWebInterface.IsEnabled = ($results.HTTP -or $results.HTTPS) -and $HostName -ne $tryToConnect
548 | $btnWebInterface.Visibility = if ($btnWebInterface.IsEnabled) { 'Visible' } else { 'Collapsed' }
549 | $global:httpAvailable = if ($results.HTTP) { 1 } else { 0 }
550 |
551 | if ($btnShare.Visibility -eq 'Visible' -or $btnWebInterface.Visibility -eq 'Visible') {$btnRDP.Margin = "0,0,25,0"} else {$btnRDP.Margin = "0,0,0,0"}
552 | $btnRDP.IsEnabled = $results.RDP -and $HostName -ne $tryToConnect
553 | $btnRDP.Visibility = if ($btnRDP.IsEnabled) { 'Visible' } else { 'Collapsed' }
554 |
555 | # Show no connections icon if nothing is available
556 | if (-not $btnRDP.IsEnabled -and -not $btnWebInterface.IsEnabled -and -not $btnShare.IsEnabled) {
557 | $btnNone.IsEnabled = $true
558 | $btnNone.Visibility = 'Visible'
559 | } else {
560 | $btnNone.IsEnabled = $false
561 | $btnNone.Visibility = 'Collapsed'
562 | }
563 |
564 | # Show ping response status in popup window
565 | $pingStatusImage.Content = Create-GradientEllipse -isPingSuccessful $selectedItem.Ping -width 12 -height 12
566 | $pingStatusText.Text = if ($selectedItem.Ping) { "ICMP response received" } else { "No ICMP response received" }
567 | }
568 |
569 | # Listview column sort logic
570 | $sortDirections = @{}
571 | $listViewSortColumn = {
572 | param([System.Object]$sender, [System.EventArgs]$Event)
573 | $column = $Event.OriginalSource.Column
574 |
575 | # Determine current direction, toggle if column has been sorted before
576 | switch ($true) {
577 | {$sortDirections.ContainsKey($column.Header)} {
578 | $sortDirections[$column.Header] = -not $sortDirections[$column.Header]
579 | }
580 | default {
581 | # false for descending, true for ascending
582 | $sortDirections[$column.Header] = $false
583 | }
584 | }
585 | $direction = if ($sortDirections[$column.Header]) { "Ascending" } else { "Descending" }
586 |
587 | # Sort items
588 | $sortedItems = switch ($column.Header) {
589 | "IP Address" {
590 | $Sender.Items | Sort-Object -Property {[version]$_.IPaddress} -Descending:($direction -eq "Descending")
591 | }
592 | default {
593 | if ($column.DisplayMemberBinding.Path.Path) {
594 | $Sender.Items | Sort-Object -Property $column.DisplayMemberBinding.Path.Path -Descending:($direction -eq "Descending")
595 | } else {
596 | $Sender.Items
597 | }
598 | }
599 | }
600 | # Rebuild sorted list
601 | $Sender.Items.Clear()
602 | $sortedItems | ForEach-Object { $Sender.Items.Add($_) }
603 | }
604 |
605 | function Create-GradientEllipse {
606 | param (
607 | [bool]$isPingSuccessful,
608 | [double]$width = 9,
609 | [double]$height = 9
610 | )
611 |
612 | $ellipse = [Windows.Shapes.Ellipse]::new()
613 | $ellipse.Width = $width
614 | $ellipse.Height = $height
615 |
616 | if ($isPingSuccessful) {
617 | # Lighter blue gradient for successful ping
618 | $gradient = New-Object System.Windows.Media.RadialGradientBrush
619 | $gradient.GradientOrigin = New-Object System.Windows.Point(0.5, 0.5)
620 | $gradient.Center = New-Object System.Windows.Point(0.5, 0.5)
621 | $gradient.RadiusX = 0.5
622 | $gradient.RadiusY = 0.5
623 | $stop1 = New-Object System.Windows.Media.GradientStop
624 | $stop1.Color = [System.Windows.Media.Color]::FromArgb(255, 51, 204, 255)
625 | $stop1.Offset = 0
626 | $gradient.GradientStops.Add($stop1)
627 | $stop2 = New-Object System.Windows.Media.GradientStop
628 | $stop2.Color = [System.Windows.Media.Color]::FromArgb(255, 25, 153, 204)
629 | $stop2.Offset = 0.8
630 | $gradient.GradientStops.Add($stop2)
631 | $stop3 = New-Object System.Windows.Media.GradientStop
632 | $stop3.Color = [System.Windows.Media.Color]::FromArgb(255, 0, 102, 153)
633 | $stop3.Offset = 1
634 | $gradient.GradientStops.Add($stop3)
635 | } else {
636 | # Shades of gray for unsuccessful ping
637 | $gradient = New-Object System.Windows.Media.RadialGradientBrush
638 | $gradient.GradientOrigin = New-Object System.Windows.Point(0.5, 0.5)
639 | $gradient.Center = New-Object System.Windows.Point(0.5, 0.5)
640 | $gradient.RadiusX = 0.5
641 | $gradient.RadiusY = 0.5
642 | $stop4 = New-Object System.Windows.Media.GradientStop
643 | $stop4.Color = [System.Windows.Media.Color]::FromArgb(255, 220, 220, 220)
644 | $stop4.Offset = 0
645 | $gradient.GradientStops.Add($stop4)
646 | $stop5 = New-Object System.Windows.Media.GradientStop
647 | $stop5.Color = [System.Windows.Media.Color]::FromArgb(255, 160, 160, 160)
648 | $stop5.Offset = 0.8
649 | $gradient.GradientStops.Add($stop5)
650 | $stop6 = New-Object System.Windows.Media.GradientStop
651 | $stop6.Color = [System.Windows.Media.Color]::FromArgb(255, 100, 100, 100)
652 | $stop6.Offset = 1
653 | $gradient.GradientStops.Add($stop6)
654 | }
655 |
656 | $ellipse.Fill = $gradient
657 | return $ellipse
658 | }
659 |
660 | # Display network speed as KB/s -MB/s -GB/s
661 | function Format-Speed {
662 | param ([double]$speedInKBs)
663 | if ($speedInKBs -ge 1024 * 1024) { return "{0:N1}gb" -f ($speedInKBs / (1024 * 1024)) }
664 | elseif ($speedInKBs -ge 1024) { return "{0:N1}mb" -f ($speedInKBs / 1024) }
665 | else { return "{0:N0}kb" -f $speedInKBs }
666 | }
667 |
668 | # Initialize hashtable for Monitor Mode
669 | $global:syncHash = [Hashtable]::Synchronized(@{
670 | NetworkStats = @{}
671 | TCPConnections = @()
672 | LastUpdate = [DateTime]::Now
673 | Error = $null
674 | })
675 |
676 | function Start-NetMonBackgroundTask {
677 | $backgroundScript = {
678 | param($syncHash, $adapters)
679 | function Get-NetworkStatsWithTimeout {
680 | $statsPerAdapter = @{}
681 | $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
682 | $timeoutMs = 100
683 | $retryCount = 2
684 | for ($i = 0; $i -lt $retryCount; $i++) {
685 | try {
686 | $currentAdapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" -and $_.InterfaceDescription -notlike "*Loopback*" -and $_.InterfaceDescription -notlike "*ISATAP*" }
687 | if (-not $currentAdapters) {
688 | return $statsPerAdapter
689 | }
690 | foreach ($adapter in $currentAdapters) {
691 | $adapterName = $adapter.Name
692 | $stats = Get-NetAdapterStatistics -Name $adapterName -ErrorAction SilentlyContinue
693 | if ($stats) {
694 | $statsPerAdapter[$adapterName] = @{
695 | RxBytes = [double]$stats.ReceivedBytes
696 | TxBytes = [double]$stats.SentBytes
697 | Timestamp = [double](Get-Date).Ticks
698 | }
699 | } else {
700 | $global:syncHash.Error = "No stats returned for adapter: $adapterName"
701 | }
702 | }
703 | if ($statsPerAdapter.Count -gt 0) { break }
704 | Start-Sleep -Milliseconds 100
705 | } catch {
706 | $global:syncHash.Error = "Network stats error: $_"
707 | }
708 | $stopwatch.Stop()
709 | if ($stopwatch.ElapsedMilliseconds -gt $timeoutMs) { break }
710 | $stopwatch.Restart()
711 | }
712 | return $statsPerAdapter
713 | }
714 | function Get-TCPConnections {
715 | try {
716 | $connections = Get-NetTCPConnection | Where-Object { $_.State -eq "Established" }
717 | $tcpList = @()
718 | foreach ($conn in $connections) {
719 | $process = Get-CimInstance Win32_Process -Filter "ProcessId = $($conn.OwningProcess)" -ErrorAction SilentlyContinue
720 | $processName = if ($process) { $process.Name } else { "Unknown" }
721 | $tcpList += [PSCustomObject]@{
722 | LocalAddress = $conn.LocalAddress; LocalPort = $conn.LocalPort; RemoteAddress = $conn.RemoteAddress; RemotePort = $conn.RemotePort; ProcessName = $processName
723 | }
724 | }
725 | return $tcpList
726 | } catch {
727 | $global:syncHash.Error = "TCP connections error: $_"
728 | return @()
729 | }
730 | }
731 | while ($true) {
732 | try {
733 | $stats = Get-NetworkStatsWithTimeout
734 | $tcp = Get-TCPConnections
735 | $global:syncHash.NetworkStats = $stats
736 | $global:syncHash.TCPConnections = $tcp
737 | $global:syncHash.LastUpdate = [DateTime]::Now
738 | $global:syncHash.Error = $null
739 | } catch {
740 | Write-Host "Background task error: $_"
741 | $global:syncHash.Error = $_.ToString()
742 | }
743 | Start-Sleep -Milliseconds 1000
744 | }
745 | }
746 | $runspace = [PowerShell]::Create().AddScript($backgroundScript).AddArgument($syncHash).AddArgument($adapters)
747 | $runspace.RunspacePool = $RunspacePool
748 | return $runspace.BeginInvoke()
749 | }
750 |
751 | # Direct MAC request via iphlpapi.dll
752 | Add-Type -TypeDefinition @"
753 | using System;
754 | using System.Collections.Generic;
755 | using System.Runtime.InteropServices;
756 |
757 | public class MacAddressResolver
758 | {
759 | [DllImport("iphlpapi.dll", ExactSpelling = true)]
760 | public static extern int SendARP(uint DestIP, uint SrcIP, byte[] pMacAddr, ref int PhyAddrLen);
761 |
762 | public static string GetMacFromIP(string ipAddress)
763 | {
764 | try
765 | {
766 | System.Net.IPAddress ip = System.Net.IPAddress.Parse(ipAddress);
767 | byte[] macAddr = new byte[6];
768 | int macAddrLen = macAddr.Length;
769 | if (SendARP(BitConverter.ToUInt32(ip.GetAddressBytes(), 0), 0, macAddr, ref macAddrLen) == 0)
770 | {
771 | string[] str = new string[macAddr.Length];
772 | for (int i = 0; i < macAddr.Length; i++)
773 | {
774 | str[i] = macAddr[i].ToString("X2");
775 | }
776 | return string.Join(":", str);
777 | }
778 | else
779 | {
780 | return "Unknown";
781 | }
782 | }
783 | catch
784 | {
785 | return "Unknown";
786 | }
787 | }
788 | }
789 | "@
790 |
791 | # Get icons from DLL or EXE files via shell32.dll
792 | $getIcons = @"
793 | using System;
794 | using System.Drawing;
795 | using System.Runtime.InteropServices;
796 | using System.Windows.Interop;
797 | using System.Windows.Media.Imaging;
798 | using System.Windows;
799 |
800 | namespace System
801 | {
802 | public class IconExtractor
803 | {
804 | public static Icon Extract(string file, int number, bool largeIcon)
805 | {
806 | IntPtr large;
807 | IntPtr small;
808 | ExtractIconEx(file, number, out large, out small, 1);
809 | try
810 | {
811 | return Icon.FromHandle(largeIcon ? large : small);
812 | }
813 | catch
814 | {
815 | return null;
816 | }
817 | }
818 | public static BitmapSource IconToBitmapSource(Icon icon)
819 | {
820 | return Imaging.CreateBitmapSourceFromHIcon(
821 | icon.Handle,
822 | Int32Rect.Empty,
823 | BitmapSizeOptions.FromEmptyOptions());
824 | }
825 | [DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
826 | private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
827 | }
828 | }
829 | "@
830 |
831 | # Define WPF GUI Structure
832 | Add-Type -TypeDefinition $getIcons -ReferencedAssemblies System.Windows.Forms, System.Drawing, PresentationCore, PresentationFramework, WindowsBase
833 | [xml]$XAML = @'
834 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
889 |
924 |
940 |
958 |
991 |
1040 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 |
1102 |
1103 |
1104 |
1149 |
1150 |
1151 |
1152 |
1153 |
1154 |
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 |
1166 |
1167 |
1168 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 |
1230 |
1231 |
1232 |
1233 |
1234 |
1235 |
1236 |
1237 |
1238 |
1239 |
1240 |
1286 |
1287 |
1288 |
1289 |
1306 |
1307 |
1308 |
1309 |
1310 |
1311 |
1312 |
1313 |
1314 |
1315 |
1316 |
1317 |
1318 |
1319 |
1320 |
1321 |
1322 |
1323 |
1324 |
1325 |
1326 |
1327 |
1328 |
1329 |
1330 |
1331 |
1332 |
1333 |
1334 |
1335 |
1336 |
1337 |
1338 |
1339 |
1340 |
1341 |
1342 |
1343 |
1344 |
1345 |
1346 |
1347 |
1348 |
1349 |
1350 |
1351 |
1352 |
1353 |
1354 |
1355 |
1356 |
1357 |
1358 |
1359 |
1360 |
1361 |
1362 |
1363 |
1364 |
1365 |
1366 |
1367 |
1368 |
1369 |
1370 |
1371 |
1372 |
1373 |
1374 |
1375 |
1376 |
1377 |
1378 |
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 | M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5A6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z
1386 |
1387 |
1388 |
1389 |
1390 |
1391 |
1392 |
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 | M3,13H5.79L10.1,4.79L11.28,13.75L14.5,9.66L17.83,13H21V15H17L14.67,12.67L9.92,18.73L8.94,11.31L7,15H3V13Z
1400 |
1401 |
1402 |
1403 |
1404 |
1405 |
1406 |
1407 |
1408 |
1409 |
1410 |
1411 |
1412 |
1413 |
1414 |
1415 |
1416 |
1417 |
1418 |
1419 |
1420 |
1421 |
1422 |
1423 |
1424 |
1425 |
1426 |
1427 |
1428 |
1429 |
1430 |
1431 |
1432 |
1433 |
1434 |
1435 |
1436 |
1437 |
1477 |
1478 |
1479 |
1480 |
1481 |
1482 |
1483 |
1484 |
1485 |
1486 |
1487 |
1488 |
1489 |
1490 |
1491 |
1492 |
1493 |
1494 |
1495 |
1496 |
1497 |
1498 |
1499 |
1500 |
1501 |
1502 |
1503 |
1504 |
1505 |
1506 |
1507 |
1508 |
1509 |
1510 |
1511 |
1512 |
1513 |
1514 |
1515 |
1516 |
1517 |
1518 |
1519 |
1520 |
1521 |
1522 |
1523 |
1524 |
1525 |
1526 |
1527 |
1528 |
1529 |
1530 |
1531 |
1532 |
1533 |
1534 |
1535 |
1536 |
1537 |
1538 |
1539 |
1540 |
1541 |
1542 |
1543 |
1544 |
1545 |
1546 |
1547 |
1548 |
1549 |
1550 |
1551 |
1552 |
1553 |
1554 |
1555 | M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5A6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z
1556 |
1557 |
1558 |
1559 |
1560 |
1561 |
1562 |
1563 |
1564 |
1565 |
1566 |
1567 |
1568 |
1569 |
1570 |
1571 |
1572 |
1573 |
1574 |
1575 |
1576 |
1577 |
1578 |
1579 |
1580 |
1581 |
1582 |
1583 |
1584 |
1585 |
1586 |
1587 |
1588 |
1589 |
1590 |
1591 |
1592 |
1593 |
1594 |
1595 |
1596 |
1597 |
1598 |
1599 |
1600 |
1601 |
1602 |
1603 |
1604 |
1605 |
1606 |
1607 |
1608 |
1609 |
1610 |
1611 |
1612 |
1613 |
1614 |
1615 |
1616 |
1617 |
1618 |
1619 |
1620 |
1621 |
1622 |
1623 |
1624 |
1625 |
1626 |
1627 |
1628 |
1629 |
1630 |
1631 |
1632 |
1633 |
1634 |
1635 |
1636 |
1637 |
1638 |
1639 |
1640 |
1641 |
1642 |
1643 |
1644 |
1645 |
1646 |
1647 |
1648 |
1649 |
1650 |
1651 |
1652 |
1653 | M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M7,13H17V11H7
1654 |
1655 |
1656 |
1657 |
1658 |
1659 |
1660 |
1661 |
1662 |
1663 |
1664 |
1665 |
1666 |
1667 |
1668 |
1669 |
1670 |
1671 |
1672 |
1673 |
1674 |
1675 |
1676 |
1677 |
1678 |
1679 |
1680 |
1681 |
1682 |
1683 |
1684 |
1685 |
1686 |
1687 |
1688 |
1689 |
1690 |
1691 |
1692 |
1693 |
1694 |
1695 |
1696 |
1697 |
1698 |
1699 |
1700 |
1701 |
1702 |
1703 |
1704 |
1705 |
1706 |
1707 |
1708 |
1709 |
1710 |
1711 |
1712 |
1713 |
1714 |
1715 |
1716 |
1717 |
1718 |
1719 |
1720 |
1721 |
1722 |
1723 |
1724 |
1725 |
1726 |
1727 |
1728 |
1729 |
1730 |
1731 |
1732 |
1733 |
1734 |
1735 |
1736 |
1737 |
1738 |
1739 |
1740 |
1741 |
1742 |
1743 |
1744 |
1745 |
1746 |
1747 |
1748 |
1749 |
1750 |
1751 |
1752 |
1753 |
1754 |
1755 |
1756 |
1757 |
1758 |
1759 |
1760 |
1761 |
1762 |
1763 |
1764 |
1765 |
1766 |
1767 | M10,2C8.89,2 8,2.89 8,4V7C8,8.11 8.89,9 10,9H11V11H2V13H6V15H5C3.89,15 3,15.89 3,17V20C3,21.11 3.89,22 5,22H9C10.11,22 11,21.11 11,20V17C11,15.89 10.11,15 9,15H8V13H16V15H15C13.89,15 13,15.89 13,17V20C13,21.11 13.89,22 15,22H19C20.11,22 21,21.11 21,20V17C21,15.89 20.11,15 19,15H18V13H22V11H13V9H14C15.11,9 16,8.11 16,7V4C16,2.89 15.11,2 14,2H10M10,4H14V7H10V4M5,17H9V20H5V17M15,17H19V20H15V17Z
1768 |
1769 |
1770 |
1771 |
1772 |
1773 |
1774 |
1775 |
1776 |
1777 |
1778 |
1779 |
1780 |
1781 |
1782 | M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5A6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z
1783 |
1784 |
1785 |
1786 |
1787 |
1788 |
1789 |
1790 |
1791 |
1792 |
1793 |
1794 |
1795 |
1796 |
1797 |
1798 |
1799 |
1800 |
1801 |
1802 |
1803 |
1804 |
1805 |
1806 |
1807 |
1808 |
1809 |
1810 |
1811 |
1812 |
1813 |
1814 |
1815 |
1816 |
1817 |
1818 |
1819 |
1820 |
1821 |
1822 |
1823 |
1824 |
1825 | M19,8L15,12H18A6,6 0 0,1 12,18C11,18 10.03,17.75 9.2,17.3L7.74,18.76C8.97,19.54 10.43,20 12,20A8,8 0 0,0 20,12H23M6,12A6,6 0 0,1 12,6C13,6 13.97,6.25 14.8,6.7L16.26,5.24C15.03,4.46 13.57,4 12,4A8,8 0 0,0 4,12H1L5,16L9,12
1826 |
1827 |
1828 |
1829 |
1830 |
1831 |
1832 |
1833 |
1834 |
1835 |
1836 |
1837 |
1838 |
1839 |
1840 |
1841 |
1842 |
1843 |
1844 |
1845 |
1846 |
1847 |
1848 |
1888 |
1889 | 2
1890 |
1891 |
1892 |
1893 |
1894 |
1895 |
1896 |
1897 |
1898 |
1899 |
1900 |
1901 |
1902 |
1903 |
1904 |
1905 |
1906 |
1907 |
1908 |
1909 |
1910 |
1911 |
1912 |
1913 |
1914 |
1915 |
1916 |
1917 |
1918 |
1919 |
1920 |
1921 |
1922 |
1923 |
1924 |
1925 |
1926 |
1927 |
1928 |
1929 |
1930 |
1931 |
1932 |
1933 | '@
1934 |
1935 | # Load XAML
1936 | $reader = (New-Object System.Xml.XmlNodeReader $xaml)
1937 | try{$Main = [Windows.Markup.XamlReader]::Load( $reader )}
1938 | catch{$shell = New-Object -ComObject Wscript.Shell; $shell.Popup("$_",0,'XAML ERROR:',0x0) | Out-Null; Exit}
1939 |
1940 | # Store Form Objects In PowerShell
1941 | $xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "$($_.Name)" -Value $Main.FindName($_.Name)}
1942 |
1943 | # Set Title
1944 | $Main.Title = "$AppId"
1945 | $titleBar.Text = "$AppId"
1946 |
1947 | # Window Closing
1948 | $Main.Add_Closing({
1949 | if ($global:timer) {
1950 | try { $global:timer.Stop() } catch { Write-Host "Error stopping timer: $_" }
1951 | }
1952 | if ($Runspace) {
1953 | try {
1954 | $global:Runspace.EndInvoke($global:netMonHandle)
1955 | $global:Runspace.Dispose()
1956 | } catch { Write-Host "Error disposing netMonHandle: $_" }
1957 | }
1958 | Get-Job | Remove-Job -Force
1959 | if ($RunspacePool) {
1960 | try { $RunspacePool.Close(); $RunspacePool.Dispose() } catch { Write-Host "Error disposing RunspacePool: $_" }
1961 | }
1962 | $Main.Add_Closed({ [Environment]::Exit(0) })
1963 | })
1964 |
1965 | $Main.Add_ContentRendered({
1966 | # Define icons
1967 | $icons = @(
1968 | @{File = 'C:\Windows\System32\imageres.dll'; Index = 73; ElementName = "scanAdminIcon"; Type = "Image"},
1969 | @{File = 'C:\Windows\System32\mstscax.dll'; Index = 0; ElementName = "btnRDP"; Type = "Button"},
1970 | @{File = 'C:\Windows\System32\shell32.dll'; Index = 13; ElementName = "btnWebInterface"; Type = "Button"},
1971 | @{File = 'C:\Windows\System32\shell32.dll'; Index = 266; ElementName = "btnShare"; Type = "Button"}
1972 | )
1973 |
1974 | # Extract and set icons
1975 | foreach ($icon in $icons) {
1976 | $extractedIcon = [System.IconExtractor]::Extract($icon.File, $icon.Index, $true)
1977 | if ($extractedIcon) {
1978 | $bitmapSource = [System.IconExtractor]::IconToBitmapSource($extractedIcon)
1979 | $element = $Main.FindName($icon.ElementName)
1980 |
1981 | switch ($icon.Type) {
1982 | "Image" {
1983 | $element.Source = $bitmapSource
1984 | $element.SetValue([System.Windows.Media.RenderOptions]::BitmapScalingModeProperty, [System.Windows.Media.BitmapScalingMode]::HighQuality)
1985 | }
1986 | "Button" {
1987 | $imageWidth = 24
1988 | $imageHeight = 24
1989 | $image = New-Object System.Windows.Controls.Image -Property @{
1990 | Source = $bitmapSource;
1991 | Width = $imageWidth;
1992 | Height = $imageHeight;
1993 | Stretch = [System.Windows.Media.Stretch]::Fill
1994 | }
1995 | $image.SetValue([System.Windows.Media.RenderOptions]::BitmapScalingModeProperty, [System.Windows.Media.BitmapScalingMode]::HighQuality)
1996 | $element.Content = $image
1997 | }
1998 | }
1999 | }
2000 | }
2001 |
2002 | # Populate the ComboBoxes
2003 | function Initialize-IPCombo {
2004 | param($comboBox)
2005 | for ($i = 0; $i -le 255; $i++) {
2006 | $comboBox.Items.Add($i)
2007 | }
2008 | $comboBox.SelectedIndex = 0
2009 | }
2010 |
2011 | # Initialize Comboboxes
2012 | @('subnetOctet1', 'subnetOctet2', 'subnetOctet3') | ForEach-Object {
2013 | Initialize-IPCombo -comboBox ($Main.FindName($_))
2014 | }
2015 |
2016 | # Register ToggleMonitorButton mouse event handlers
2017 | $ToggleMonitorButton.Add_MouseEnter({
2018 | $storyboard = $ToggleMonitorButton.Resources["mouseEnterAnimation"]
2019 | $storyboard.Begin($ToggleMonitorButton)
2020 | })
2021 | $ToggleMonitorButton.Add_MouseLeave({
2022 | $storyboard = $ToggleMonitorButton.Resources["mouseLeaveAnimation"]
2023 | $storyboard.Begin($ToggleMonitorButton)
2024 | })
2025 |
2026 | # Initialize Monitor Mode Variables
2027 | $global:adapters = @()
2028 | $global:adapterStats = @{}
2029 | $global:speedHistory = @{}
2030 | $global:lastTime = $null
2031 | $global:canvasWidth = 830
2032 | $global:canvasHeight = 100
2033 | $global:maxPoints = 60
2034 | $global:historySize = 2
2035 | $global:resetThreshold = 100000
2036 | $global:maxSpeed = 1000
2037 | $global:LastSelectedItem = $null
2038 |
2039 | # Start Monitor Mode Background Task
2040 | try {
2041 | if (-not $RunspacePool) {
2042 | Show-Popup2 -Message "RunspacePool is not initialized." -Title "Error:"
2043 | return
2044 | }
2045 | if ($RunspacePool.RunspacePoolStateInfo.State -ne 'Opened') {
2046 | Show-Popup2 -Message "RunspacePool is not open." -Title "Error:"
2047 | return
2048 | }
2049 | $global:netMonHandle = Start-NetMonBackgroundTask
2050 | } catch {
2051 | Show-Popup2 -Message "Failed to start Monitor Mode background task: $_" -Title "Error:"
2052 | return
2053 | }
2054 |
2055 | # Initialize Adapters
2056 | try {
2057 | $initialAdapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" -and $_.InterfaceDescription -notlike "*Loopback*" -and $_.InterfaceDescription -notlike "*ISATAP*" }
2058 | if (-not $initialAdapters) {
2059 | Show-Popup2 -Message "No network adapters available." -Title "Error:"
2060 | return
2061 | }
2062 | foreach ($adapter in $initialAdapters) {
2063 | $adapterName = $adapter.Name
2064 | $global:adapters += $adapterName
2065 | $global:adapterStats[$adapterName] = @{
2066 | RxHistory = @()
2067 | TxHistory = @()
2068 | TimeHistory = @()
2069 | LastRxBytes = 0
2070 | LastTxBytes = 0
2071 | }
2072 | $global:speedHistory[$adapterName] = @{
2073 | RxSpeeds = @()
2074 | TxSpeeds = @()
2075 | }
2076 | if ($AdapterDropdown) {
2077 | $AdapterDropdown.Items.Add($adapterName) | Out-Null
2078 | }
2079 | }
2080 | if ($AdapterDropdown -and $AdapterDropdown.Items.Count -gt 0) {
2081 | $AdapterDropdown.SelectedIndex = 0
2082 | }
2083 | } catch {
2084 | Show-Popup2 -Message "Error initializing adapters: $_" -Title "Error:"
2085 | }
2086 |
2087 | # Initialize Timer
2088 | $global:timer = New-Object System.Windows.Threading.DispatcherTimer
2089 | $global:timer.Interval = [TimeSpan]::FromMilliseconds(1000)
2090 | $global:timer.Add_Tick({
2091 | try {
2092 | if ($global:syncHash.Error) {
2093 | if ($NetMonContentGrid -and $NetMonContentGrid.Visibility -eq [System.Windows.Visibility]::Visible) {
2094 | Show-Popup2 -Message "Background task error: $($global:syncHash.Error)" -Title "Error:"
2095 | }
2096 | return
2097 | }
2098 | $statsPerAdapter = $global:syncHash.NetworkStats
2099 | $tcpConnections = $global:syncHash.TCPConnections
2100 | $currentTime = [DateTime]::Now
2101 | if ($null -eq $global:lastTime) { $global:lastTime = $currentTime }
2102 | $deltaTime = [Math]::Max(0.2, ($currentTime - $global:lastTime).TotalSeconds)
2103 | $global:lastTime = $currentTime
2104 |
2105 | foreach ($adapterName in $global:adapters) {
2106 | $adapterStat = $global:adapterStats[$adapterName]
2107 | if ($statsPerAdapter.ContainsKey($adapterName)) {
2108 | $currentStats = $statsPerAdapter[$adapterName]
2109 | $rxBytes = $currentStats.RxBytes
2110 | $txBytes = $currentStats.TxBytes
2111 | $timestamp = $currentStats.Timestamp
2112 |
2113 | if ($adapterStat.LastRxBytes -gt 0 -and $adapterStat.LastTxBytes -gt 0) {
2114 | $resetRx = $rxBytes -lt $adapterStat.LastRxBytes -and ($adapterStat.LastRxBytes - $rxBytes) -gt $global:resetThreshold
2115 | $resetTx = $txBytes -lt $adapterStat.LastTxBytes -and ($adapterStat.LastTxBytes - $txBytes) -gt $global:resetThreshold
2116 | if ($resetRx -or $resetTx) {
2117 | $adapterStat.RxHistory = @()
2118 | $adapterStat.TxHistory = @()
2119 | $adapterStat.TimeHistory = @()
2120 | $global:speedHistory[$adapterName].RxSpeeds = @()
2121 | $global:speedHistory[$adapterName].TxSpeeds = @()
2122 | }
2123 | }
2124 |
2125 | $adapterStat.RxHistory += $rxBytes
2126 | $adapterStat.TxHistory += $txBytes
2127 | $adapterStat.TimeHistory += $timestamp
2128 | if ($adapterStat.RxHistory.Count -gt $global:historySize) {
2129 | $adapterStat.RxHistory = $adapterStat.RxHistory | Select-Object -Last $global:historySize
2130 | $adapterStat.TxHistory = $adapterStat.TxHistory | Select-Object -Last $global:historySize
2131 | $adapterStat.TimeHistory = $adapterStat.TimeHistory | Select-Object -Last $global:historySize
2132 | }
2133 | $adapterStat.LastRxBytes = $rxBytes
2134 | $adapterStat.LastTxBytes = $txBytes
2135 |
2136 | $rxSpeed = 0
2137 | $txSpeed = 0
2138 | if ($adapterStat.RxHistory.Count -ge 2 -and $adapterStat.TimeHistory.Count -ge 2) {
2139 | $totalRxDiff = 0
2140 | $totalTxDiff = 0
2141 | $totalTicks = 0
2142 | for ($i = 1; $i -lt $adapterStat.RxHistory.Count; $i++) {
2143 | $rxDiff = [double]($adapterStat.RxHistory[$i] - $adapterStat.RxHistory[$i-1])
2144 | $txDiff = [double]($adapterStat.TxHistory[$i] - $adapterStat.TxHistory[$i-1])
2145 | $tickDiff = [double]($adapterStat.TimeHistory[$i] - $adapterStat.TimeHistory[$i-1])
2146 | $totalRxDiff += $rxDiff
2147 | $totalTxDiff += $txDiff
2148 | $totalTicks += $tickDiff
2149 | }
2150 | $totalTime = $totalTicks / 10000000.0
2151 | if ($totalTime -gt 0) {
2152 | $rxSpeed = [Math]::Max(0, $totalRxDiff / $totalTime / 1024)
2153 | $txSpeed = [Math]::Max(0, $totalTxDiff / $totalTime / 1024)
2154 | }
2155 | }
2156 |
2157 | $global:speedHistory[$adapterName].RxSpeeds += $rxSpeed
2158 | $global:speedHistory[$adapterName].TxSpeeds += $txSpeed
2159 | if ($global:speedHistory[$adapterName].RxSpeeds.Count -gt $global:maxPoints) {
2160 | $global:speedHistory[$adapterName].RxSpeeds = $global:speedHistory[$adapterName].RxSpeeds | Select-Object -Last $global:maxPoints
2161 | $global:speedHistory[$adapterName].TxSpeeds = $global:speedHistory[$adapterName].TxSpeeds | Select-Object -Last $global:maxPoints
2162 | }
2163 | }
2164 | }
2165 |
2166 | if ($NetMonContentGrid -and $NetMonContentGrid.Visibility -eq [System.Windows.Visibility]::Visible) {
2167 | $Main.Dispatcher.Invoke([Action]{
2168 | if (-not $GraphCanvas -or -not $YLabelMax -or -not $YLabel75 -or -not $YLabel50 -or -not $YLabel25 -or -not $NetMonTCPList) {
2169 | return
2170 | }
2171 |
2172 | $childrenToRemove = $GraphCanvas.Children | Where-Object { $_.GetType().Name -eq "Line" }
2173 | foreach ($child in $childrenToRemove) { $GraphCanvas.Children.Remove($child) }
2174 |
2175 | $selectedAdapter = if ($AdapterDropdown -and $AdapterDropdown.Items.Count -gt 0) { $AdapterDropdown.SelectedItem } else { $global:adapters[0] }
2176 | if (-not $selectedAdapter) {
2177 | Show-Popup2 -Message "No adapter selected." -Title "Warning:"
2178 | return
2179 | }
2180 |
2181 | $rxSpeeds = $global:speedHistory[$selectedAdapter].RxSpeeds
2182 | $txSpeeds = $global:speedHistory[$selectedAdapter].TxSpeeds
2183 |
2184 | $maxRx = if ($rxSpeeds) { ($rxSpeeds | Measure-Object -Maximum).Maximum } else { 0 }
2185 | $maxTx = if ($txSpeeds) { ($txSpeeds | Measure-Object -Maximum).Maximum } else { 0 }
2186 | $maxSpeedCalc = [Math]::Max($maxRx, $maxTx)
2187 |
2188 | if ($maxSpeedCalc -le 25) { $global:maxSpeed = 25 }
2189 | elseif ($maxSpeedCalc -le 250) { $global:maxSpeed = 250 }
2190 | elseif ($maxSpeedCalc -le 500) { $global:maxSpeed = 500 }
2191 | elseif ($maxSpeedCalc -le 1000) { $global:maxSpeed = 1000 }
2192 | else { $global:maxSpeed = [Math]::Ceiling($maxSpeedCalc / 250) * 250 }
2193 |
2194 | $YLabelMax.Text = Format-Speed -speedInKBs $global:maxSpeed
2195 | $YLabel75.Text = Format-Speed -speedInKBs ($global:maxSpeed * 0.75)
2196 | $YLabel50.Text = Format-Speed -speedInKBs ($global:maxSpeed * 0.5)
2197 | $YLabel25.Text = Format-Speed -speedInKBs ($global:maxSpeed * 0.25)
2198 |
2199 | $yScale = $global:canvasHeight / $global:maxSpeed
2200 | $xScale = $global:canvasWidth / $global:maxPoints
2201 |
2202 | for ($i = 1; $i -le 3; $i++) {
2203 | $gridLine = New-Object System.Windows.Shapes.Line
2204 | $gridLine.X1 = 0
2205 | $gridLine.X2 = $global:canvasWidth
2206 | $gridLine.Y1 = $i * ($global:canvasHeight / 4)
2207 | $gridLine.Y2 = $i * ($global:canvasHeight / 4)
2208 | $gridLine.Stroke = New-Object System.Windows.Media.SolidColorBrush -ArgumentList ([System.Windows.Media.Color]::FromRgb(63, 63, 70))
2209 | $gridLine.StrokeThickness = 1
2210 | $GraphCanvas.Children.Add($gridLine)
2211 | }
2212 |
2213 | for ($i = 1; $i -lt $rxSpeeds.Count; $i++) {
2214 | $x1 = $global:canvasWidth - ($rxSpeeds.Count - $i) * $xScale
2215 | $y1 = $global:canvasHeight - ($rxSpeeds[$i-1] * $yScale)
2216 | $x2 = $global:canvasWidth - ($rxSpeeds.Count - 1 - $i) * $xScale
2217 | $y2 = $global:canvasHeight - ($rxSpeeds[$i] * $yScale)
2218 | $line = New-Object System.Windows.Shapes.Line
2219 | $line.X1 = $x1; $line.Y1 = $y1; $line.X2 = $x2; $line.Y2 = $y2
2220 | $line.Stroke = New-Object System.Windows.Media.SolidColorBrush -ArgumentList ([System.Windows.Media.Color]::FromRgb(0, 191, 255))
2221 | $line.StrokeThickness = 2
2222 | $GraphCanvas.Children.Add($line)
2223 | }
2224 |
2225 | for ($i = 1; $i -lt $txSpeeds.Count; $i++) {
2226 | $x1 = $global:canvasWidth - ($txSpeeds.Count - $i) * $xScale
2227 | $y1 = $global:canvasHeight - ($txSpeeds[$i-1] * $yScale)
2228 | $x2 = $global:canvasWidth - ($txSpeeds.Count - 1 - $i) * $xScale
2229 | $y2 = $global:canvasHeight - ($txSpeeds[$i] * $yScale)
2230 | $line = New-Object System.Windows.Shapes.Line
2231 | $line.X1 = $x1; $line.Y1 = $y1; $line.X2 = $x2; $line.Y2 = $y2
2232 | $line.Stroke = New-Object System.Windows.Media.SolidColorBrush -ArgumentList ([System.Windows.Media.Color]::FromRgb(178, 0, 178))
2233 | $line.StrokeThickness = 2
2234 | $GraphCanvas.Children.Add($line)
2235 | }
2236 |
2237 | if ($tcpConnections) {
2238 | $NetMonTCPList.ItemsSource = $tcpConnections
2239 | $NetMonTotalConnections.Text = "$($tcpConnections.Count) active connections"
2240 |
2241 | if ($global:LastSelectedItem) {
2242 | foreach ($item in $NetMonTCPList.Items) {
2243 | # Compare key properties to find the matching item
2244 | if ($item.LocalAddress -eq $global:LastSelectedItem.LocalAddress -and
2245 | $item.LocalPort -eq $global:LastSelectedItem.LocalPort -and
2246 | $item.RemoteAddress -eq $global:LastSelectedItem.RemoteAddress -and
2247 | $item.RemotePort -eq $global:LastSelectedItem.RemotePort -and
2248 | $item.ProcessName -eq $global:LastSelectedItem.ProcessName) {
2249 | $NetMonTCPList.SelectedItem = $item
2250 | break
2251 | }
2252 | }
2253 | }
2254 | }
2255 | }, [System.Windows.Threading.DispatcherPriority]::Render)
2256 | }
2257 | } catch {
2258 | if ($NetMonContentGrid -and $NetMonContentGrid.Visibility -eq [System.Windows.Visibility]::Visible) {
2259 | Show-Popup2 -Message "UI update error: $_" -Title "Error:"
2260 | }
2261 | }
2262 | })
2263 |
2264 | # Start Timer
2265 | $global:timer.Start()
2266 |
2267 | # Toggle Scanner/Monitor Mode button Click Handler
2268 | $ToggleMonitorButton.Add_Click({
2269 | try {
2270 | if (-not $NetMonContentGrid -or -not $IPScannerContentGrid -or -not $HideGraphic -or -not $ShowGraphic) {
2271 | Show-Popup2 -Message "UI elements not found." -Title "Error:"
2272 | return
2273 | }
2274 | if ($NetMonContentGrid.Visibility -eq [System.Windows.Visibility]::Visible) {
2275 | $ToggleMonitorButton.ToolTip = "Monitor Mode"
2276 | $NetMonContentGrid.Visibility = [System.Windows.Visibility]::Hidden
2277 | $IPScannerContentGrid.Visibility = [System.Windows.Visibility]::Visible
2278 | $HideGraphic.Visibility = [System.Windows.Visibility]::Hidden
2279 | $ShowGraphic.Visibility = [System.Windows.Visibility]::Visible
2280 | } else {
2281 | $ToggleMonitorButton.ToolTip = "Scanner Mode"
2282 | $NetMonContentGrid.Visibility = [System.Windows.Visibility]::Visible
2283 | $IPScannerContentGrid.Visibility = [System.Windows.Visibility]::Hidden
2284 | $HideGraphic.Visibility = [System.Windows.Visibility]::Visible
2285 | $ShowGraphic.Visibility = [System.Windows.Visibility]::Hidden
2286 | }
2287 | } catch {
2288 | Show-Popup2 -Message "Error toggling view: $_" -Title "Error:"
2289 | }
2290 | })
2291 |
2292 | # Monitor Mode Context Menu Handlers
2293 | $contextMenuElements = @('NetMonExportContext', 'CopyLocalAddress', 'CopyLocalPort', 'CopyRemoteAddress', 'CopyRemotePort', 'CopyProcessName', 'CopyAll', 'NetMonExportToHTML', 'NetMonExportToCSV', 'NetMonExportToText')
2294 |
2295 | $NetMonTCPList.Add_SelectionChanged({
2296 | $NetMonExportContext.IsEnabled = $NetMonTCPList.Items.Count -gt 0
2297 | })
2298 |
2299 | $NetMonTCPList.Add_PreviewMouseLeftButtonUp({
2300 | param($sender, $e)
2301 | $originalSource = $e.OriginalSource
2302 | $listViewItem = $null
2303 | $currentElement = $originalSource
2304 | while ($currentElement -ne $null -and $listViewItem -eq $null) {
2305 | if ($currentElement -is [System.Windows.Controls.ListViewItem]) { $listViewItem = $currentElement }
2306 | $currentElement = [System.Windows.Media.VisualTreeHelper]::GetParent($currentElement)
2307 | }
2308 | if ($listViewItem -ne $null) {
2309 | $NetMonTCPList.SelectedItems.Clear()
2310 | $listViewItem.IsSelected = $true
2311 | $global:LastSelectedItem = $listViewItem.Content
2312 | }
2313 | $e.Handled = $true
2314 | })
2315 |
2316 | $NetMonTCPList.Add_PreviewMouseRightButtonDown({
2317 | param($sender, $e)
2318 | $originalSource = $e.OriginalSource
2319 | $listViewItem = $null
2320 | $currentElement = $originalSource
2321 | while ($currentElement -ne $null -and $listViewItem -eq $null) {
2322 | if ($currentElement -is [System.Windows.Controls.ListViewItem]) { $listViewItem = $currentElement }
2323 | $currentElement = [System.Windows.Media.VisualTreeHelper]::GetParent($currentElement)
2324 | }
2325 | $NetMonTCPList.SelectedItems.Clear()
2326 | if ($listViewItem -ne $null) { $listViewItem.IsSelected = $true }
2327 | $NetMonTCPList.ContextMenu = $Main.FindResource("NetMonRightClickContextMenu")
2328 | $NetMonTCPList.ContextMenu.IsOpen = $true
2329 | $e.Handled = $true
2330 | })
2331 |
2332 | $NetMonTCPList.Add_MouseDoubleClick({
2333 | param($sender, $e)
2334 | $originalSource = $e.OriginalSource
2335 | $listViewItem = $null
2336 | $currentElement = $originalSource
2337 | while ($currentElement -ne $null -and $listViewItem -eq $null) {
2338 | if ($currentElement -is [System.Windows.Controls.ListViewItem]) { $listViewItem = $currentElement }
2339 | $currentElement = [System.Windows.Media.VisualTreeHelper]::GetParent($currentElement)
2340 | }
2341 | $NetMonTCPList.SelectedItems.Clear()
2342 | if ($listViewItem -ne $null) {
2343 | $listViewItem.IsSelected = $true
2344 | $global:LastSelectedItem = $listViewItem.Content
2345 | $NetMonTCPList.ContextMenu = $Main.FindResource("NetMonDoubleClickContextMenu")
2346 | $NetMonTCPList.ContextMenu.IsOpen = $true
2347 | }
2348 | $e.Handled = $true
2349 | })
2350 |
2351 | $CopyLocalAddress.Add_Click({
2352 | $item = if ($NetMonTCPList.SelectedItem) { $NetMonTCPList.SelectedItem } else { $global:LastSelectedItem }
2353 | if ($item) { Set-Clipboard -Value $item.LocalAddress; Show-Popup2 -Message "Local Address copied!" -Title "Info:" }
2354 | else { Show-Popup2 -Message "No item selected!" -Title "Warning:" }
2355 | })
2356 |
2357 | $CopyLocalPort.Add_Click({
2358 | $item = if ($NetMonTCPList.SelectedItem) { $NetMonTCPList.SelectedItem } else { $global:LastSelectedItem }
2359 | if ($item) { Set-Clipboard -Value $item.LocalPort; Show-Popup2 -Message "Local Port copied!" -Title "Info:" }
2360 | else { Show-Popup2 -Message "No item selected!" -Title "Warning:" }
2361 | })
2362 |
2363 | $CopyRemoteAddress.Add_Click({
2364 | $item = if ($NetMonTCPList.SelectedItem) { $NetMonTCPList.SelectedItem } else { $global:LastSelectedItem }
2365 | if ($item) { Set-Clipboard -Value $item.RemoteAddress; Show-Popup2 -Message "Remote Address copied!" -Title "Info:" }
2366 | else { Show-Popup2 -Message "No item selected!" -Title "Warning:" }
2367 | })
2368 |
2369 | $CopyRemotePort.Add_Click({
2370 | $item = if ($NetMonTCPList.SelectedItem) { $NetMonTCPList.SelectedItem } else { $global:LastSelectedItem }
2371 | if ($item) { Set-Clipboard -Value $item.RemotePort; Show-Popup2 -Message "Remote Port copied!" -Title "Info:" }
2372 | else { Show-Popup2 -Message "No item selected!" -Title "Warning:" }
2373 | })
2374 |
2375 | $CopyProcessName.Add_Click({
2376 | $item = if ($NetMonTCPList.SelectedItem) { $NetMonTCPList.SelectedItem } else { $global:LastSelectedItem }
2377 | if ($item) { Set-Clipboard -Value $item.ProcessName; Show-Popup2 -Message "Process Name copied!" -Title "Info:" }
2378 | else { Show-Popup2 -Message "No item selected!" -Title "Warning:" }
2379 | })
2380 |
2381 | $CopyAll.Add_Click({
2382 | $item = if ($NetMonTCPList.SelectedItem) { $NetMonTCPList.SelectedItem } else { $global:LastSelectedItem }
2383 | if ($item) {
2384 | $details = "Local Address: $($item.LocalAddress)`nLocal Port: $($item.LocalPort)`nRemote Address: $($item.RemoteAddress)`nRemote Port: $($item.RemotePort)`nProcess Name: $($item.ProcessName)"
2385 | Set-Clipboard -Value $details
2386 | Show-Popup2 -Message "All details copied!" -Title "Info:"
2387 | } else {
2388 | Show-Popup2 -Message "No item selected!" -Title "Warning:"
2389 | }
2390 | })
2391 |
2392 | $NetMonExportToHTML.Add_Click({
2393 | if ($NetMonTCPList.Items.Count -eq 0) { Show-Popup2 -Message "No data to export!" -Title "Warning:"; return }
2394 | $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
2395 | $saveFileDialog.Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*"
2396 | $saveFileDialog.FileName = "TCP_Connections"
2397 | if ($saveFileDialog.ShowDialog() -eq "OK") {
2398 | $path = $saveFileDialog.FileName
2399 | try {
2400 | $htmlContent = "
TCP Connections TCP Connections Date/Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
Total Connections: $($NetMonTCPList.Items.Count)
Local Address Local Port Remote Address Remote Port Process Name "
2401 | $NetMonTCPList.Items | ForEach-Object { $htmlContent += "$($_.LocalAddress) $($_.LocalPort) $($_.RemoteAddress) $($_.RemotePort) $($_.ProcessName) " }
2402 | $htmlContent += "
"
2403 | [System.IO.File]::WriteAllText($path, $htmlContent)
2404 | Show-Popup2 -Message "Export to HTML completed!" -Title "Export:"
2405 | } catch {
2406 | Show-Popup2 -Message "Error during export: $_" -Title "ERROR:"
2407 | }
2408 | }
2409 | })
2410 |
2411 | $NetMonExportToCSV.Add_Click({
2412 | if ($NetMonTCPList.Items.Count -eq 0) { Show-Popup2 -Message "No data to export!" -Title "Warning:"; return }
2413 | $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
2414 | $saveFileDialog.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*"
2415 | $saveFileDialog.FileName = "TCP_Connections"
2416 | if ($saveFileDialog.ShowDialog() -eq "OK") {
2417 | $path = $saveFileDialog.FileName
2418 | try {
2419 | $csvHeader = "Local Address,Local Port,Remote Address,Remote Port,Process Name"
2420 | $csvContent = $NetMonTCPList.Items | ForEach-Object { "`r`n$($_.LocalAddress.Replace(',','')),$($_.LocalPort),$($_.RemoteAddress.Replace(',','')),$($_.RemotePort),$($_.ProcessName.Replace(',',''))" }
2421 | [System.IO.File]::WriteAllLines($path, ($csvHeader + $csvContent))
2422 | Show-Popup2 -Message "Export to CSV completed!" -Title "Export:"
2423 | } catch {
2424 | Show-Popup2 -Message "Error during export: $_" -Title "ERROR:"
2425 | }
2426 | }
2427 | })
2428 |
2429 | $NetMonExportToText.Add_Click({
2430 | if ($NetMonTCPList.Items.Count -eq 0) { Show-Popup2 -Message "No data to export!" -Title "Warning:"; return }
2431 | $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
2432 | $saveFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
2433 | $saveFileDialog.FileName = "TCP_Connections"
2434 | if ($saveFileDialog.ShowDialog() -eq "OK") {
2435 | $path = $saveFileDialog.FileName
2436 | try {
2437 | $textContent = "TCP CONNECTIONS`n`nDATE/TIME: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`nTOTAL CONNECTIONS: $($NetMonTCPList.Items.Count)`n`n--------------------------------------`n"
2438 | $textContent += $NetMonTCPList.Items | ForEach-Object { "Local Address: $($_.LocalAddress)`nLocal Port: $($_.LocalPort)`nRemote Address: $($_.RemoteAddress)`nRemote Port: $($_.RemotePort)`nProcess Name: $($_.ProcessName)`n--------------------------------------`n" }
2439 | [System.IO.File]::WriteAllText($path, $textContent)
2440 | Show-Popup2 -Message "Export to Text completed!" -Title "Export:"
2441 | } catch {
2442 | Show-Popup2 -Message "Error during export: $_" -Title "ERROR:"
2443 | }
2444 | }
2445 | })
2446 |
2447 | # Bring window to foreground
2448 | $Main.Dispatcher.Invoke([action]{
2449 | $Main.Activate()
2450 | }, [Windows.Threading.DispatcherPriority]::Background)
2451 | })
2452 |
2453 | # Center main window
2454 | $screen = [System.Windows.SystemParameters]::WorkArea
2455 | $windowLeft = ($screen.Width - $Main.Width) / 2
2456 | $windowTop = ($screen.Height - $Main.Height) / 2
2457 | $Main.Left = $windowLeft
2458 | $Main.Top = $windowTop
2459 |
2460 | $btnMinimize.Add_Click({
2461 | $Main.WindowState = [System.Windows.WindowState]::Minimized
2462 | })
2463 |
2464 | $btnMinimize.Add_MouseEnter({
2465 | $btnMinimize.Background='#BBBBBB'
2466 | })
2467 | $btnMinimize.Add_MouseLeave({
2468 | $btnMinimize.Background='#DDDDDD'
2469 | })
2470 |
2471 | $btnClose.Add_Click({
2472 | $Main.Close()
2473 | })
2474 |
2475 | $btnClose.Add_MouseEnter({
2476 | $btnClose.Background='#ff0000'
2477 | })
2478 | $btnClose.Add_MouseLeave({
2479 | $btnClose.Background='#DDDDDD'
2480 | })
2481 |
2482 | $Main.Add_MouseLeftButtonDown({
2483 | $Main.DragMove()
2484 | })
2485 |
2486 | $pCloseButton.Add_Click({
2487 | $PopupCanvas.Visibility = 'Hidden'
2488 | })
2489 |
2490 | $pCloseButton.Add_MouseEnter({
2491 | $pCloseButton.Background='#ff0000'
2492 | })
2493 | $pCloseButton.Add_MouseLeave({
2494 | $pCloseButton.Background='#111111'
2495 | })
2496 |
2497 | $pCloseButton2.Add_Click({
2498 | $PopupCanvas2.Visibility = 'Hidden'
2499 | if(-not $btnScan.IsEnabled){
2500 | $global:abortscan = $true
2501 | $ProgressText.Visibility = 'Collapsed'
2502 | $btnScan.Visibility = 'Visible'
2503 | $ProgressBar.Visibility = 'Collapsed'
2504 | $ProgressBar.Value = 0
2505 | Update-uiMain
2506 | $btnScan.IsEnabled = $true
2507 | $Scan.IsEnabled = $true
2508 | }
2509 | $global:CtrlIsDown = $false
2510 | if ($global:gatewayPrefix -ne $originalGatewayPrefix) {
2511 | $scanButtonText.Text = 'Custom Scan'
2512 | } else {
2513 | $scanButtonText.Text = 'Scan'
2514 | }
2515 | $scanAdminIcon.Visibility = 'Collapsed'
2516 | })
2517 |
2518 | $pCloseButton2.Add_MouseEnter({
2519 | $pCloseButton2.Background='#ff0000'
2520 | })
2521 | $pCloseButton2.Add_MouseLeave({
2522 | $pCloseButton2.Background='#111111'
2523 | })
2524 |
2525 | function Show-SubnetPopup {
2526 | $btnOK2.Visibility = 'Visible'
2527 | $PopupTitle2.Text = 'Segment Exploration'
2528 | $PopupText2.Visibility = 'Collapsed'
2529 | $SubnetInput.Visibility = 'Visible'
2530 | $ScanPanel.Visibility = 'Collapsed'
2531 | $ResultsList.Visibility = 'Collapsed'
2532 | $imgPortScan.Visibility = 'Collapsed'
2533 | $subnetIcon.Visibility = 'Visible'
2534 | $PopupCanvas2.SetValue([System.Windows.Controls.Canvas]::LeftProperty, [System.Windows.Controls.Canvas]::GetLeft($listView) + 10)
2535 | $PopupCanvas2.SetValue([System.Windows.Controls.Canvas]::TopProperty, [System.Windows.Controls.Canvas]::GetTop($listView) + 10)
2536 | $PopupCanvas2.Visibility = 'Visible'
2537 | }
2538 |
2539 | function Show-PortScanPopup {
2540 | $btnOK2.Visibility = 'Collapsed'
2541 | $PopupTitle2.Text = "$global:target"
2542 | $SubnetInput.Visibility = 'Collapsed'
2543 | $ScanPanel.Visibility = 'Visible'
2544 | $ResultsList.Visibility = 'Visible'
2545 | $imgPortScan.Visibility = 'Visible'
2546 | $subnetIcon.Visibility = 'Collapsed'
2547 | $PopupCanvas2.SetValue([System.Windows.Controls.Canvas]::LeftProperty, [System.Windows.Controls.Canvas]::GetLeft($listView) + 10)
2548 | $PopupCanvas2.SetValue([System.Windows.Controls.Canvas]::TopProperty, [System.Windows.Controls.Canvas]::GetTop($listView) + 10)
2549 | $PopupCanvas2.Visibility = 'Visible'
2550 | }
2551 |
2552 | function Show-Popup2 {
2553 | param (
2554 | [string]$Message,
2555 | [string]$Title = 'Info',
2556 | [bool]$IsSubnetPopup = $false
2557 | )
2558 | $SubnetInput.Visibility = 'Collapsed'
2559 | $ScanPanel.Visibility = 'Collapsed'
2560 | $ResultsList.Visibility = 'Collapsed'
2561 | $PopupText2.Visibility = 'Visible'
2562 | $btnOK2.Visibility = 'Visible'
2563 |
2564 | $PopupTitle2.Text = $Title
2565 | $PopupText2.Text = $Message
2566 |
2567 | $centerX = ($Main.ActualWidth - $PopupBorder2.ActualWidth) / 2
2568 | $centerY = ($Main.ActualHeight - $PopupBorder2.ActualHeight) / 2
2569 | $PopupCanvas2.SetValue([System.Windows.Controls.Canvas]::LeftProperty, [System.Windows.Controls.Canvas]::GetLeft($listView) + 10)
2570 | $PopupCanvas2.SetValue([System.Windows.Controls.Canvas]::TopProperty, [System.Windows.Controls.Canvas]::GetTop($listView) + 10)
2571 |
2572 | $PopupCanvas2.Visibility = 'Visible'
2573 | }
2574 |
2575 | $btnPortScan.Add_Click({
2576 | $global:target = $pIP.Text -replace 'IP: '
2577 |
2578 | # Set multi-popup for portscan
2579 | Show-PortScanPopup
2580 | $PopupText2.Visibility = 'Collapsed'
2581 | $SubnetInput.Visibility = 'Collapsed'
2582 |
2583 | # Show port scanning elements
2584 | $btnScan.Visibility = 'Visible'
2585 | $btnScanText.Text = 'Scan Ports'
2586 | $ProgressBar.Visibility = 'Collapsed'
2587 | $ProgressText.Visibility = 'Visible'
2588 | $ProgressText.Text = ''
2589 | $cmbPortRange.Visibility = 'Visible'
2590 | $ResultsList.Visibility = 'Visible'
2591 | $ResultsList.Items.Clear()
2592 | $PopupCanvas2.Visibility = 'Visible'
2593 |
2594 | $btnOK2.Content = 'OK'
2595 | $ButtonStackPanel2.Visibility = 'Visible'
2596 |
2597 | # Initialize combobox if not already done
2598 | if ($cmbPortRange.Items.Count -eq 0) {
2599 | for ($start = 1; $start -le 65535; $start += 4000) {
2600 | $end = [Math]::Min($start + 3999, 65535)
2601 | $range = "$start-$end"
2602 | $cmbPortRange.Items.Add($range) | Out-Null
2603 | }
2604 | $cmbPortRange.SelectedIndex = 0
2605 | }
2606 | })
2607 |
2608 | $btnScan.Add_Click({
2609 | $btnScan.IsEnabled = $false
2610 | $Scan.IsEnabled = $false
2611 | # Check if anything is selected in the ComboBox
2612 | if ($cmbPortRange.SelectedIndex -ge 0) {
2613 | $selectedRange = $cmbPortRange.SelectedItem.ToString()
2614 | $portRange = $selectedRange -split '-' | ForEach-Object {[int]$_}
2615 | $startPort, $endPort = $portRange
2616 | $totalPorts = $endPort - $startPort + 1
2617 |
2618 | if($ResultsList.Items){
2619 | $ResultsList.Items.Clear()
2620 | }
2621 | $openPorts = @()
2622 | $ProgressText.Text = 'Scanning Ports'
2623 | $btnScan.Visibility = 'Collapsed'
2624 | $ProgressBar.Visibility = 'Visible'
2625 | $ProgressText.Visibility = 'Visible'
2626 | Update-uiMain
2627 |
2628 | for ($port = $startPort; $port -le $endPort; $port++) {
2629 | $result = Test-Port -computer $global:target -port $port
2630 | if ($result) {
2631 | $openPorts += $result
2632 | $ResultsList.Items.Add($result)
2633 | }
2634 | $progress = (($port - $startPort + 1) / $totalPorts) * 100
2635 | $ProgressBar.Value = $progress
2636 | Update-uiMain
2637 | if($abortscan){
2638 | $ResultsList.Items.Clear()
2639 | Update-uiMain
2640 | $global:abortscan = $false
2641 | break
2642 | }
2643 | }
2644 |
2645 | if ($openPorts.Count -eq 0) {
2646 | $ResultsList.Items.Add("No open ports found in the specified range.")
2647 | }
2648 | $ProgressText.Visibility = 'Collapsed'
2649 | $btnScan.Visibility = 'Visible'
2650 | $ProgressBar.Visibility = 'Collapsed'
2651 | $ProgressBar.Value = 0
2652 | Update-uiMain
2653 | } else {
2654 | $ResultsList.Items.Add("Please select a port range.")
2655 | }
2656 | $btnScan.IsEnabled = $true
2657 | $Scan.IsEnabled = $true
2658 | })
2659 |
2660 | # Window Icon
2661 | $windowIcon = [System.IconExtractor]::Extract('C:\Windows\System32\shell32.dll', 18, $true)
2662 | if ($windowIcon) {
2663 | $bitmapSource = [System.IconExtractor]::IconToBitmapSource($windowIcon)
2664 | $Main.Icon = $bitmapSource
2665 | $Main.TaskbarItemInfo.Overlay = $bitmapSource
2666 | $Main.TaskbarItemInfo.Description = $AppId
2667 | ($Main.FindName('WindowIconImage')).Source = $bitmapSource
2668 | ($Main.FindName('WindowIconImage')).SetValue([System.Windows.Media.RenderOptions]::BitmapScalingModeProperty, [System.Windows.Media.BitmapScalingMode]::HighQuality)
2669 | }
2670 |
2671 | $ChangeSubnet.Add_Click({
2672 | $parts = $global:gatewayPrefix -split '\.'
2673 | if ($parts.Length -ge 3) {
2674 | $subnetOctet1.SelectedItem = [int]$parts[0]
2675 | $subnetOctet2.SelectedItem = [int]$parts[1]
2676 | $subnetOctet3.SelectedItem = [int]$parts[2]
2677 | } else {
2678 | $subnetOctet1.SelectedItem = 192
2679 | $subnetOctet2.SelectedItem = 168
2680 | $subnetOctet3.SelectedItem = 1
2681 | }
2682 | Show-SubnetPopup
2683 | })
2684 |
2685 | $btnReset.Add_Click({
2686 | if ($originalGatewayPrefix) {
2687 | $parts = $originalGatewayPrefix -split '\.'
2688 | if ($parts.Length -ge 3) {
2689 | $subnetOctet1.SelectedItem = [int]$parts[0]
2690 | $subnetOctet2.SelectedItem = [int]$parts[1]
2691 | $subnetOctet3.SelectedItem = [int]$parts[2]
2692 | $global:gatewayPrefix = $originalGatewayPrefix
2693 | }
2694 | }
2695 | })
2696 |
2697 | $btnOK2.Add_Click({
2698 | if ($SubnetInput.Visibility -eq 'Visible') {
2699 | $global:gatewayPrefix = "{0}.{1}.{2}." -f $subnetOctet1.SelectedItem, $subnetOctet2.SelectedItem, $subnetOctet3.SelectedItem
2700 | }
2701 | $global:CtrlIsDown = $false
2702 | if ($global:gatewayPrefix -ne $originalGatewayPrefix) {
2703 | $scanButtonText.Text = 'Custom Scan'
2704 | } else {
2705 | $scanButtonText.Text = 'Scan'
2706 | }
2707 | $scanAdminIcon.Visibility = 'Collapsed'
2708 | $PopupCanvas2.Visibility = 'Hidden'
2709 | })
2710 |
2711 | $btnOK2.Add_MouseEnter({
2712 | $btnOK2.Foreground='#000000'
2713 | $btnOK2.Background='#CCCCCC'
2714 | })
2715 | $btnOK2.Add_MouseLeave({
2716 | $btnOK2.Foreground='#EEEEEE'
2717 | $btnOK2.Background='#111111'
2718 | })
2719 |
2720 | $btnRDP.Add_Click({
2721 | &mstsc /v:$tryToConnect
2722 | })
2723 |
2724 | $btnWebInterface.Add_Click({
2725 | # Priority order: HTTP/HTTPS
2726 | if($script:httpAvailable -eq 1){
2727 | Start-Process "`"http://$tryToConnect`""
2728 | } else {
2729 | Start-Process "`"https://$tryToConnect`""
2730 | }
2731 | })
2732 |
2733 | $btnShare.Add_Click({
2734 | &explorer "`"\\$tryToConnect`""
2735 | })
2736 |
2737 | # Button Animation Triggers
2738 | $btnRDP.Add_MouseEnter({
2739 | $btnRDP.FindResource("mouseEnterAnimation").Begin($btnRDP)
2740 | })
2741 |
2742 | $btnRDP.Add_MouseLeave({
2743 | $btnRDP.FindResource("mouseLeaveAnimation").Begin($btnRDP)
2744 | })
2745 |
2746 | $btnReset.Add_MouseEnter({
2747 | $btnReset.FindResource("mouseEnterAnimation").Begin($btnReset)
2748 | })
2749 |
2750 | $btnReset.Add_MouseLeave({
2751 | $btnReset.FindResource("mouseLeaveAnimation").Begin($btnReset)
2752 | })
2753 |
2754 | $btnWebInterface.Add_MouseEnter({
2755 | $btnWebInterface.FindResource("mouseEnterAnimation").Begin($btnWebInterface)
2756 | })
2757 |
2758 | $btnWebInterface.Add_MouseLeave({
2759 | $btnWebInterface.FindResource("mouseLeaveAnimation").Begin($btnWebInterface)
2760 | })
2761 |
2762 | $btnShare.Add_MouseEnter({
2763 | $btnShare.FindResource("mouseEnterAnimation").Begin($btnShare)
2764 | })
2765 |
2766 | $btnShare.Add_MouseLeave({
2767 | $btnShare.FindResource("mouseLeaveAnimation").Begin($btnShare)
2768 | })
2769 |
2770 | $btnNone.Add_MouseEnter({
2771 | $btnNone.FindResource("mouseEnterAnimation").Begin($btnNone)
2772 | })
2773 |
2774 | $btnNone.Add_MouseLeave({
2775 | $btnNone.FindResource("mouseLeaveAnimation").Begin($btnNone)
2776 | })
2777 |
2778 | $btnPortScan.Add_MouseEnter({
2779 | $btnPortScan.FindResource("mouseEnterAnimation").Begin($btnPortScan)
2780 | })
2781 |
2782 | $btnPortScan.Add_MouseLeave({
2783 | $btnPortScan.FindResource("mouseLeaveAnimation").Begin($btnPortScan)
2784 | })
2785 |
2786 | # Export List in HTML format
2787 | $ExportToHTML.Add_Click({
2788 | $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
2789 | $saveFileDialog.Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*"
2790 | $saveFileDialog.FileName = "Network_Scan_Results"
2791 | if ($saveFileDialog.ShowDialog() -eq "OK") {
2792 | $path = $saveFileDialog.FileName
2793 | try {
2794 | # Create HTML content with header
2795 | $htmlContent = @"
2796 |
2797 |
2798 |
2799 | Network Scan Results
2800 |
2807 |
2808 |
2809 | Network Scan Results
2810 |
2811 |
External IP: $global:externalIP
2812 |
Domain: $global:domain
2813 |
Date/Time: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
2814 |
Total Devices: $global:totalCount
2815 |
2816 |
2817 |
2818 | MAC Address
2819 | Vendor
2820 | IP Address
2821 | Host Name
2822 |
2823 | "@
2824 | $listView.Items | ForEach-Object {
2825 | $htmlContent += @"
2826 |
2827 | $($_.MACaddress)
2828 | $($_.Vendor)
2829 | $($_.IPaddress)
2830 | $($_.HostName.Replace(' (This Device)',''))
2831 |
2832 | "@
2833 | }
2834 | $htmlContent += @"
2835 |
2836 |
2837 |
2838 | "@
2839 |
2840 | # Write HTML to file
2841 | [System.IO.File]::WriteAllText($path, $htmlContent)
2842 | Show-Popup2 -Message 'Export to HTML completed successfully!' -Title 'Export:'
2843 | }
2844 | catch {
2845 | Show-Popup2 -Message "Error during export: $_" -Title 'ERROR:'
2846 | }
2847 | }
2848 | })
2849 |
2850 | # Export List in CSV format
2851 | $ExportToCSV.Add_Click({
2852 | $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
2853 | $saveFileDialog.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*"
2854 | $saveFileDialog.FileName = "Network_Scan_Results"
2855 | if ($saveFileDialog.ShowDialog() -eq "OK") {
2856 | $path = $saveFileDialog.FileName
2857 | try {
2858 | # CSV header
2859 | $csvHeader = "MAC Address,Vendor,IP Address,Hostname"
2860 | $csvContent = $listView.Items | ForEach-Object {
2861 | "`r`n$($_.MACaddress.Replace(',','')),$($_.Vendor.Replace(',','')),$($_.IPaddress.Replace(',','')),$($_.HostName.Replace(' (This Device)','').Replace(',',''))"
2862 | }
2863 | [System.IO.File]::WriteAllLines($path, ($csvHeader + $csvContent))
2864 | Show-Popup2 -Message 'Export to CSV completed successfully!' -Title 'Export:'
2865 | }
2866 | catch {
2867 | Show-Popup2 -Message "Error during export: $_" -Title 'ERROR:'
2868 | }
2869 | }
2870 | })
2871 |
2872 | # Export List in TXT format
2873 | $ExportToText.Add_Click({
2874 | $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
2875 | $saveFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
2876 | $saveFileDialog.FileName = "Network_Scan_Results"
2877 | if ($saveFileDialog.ShowDialog() -eq "OK") {
2878 | $path = $saveFileDialog.FileName
2879 | try {
2880 | # TXT header
2881 | $textContent = @"
2882 | NETWORK SCAN RESULTS
2883 |
2884 | EXTERNAL IP : $global:externalIP
2885 | DOMAIN : $global:domain
2886 | DATE/TIME : $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
2887 | TOTAL DEVICES : $global:totalCount
2888 |
2889 | --------------------------------------
2890 | "@
2891 | $textContent += $listView.Items | ForEach-Object {
2892 | @"
2893 |
2894 | MAC : $($_.MACaddress)
2895 | Vendor : $($_.Vendor)
2896 | IP : $($_.IPaddress)
2897 | Hostname : $($_.HostName.Replace(' (This Device)',''))
2898 | --------------------------------------
2899 | "@
2900 | }
2901 | [System.IO.File]::WriteAllText($path, $textContent)
2902 | Show-Popup2 -Message 'Export to Text completed successfully!' -Title 'Export:'
2903 | }
2904 | catch {
2905 | Show-Popup2 -Message "Error during export: $_" -Title 'ERROR:'
2906 | }
2907 | }
2908 | })
2909 |
2910 | # Add listView column header click capture
2911 | $ListView.AddHandler(
2912 | [System.Windows.Controls.GridViewColumnHeader]::ClickEvent,
2913 | [System.Windows.RoutedEventHandler]$listViewSortColumn
2914 | )
2915 |
2916 | # Find and assign Hostname column from listView to control width when scrollbar is present
2917 | $hostNameColumn = ($listView.View.Columns | Where-Object {$_.Header -eq "Host Name"})
2918 |
2919 | $listView.Add_MouseDoubleClick({
2920 | if($listView.SelectedItems.Count -gt 0){
2921 | CheckConnectivity -selectedhost $listView.SelectedItems.IPaddress
2922 | $selectedItem = $listView.SelectedItems[0]
2923 | $pMAC.Text = "MAC: " + $selectedItem.MACaddress
2924 | $pVendor.Text = "Vendor: " + $selectedItem.Vendor
2925 | $pIP.Text = "IP: " + $selectedItem.IPaddress
2926 | $pHost.Text = "Host: " + $selectedItem.HostName.Replace(' (This Device)','')
2927 | $PopupCanvas.SetValue([System.Windows.Controls.Canvas]::LeftProperty, [System.Windows.Controls.Canvas]::GetLeft($listView) + 10)
2928 | $PopupCanvas.SetValue([System.Windows.Controls.Canvas]::TopProperty, [System.Windows.Controls.Canvas]::GetTop($listView) + 10)
2929 | $PopupCanvas.Visibility = 'Visible'
2930 | }
2931 | })
2932 |
2933 | $listView.Add_MouseLeftButtonDown({
2934 | $listView.SelectedItems.Clear()
2935 | })
2936 |
2937 | # Single item pop-up context menu, IP Address to clipboard
2938 | $PopupContextCopyIP_Click = {
2939 | if ($PopupCanvas.Visibility -eq 'Visible') {
2940 | $ipText = $pIP.Text -replace 'IP: '
2941 | Set-Clipboard -Value $ipText
2942 | Show-Popup2 -Message 'IP Address copied to clipboard!' -Title 'Info:'
2943 | } else {
2944 | Show-Popup2 -Message 'No item available to copy IP Address from!' -Title 'Warning:'
2945 | }
2946 | }
2947 | $PopupContextCopyIP.Add_Click($PopupContextCopyIP_Click)
2948 |
2949 | # Single item pop-up context menu, Hostname to clipboard
2950 | $PopupContextCopyHostname_Click = {
2951 | if ($PopupCanvas.Visibility -eq 'Visible') {
2952 | $hostText = $pHost.Text -replace 'Host: '
2953 | Set-Clipboard -Value $hostText
2954 | Show-Popup2 -Message 'Hostname copied to clipboard!' -Title 'Info:'
2955 | } else {
2956 | Show-Popup2 -Message 'No item available to copy Hostname from!' -Title 'Warning:'
2957 | }
2958 | }
2959 | $PopupContextCopyHostname.Add_Click($PopupContextCopyHostname_Click)
2960 |
2961 | # Single item pop-up context menu, MAC Address to clipboard
2962 | $PopupContextCopyMAC_Click = {
2963 | if ($PopupCanvas.Visibility -eq 'Visible') {
2964 | $macText = $pMAC.Text -replace 'MAC: '
2965 | Set-Clipboard -Value $macText
2966 | Show-Popup2 -Message 'MAC Address copied to clipboard!' -Title 'Info:'
2967 | } else {
2968 | Show-Popup2 -Message 'No item available to copy MAC Address from!' -Title 'Warning:'
2969 | }
2970 | }
2971 | $PopupContextCopyMAC.Add_Click($PopupContextCopyMAC_Click)
2972 |
2973 | # Single item pop-up context menu, Vendor to clipboard
2974 | $PopupContextCopyVendor_Click = {
2975 | if ($PopupCanvas.Visibility -eq 'Visible') {
2976 | $vendorText = $pVendor.Text -replace 'Vendor: '
2977 | Set-Clipboard -Value $vendorText
2978 | Show-Popup2 -Message 'Vendor copied to clipboard!' -Title 'Info:'
2979 | } else {
2980 | Show-Popup2 -Message 'No item available to copy Vendor from!' -Title 'Warning:'
2981 | }
2982 | }
2983 | $PopupContextCopyVendor.Add_Click($PopupContextCopyVendor_Click)
2984 |
2985 | # Single item pop-up context menu, All details to clipboard
2986 | $PopupContextCopyAll_Click = {
2987 | if ($PopupCanvas.Visibility -eq 'Visible') {
2988 | $hostText = $pHost.Text -replace 'Host: '
2989 | $ipText = $pIP.Text -replace 'IP: '
2990 | $macText = $pMAC.Text -replace 'MAC: '
2991 | $vendorText = $pVendor.Text -replace 'Vendor: '
2992 | $details = "Host: $hostText`nIP: $ipText`nMAC: $macText`nVendor: $vendorText"
2993 | Set-Clipboard -Value $details
2994 | Show-Popup2 -Message 'All details copied to clipboard!' -Title 'Info:'
2995 | } else {
2996 | Show-Popup2 -Message 'No item available to copy details from!' -Title 'Warning:'
2997 | }
2998 | }
2999 | $PopupContextCopyAll.Add_Click($PopupContextCopyAll_Click)
3000 |
3001 | # Clear CTRL key value
3002 | $global:CtrlIsDown = $false
3003 |
3004 | # KeyDown event handler
3005 | $Main.Add_KeyDown({
3006 | if ($_.Key -eq 'LeftCtrl' -or $_.Key -eq 'RightCtrl') {
3007 | $global:CtrlIsDown = $true
3008 | if($Scan.IsEnabled){
3009 | $scanButtonText.Text = 'Clear ARP cache'
3010 | $scanAdminIcon.Visibility = 'Visible'
3011 | }
3012 | }
3013 | })
3014 |
3015 | # KeyUp event handler
3016 | $Main.Add_KeyUp({
3017 | if ($_.Key -eq 'LeftCtrl' -or $_.Key -eq 'RightCtrl') {
3018 | $global:CtrlIsDown = $false
3019 | if($Scan.IsEnabled){
3020 | if ($global:gatewayPrefix -ne $originalGatewayPrefix) {
3021 | $scanButtonText.Text = 'Custom Scan'
3022 | } else {
3023 | $scanButtonText.Text = 'Scan'
3024 | }
3025 | $scanAdminIcon.Visibility = 'Collapsed'
3026 | }
3027 | }
3028 | })
3029 |
3030 | # Wait for background jobs to finish with progress tracking
3031 | function TrackProgress {
3032 | $totalItems = $listView.Items.Count
3033 | $completedItems = 0
3034 | # Initialize to a value that will always differ from $completedItems on first check
3035 | $previousCompletedItems = -1
3036 |
3037 | do {
3038 | # Count items with both HostName and Vendor resolved
3039 | $completedItems = ($listView.Items | Where-Object {
3040 | $_.HostName -ne "Resolving..." -and
3041 | $_.Vendor -ne "Identifying..."
3042 | }).Count
3043 |
3044 | # Check if the number of completed items has changed
3045 | if ($completedItems -ne $previousCompletedItems) {
3046 | # Update UI with the new progress
3047 | $completedPercentage = if ($totalItems -gt 0) {
3048 | ($completedItems / $totalItems) * 100
3049 | } else {
3050 | 0
3051 | }
3052 | Update-Progress ([math]::Min(100, $completedPercentage)) 'Identifying Devices'
3053 |
3054 | # Refresh ListView to show changes
3055 | $listView.Items.Refresh()
3056 | Update-uiMain
3057 |
3058 | # Update the previous count for the next iteration
3059 | $previousCompletedItems = $completedItems
3060 | } else {
3061 | # If no change, update the UI occasionally to show that the process is ongoing
3062 | if ($completedItems -lt $totalItems) {
3063 | Update-Progress ([math]::Min(100, $completedPercentage)) 'Identifying Devices'
3064 | }
3065 | }
3066 |
3067 | # Short sleep to not overload the system
3068 | Start-Sleep -Milliseconds 5
3069 | } while ($completedItems -lt $totalItems)
3070 | }
3071 |
3072 | # Ensure clean ListView
3073 | if($listview.Items){
3074 | $listview.Items.Clear()
3075 | }
3076 |
3077 | $ExportContext.IsEnabled = $false
3078 |
3079 | # Define Scan Button Actions
3080 | $btnScan.Add_MouseEnter({
3081 | $btnScan.Background = '#EEEEEE'
3082 | })
3083 |
3084 | $btnScan.Add_MouseLeave({
3085 | $btnScan.Background = '#777777'
3086 | })
3087 |
3088 | $Scan.Add_MouseEnter({
3089 | $Scan.Background = '#EEEEEE'
3090 | })
3091 |
3092 | $Scan.Add_MouseLeave({
3093 | $Scan.Background = '#777777'
3094 | })
3095 |
3096 | $Scan.Add_Click({
3097 | if($PopupCanvas.Visibility -eq 'Visible') {
3098 | $PopupCanvas.Visibility = 'Hidden'
3099 | }
3100 | if($PopupCanvas2.Visibility -eq 'Visible') {
3101 | $PopupCanvas2.Visibility = 'Hidden'
3102 | }
3103 | # If CTRL key is held while clicking the Scan button, offer to clear ARP cache as Admin prior to Scan process
3104 | if ($global:CtrlIsDown) {
3105 | $Scan.IsEnabled = $false
3106 | $osInfo = Get-CimInstance Win32_OperatingSystem
3107 | if ($osInfo.Caption -match "Server") {
3108 | Show-Popup2 -Message 'This option is not available for Windows Servers. Please clear your ARP Cache manually.' -Title 'Restricted Feature:'
3109 | } else {
3110 | try{
3111 | Start-Process -Verb RunAs powershell -WindowStyle Minimized -ArgumentList '-Command "& {Remove-NetNeighbor -InterfaceAlias * -Confirm:$false}"'
3112 | $listView.Items.Clear()
3113 | $TotalListed.Text = ''
3114 | Show-Popup2 -Message 'Cached peer list cleared...' -Title 'List Cleared:'
3115 | }catch{
3116 | Show-Popup2 -Message 'No action was taken...' -Title 'Process Aborted:'
3117 | }
3118 | }
3119 | if ($global:gatewayPrefix -ne $originalGatewayPrefix) {
3120 | $scanButtonText.Text = 'Custom Scan'
3121 | } else {
3122 | $scanButtonText.Text = 'Scan'
3123 | }
3124 | $scanAdminIcon.Visibility = 'Collapsed'
3125 | $Scan.IsEnabled = $true
3126 | $global:CtrlIsDown = $false
3127 | } else {
3128 | $Scan.IsEnabled = $false
3129 | # Make ProgressBar visible, hide Button
3130 | $Scan.Visibility = 'Collapsed'
3131 | $Progress.Visibility = 'Visible'
3132 | $Progress.Value = 0
3133 | $BarText.Text = 'Initializing'
3134 | $listView.Items.Clear()
3135 | $TotalListed.Text = ''
3136 | $global:totalCount = 0
3137 | $ExportContext.IsEnabled = $false
3138 | $hostNameColumn.Width = 284
3139 | Update-uiMain
3140 | Get-HostInfo -gateway $global:gateway -gatewayPrefix $global:gatewayPrefix -originalGatewayPrefix $originalGatewayPrefix
3141 | $externalIPt.Text = "`- `[ External IP: $externalIP `]"
3142 | $domainName.Text = "`- `[ Domain: $domain `]"
3143 | Update-uiMain
3144 | Scan-Subnet
3145 | List-Machines
3146 | processVendors
3147 | processHostnames
3148 | TrackProgress
3149 | # Hide ProgressBar, show button
3150 | $Progress.Visibility = 'Collapsed'
3151 | $Scan.Visibility = 'Visible'
3152 | $BarText.Text = ''
3153 | $Scan.IsEnabled = $true
3154 | $Progress.Value = 0
3155 | if ($listView.Items.Count -eq 0) {
3156 | $ExportContext.IsEnabled = $false
3157 | } else {
3158 | $ExportContext.IsEnabled = $true
3159 | }
3160 | Update-uiMain
3161 | $global:CtrlIsDown = $false
3162 | }
3163 | })
3164 |
3165 | # Show Window
3166 | $Main.ShowDialog() | out-null
3167 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 illsk1lls
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Simple IP Scanner - A Powershell Network Scanner
2 |
3 |
4 |
5 |
6 | [](https://www.powershellgallery.com/packages/IPScanner)
7 |
8 |
9 | This script will work as either a .CMD or .PS1 file.
10 | (For ease of use it will be posted as a CMD file, as you can simply Double-Click it to launch)
11 |
12 | A lightweight, fast, network scanner, for use with basic networks. Get an up to date list of peer: MAC Address, Vendor, IP Address, and Hostnames -
13 | External IP Address and Domain are also displayed in the titlebar after initiating a scan, and a
14 | simple connection monitor can show you current bandwidth usage and active port connections
15 |
16 | Right click the scan button to change subnets
17 |
18 |
19 | Click the Monitor Mode / Scanner Mode toggle in the titlebar to switch modes
20 |
21 |
22 |
23 | Double-clicking a listed item will give you a pop-up showing available connection options to that device. Clickable buttons will appear for available options.
24 |
25 |
26 | Right clicking the pop-up will produce CopyToClipboard options.
27 |
28 |
29 | Click the magnifying glass to the right of the IP Address on the popup to use the port scanner.
30 |
31 |
32 | Right-Click anywhere in the main ListView window for Export options for the current list. (HTML example export shown)
33 |
34 |
35 |
36 | To clear the cached network peer list (ARP Cache), **hold the \[CTRL\] key**. The Scan button will change.
37 | **(Note: Clearing network peer cache requires Admin rights, while normal scanning/usage does not.)**
38 |
39 |
--------------------------------------------------------------------------------
/cmd-wmic-version/IPScanner.cmd:
--------------------------------------------------------------------------------
1 | @ECHO OFF&SET "TitleName=IP Scanner"
2 | TASKLIST /V /NH /FI "imagename eq cmd.exe"|FIND /I /C "%TitleName%">nul
3 | IF NOT %errorlevel%==1 POWERSHELL -nop -c "$^={$Notify=[PowerShell]::Create().AddScript({$Audio=New-Object System.Media.SoundPlayer;$Audio.SoundLocation=$env:WinDir + '\Media\Windows Notify System Generic.wav';$Audio.playsync()});$rs=[RunspaceFactory]::CreateRunspace();$rs.ApartmentState="^""STA"^"";$rs.ThreadOptions="^""ReuseThread"^"";$rs.Open();$Notify.Runspace=$rs;$Notify.BeginInvoke()};&$^;$PopUp=New-Object -ComObject Wscript.Shell;$PopUp.Popup("^""IP Scanner is already open!"^"",0,'ERROR:',0x10)">nul&EXIT
4 | TITLE %TitleName%
5 | >nul 2>&1 reg add hkcu\software\classes\.IPscanner\shell\runas\command /f /ve /d "cmd /x /d /r set \"f0=%%2\"& call \"%%2\" %%3"& set _= %*
6 | >nul 2>&1 fltmc|| if "%f0%" neq "%~f0" (cd.>"%ProgramData%\runas.IPscanner" & start "%~n0" /high "%ProgramData%\runas.IPscanner" "%~f0" "%_:"=""%" & exit /b)
7 | >nul 2>&1 reg delete hkcu\software\classes\.IPscanner\ /f &>nul 2>&1 del %ProgramData%\runas.IPscanner /f /q
8 | >nul 2>&1 netsh advfirewall firewall set rule group="Network Discovery" new enable=Yes
9 | SETLOCAL ENABLEDELAYEDEXPANSION
10 | FOR /L %%# IN (1,1,20) DO (SET "EMPT=!EMPT! "&SET "FULL=!FULL!%%")
11 | FOR /F %%# IN ('COPY/Z "%~dpf0" nul')DO FOR /F "skip=4" %%$ IN ('ECHO;PROMPT;$H^|CMD')DO SET "BS=%%$"&SET "CR=%%#"
12 |
13 | :LOAD
14 | CALL :GETHOSTINFO
15 | CALL :SCANSUBNETS
16 | CALL :LISTMACHINES
17 | SET "KEY="&ECHO/&ECHO Press any key to refresh, (X) to Exit
18 | FOR /f "delims=" %%# IN ('2^> nul XCOPY /L /W /I "%~f0" "%~f0"') DO IF NOT DEFINED KEY SET "KEY=%%#"
19 | IF /I "!KEY:~-1!"=="X" ENDLOCAL&GOTO :EOF
20 | GOTO :LOAD
21 |
22 | :GETCOMPUTERNAME
23 | FOR /f "usebackq tokens=2" %%# IN (`PING -a %1 -n 1`) DO (SET %2=%%#)&EXIT /b
24 | EXIT /b
25 |
26 | :PROGRESS <% Per Cycle>
27 | SET/A P+=%2
28 | IF %P% GEQ 5 SET/A L=(%P%/5)+1&IF %P% GEQ 100 SET/A P=100
29 | SET/P "=!CR!!BS!!CR![!FULL:~0,%L%!!BS!!EMPT:~%L%!]%~1 [%%%P%] "nul
78 | IF NOT !errorlevel! == 0 (
79 | SET "EXT=No Internet Detected"
80 | ) ELSE (
81 | FOR /f "usebackq" %%# IN (`curl -s ifconfig.me`) DO (SET EXT=%%#)
82 | )
83 | CALL :GETIP ISHOST&IF NOT DEFINED ISHOST SET "ISHOST=Unknown"
84 | CALL :GETADAPTER ADAPTER&IF NOT DEFINED ADAPTER SET "ADAPTER=Unknown"
85 | CALL :GETSUBNET SUBNET&IF NOT DEFINED SUBNET SET "SUBNET=Unknown"
86 | CALL :GETGATEWAY GATEWAY&IF NOT DEFINED GATEWAY SET "GATEWAY=Unknown"
87 | CALL :GETDOMAIN DOMAIN&IF NOT DEFINED DOMAIN SET "DOMAIN=Unknown"
88 | EXIT/b
89 |
90 | :GETIP
91 | FOR /f "tokens=2 delims={,}" %%# IN ('"WMIC NICConfig where IPEnabled="True" get IPAddress /value"') DO SET "%1=%%~#"&EXIT /b
92 | EXIT /b
93 |
94 | :GETSUBNET
95 | FOR /f "tokens=2 delims={,}" %%# IN ('"WMIC NICConfig where IPEnabled="True" get IPSubnet /value"') DO SET "%1=%%~#"&EXIT /b
96 | EXIT /b
97 |
98 | :GETGATEWAY
99 | FOR /f "tokens=2 delims={,}" %%# IN ('"WMIC NICConfig where IPEnabled="True" get DefaultIPGateway /value"') DO SET "%1=%%~#"&EXIT /b
100 | EXIT /b
101 |
102 | :GETDOMAIN
103 | FOR /f "usebackq skip=1 tokens=*" %%# in (`WMIC ComputerSystem Get Workgroup ^| Findstr /r /v "^$"`) DO SET "%1=%%~#"&EXIT /b
104 | EXIT /b
105 |
106 | :GETADAPTER
107 | FOR /f "usebackq skip=1 tokens=1*" %%# in (`"WMIC NICConfig Where IPEnabled="True" Get Caption"`) DO SET "%1=%%$"&EXIT /b
108 | EXIT /b
109 |
110 | :SCANSUBNETS
111 | CLS
112 | NETSH Interface IPV4 DELETE Neighbors>nul
113 | FOR /f "usebackq tokens=1,2" %%# IN (`ARP -a`) DO (
114 | IF "%%#"=="Interface:" (
115 | SET "INTF=%%$"
116 | FOR /f "delims=. tokens=1-3" %%a IN ("!INTF!") DO (
117 | SET "SCAN=%%a.%%b.%%c."
118 | SET/A L=1&SET/A P=0&SET/A X=1&SET "MSSG=Sending Packets, Please Wait...."
119 | FOR /L %%# IN (1,1,254) DO (
120 | START /min "" ""CMD.exe /c PING -n 1 -w 200 !SCAN!%%#"">nul
121 | :: No floating point in CMD ;(
122 | SET/A X+=1
123 | IF !X! EQU 2 (CALL :PROGRESS "!MSSG!" 1)
124 | IF !X! EQU 5 (SET/A X=0&CALL :PROGRESS "!MSSG!" 1)
125 | )
126 | )
127 | )
128 | )
129 | SET/A L=1&SET/A P=0&SET "MSSG=Listening, Please Wait.........."
130 | FOR /L %%# IN (1,1,8) DO (
131 | CALL :PROGRESS "!MSSG!" 13
132 | >nul 2>&1 PING 127.0.0.1 -n 2
133 | )
134 | EXIT /b
135 |
--------------------------------------------------------------------------------
/console-crossplatform/IPScanner.ps1:
--------------------------------------------------------------------------------
1 | function Get-HostInfo {
2 | # Hostname
3 | $global:hostName = [System.Net.Dns]::GetHostName()
4 |
5 | # Check Internet Connection and Get External IP
6 | $ProgressPreference = 'SilentlyContinue'
7 | $hotspotRedirectionTest = irm "http://www.msftncsi.com/ncsi.txt"
8 | $global:externalIP = if ($hotspotRedirectionTest -eq "Microsoft NCSI") {
9 | irm "http://ifconfig.me/ip"
10 | } else {
11 | "No Internet or Redirection"
12 | }
13 | $ProgressPreference = 'Continue'
14 |
15 | # Find Gateway
16 | $global:gateway = (Get-NetRoute -DestinationPrefix 0.0.0.0/0 | Select-Object -First 1).NextHop
17 | $gatewayParts = $gateway -split '\.'
18 | $global:gatewayPrefix = "$($gatewayParts[0]).$($gatewayParts[1]).$($gatewayParts[2])."
19 |
20 | # Internal IP
21 | $global:internalIP = (Get-NetIPAddress | Where-Object {$_.AddressFamily -eq 'IPv4' -and $_.InterfaceAlias -ne 'Loopback Pseudo-Interface 1' -and ($_.IPAddress -like "$gatewayPrefix*")}).IPAddress
22 |
23 | # Host adapter type
24 | $global:adapter = (Get-NetIPAddress -InterfaceAlias "*Ethernet*","*Wi-Fi*" -AddressFamily IPv4 | Where-Object { $_.IPAddress -like "$gatewayPrefix*" }).InterfaceAlias
25 |
26 | # My Mac
27 | $global:myMac = (Get-NetAdapter -Name $adapter).MacAddress.Replace('-',':')
28 |
29 | # Convert subnet prefix to readable number
30 | $prefixLength = (Get-NetIPAddress | Where-Object {$_.AddressFamily -eq 'IPv4' -and $_.InterfaceAlias -ne 'Loopback Pseudo-Interface 1'} | Select-Object -First 1).PrefixLength
31 | $subnetMask = ([System.Net.IPAddress]::Parse(($([Math]::Pow(2, $prefixLength)) - 1) * [Math]::Pow(2, 32 - $prefixLength))).GetAddressBytes() -join "."
32 |
33 | # Domain
34 | $domain = (Get-WmiObject -Class Win32_ComputerSystem).Domain
35 |
36 | # Output results
37 | $global:hostOutput = [PSCustomObject]@{
38 | Host = if($hostName){$hostName} else {'Unknown'}
39 | ExternalIP = if($externalIP){$externalIP} else {'Unknown'}
40 | InternalIP = if($internalIP){$internalIP} else {'Unknown'}
41 | Adapter = if($adapter){$adapter} else {'Unknown'}
42 | Subnet = if($subnetMask){$subnetMask} else {'Unknown'}
43 | Gateway = if($gateway){$gateway} else {'Unknown'}
44 | Domain = if($domain){$domain} else {'Unknown'}
45 | }
46 | }
47 |
48 | function Get-MacVendor($mac) {
49 | # Get Vendor via Mac (thanks to u/mprz)
50 | return (irm "https://www.macvendorlookup.com/api/v2/$($mac.Replace(':','').Substring(0,6))" -Method Get)
51 | }
52 |
53 | function Scan-Subnet {
54 | # Ping Entire Subnet
55 | for ($i = 1; $i -le 254; $i++) {
56 | Test-Connection $gatewayPrefix$i -Count 1 -AsJob | Out-Null
57 | Write-Progress -Activity "Sending Packets" -Status "Progress..." -PercentComplete ($i * (100 / 254))
58 | }
59 | Write-Progress -Activity "Sending Packets" -Status "Done" -PercentComplete 100
60 | Start-Sleep -Seconds 1
61 | Write-Progress -Activity "Sending Packets" -Completed
62 | }
63 |
64 | function waitForResponses {
65 | # Wait with progress
66 | for ($i = 1; $i -le 100; $i++) {
67 | Write-Progress -Activity "Listening" -Status "Waiting for responses..." -PercentComplete ($i)
68 | Start-Sleep -Milliseconds 50
69 | }
70 | Write-Progress -Activity "Listening" -Status "Done" -PercentComplete 100
71 | Start-Sleep -Seconds 1
72 | Write-Progress -Activity "Listening" -Completed
73 | }
74 |
75 | function List-Machines {
76 | # Header
77 | $DisplayA = ("{0,-18} {1,-26} {2, -14} {3}" -f 'MAC ADDRESS', 'VENDOR', 'IP ADDRESS', 'REMOTE HOSTNAME')
78 | Write-Host; Write-Host $DisplayA
79 | Write-Host "================================================================================================="
80 |
81 | # Filter for Reachable or Stale states and select only IP and MAC address
82 | $arpInit = Get-NetNeighbor | Where-Object { $_.State -eq "Reachable" -or $_.State -eq "Stale" } | Select-Object -Property IPAddress, LinkLayerAddress
83 |
84 | # Convert IP Addresses from string to int by each section
85 | $arpConverted = $arpInit | Where-Object { $_.IPAddress -match "^\d+\.\d+\.\d+\.\d+$" } | Sort-Object -Property { $ip = $_.IPAddress; [version]($ip) }
86 |
87 | # Sort by IP using [version] sorting
88 | $arpOutput = $arpConverted | Sort-Object {[version]$_.IPaddress}
89 | $self = 0
90 | $myLastOctet = [int]($internalIP -split '\.')[-1]
91 |
92 | # Get My Vendor via Mac lookup
93 | $tryMyVendor = (Get-MacVendor "$myMac").Company
94 | $myVendor = if($tryMyVendor){$tryMyVendor.substring(0, [System.Math]::Min(25, $tryMyVendor.Length))} else {'Unknown'}
95 |
96 | # Cycle through ARP table
97 | foreach ($line in $arpOutput) {
98 | $ip = $line.IPAddress
99 | $mac = $line.LinkLayerAddress.Replace('-',':')
100 | # Get Hostname
101 | try{
102 | $name = [System.Net.Dns]::GetHostEntry($ip).HostName
103 | } catch {
104 | $name = "Unable to Resolve"
105 | } finally {
106 | if ([string]::IsNullOrEmpty($name)) {
107 | $name = "Unable to Resolve"
108 | }
109 | }
110 |
111 | # Get Remote Device Vendor via Mac lookup
112 | $tryVendor=(Get-MacVendor "$mac").Company
113 | $vendor = if($tryVendor){$tryVendor.substring(0, [System.Math]::Min(25, $tryVendor.Length))} else {'Unknown'}
114 |
115 | # Format and display
116 | $DisplayX = ("{0,-18} {1,-26} {2, -14} {3}" -f $mac, $vendor, $ip, $name)
117 | $DisplayZ = ("{0,-18} {1,-26} {2, -14} {3}" -f $myMac, $myVendor, $internalIP, "$hostName (This Device)")
118 | $lastOctet = [int]($ip -split '\.')[-1]
119 | if ($myLastOctet -gt $lastOctet) {
120 | Write-Host $DisplayX
121 | } else {
122 | if ($self -ge 1) {
123 | Write-Host $DisplayX
124 | } else {
125 | Write-Host $DisplayZ
126 | Write-Host $DisplayX
127 | $self++
128 | }
129 | }
130 | }
131 | }
132 |
133 | do {
134 | # Main
135 | Clear-Host
136 | Write-Host -NoNewLine 'Getting Ready...'
137 | Get-HostInfo
138 | Scan-Subnet
139 | waitForResponses
140 | Write-Host 'Done';Write-Host
141 | $hostOutput | Out-String -Stream | Where-Object { $_.Trim().Length -gt 0 } | Write-Host
142 | $ProgressPreference = 'SilentlyContinue'
143 | List-Machines
144 | $ProgressPreference = 'Continue'
145 | Write-Host;Write-Host -NoNewLine 'Press any key to refresh, (X) to Exit'
146 | $Host.UI.RawUI.Flushinputbuffer()
147 | } until ($Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown').VirtualKeyCode -eq 88)
148 |
--------------------------------------------------------------------------------