Find-LANHosts.psm1
<# .SYNOPSIS Quickly finds host on a local network using ARP for discovery .DESCRIPTION Uses ARP requests to determine whether a host is present on a network segment. As APR is a Layer 2 mechanism, the list of IP addesses need to be on the same network as the device running the script. .PARAMETER IP Optional. Specifies one or more IP addresses to scan for. Typically this will be a list of all usable hosts on a network. .PARAMETER NetAdapter Optional. Specifies one or more NetAdaper (CimInstance) objects from Get-NetAdapter. These interfaces will have attached subnets detected and used for the scan. If both the IP and NetAdapter parameters are omitted, all network adapters will be enumerated and local subnets automatically determined. This may require elevated priviliges. Please note that this can include adapters with very high host counts (/16, etc) which will take considerable time to enumerate. .PARAMETER DelayMS Optional. Specifies the interpacket delay, default is 2ms. Can be increased if scanning unreliable or high latency networks. .PARAMETER ClearARPCache Optional. Clears the ARP cache before starting a scan. This is recommended, but may require elevated priviliges. .EXAMPLE Find-LANHosts .EXAMPLE Find-LANHosts -ClearARPCache -DelayMS 5 .EXAMPLE Get-NetAdapter -Name Ethernet | Find-LANHosts .EXAMPLE Get-NetAdapter | ? {($_ | Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue) -ne $null} | Find-LANHosts .EXAMPLE Get-NetRoute -DestinationPrefix 0.0.0.0/0 | Get-NetAdapter | Find-LANHosts .EXAMPLE $IPs = 1..254 | % {"10.250.1.$_"} Find-LANHosts -IP $IPs .EXAMPLE 1..254 | % {"192.168.1.$_"} | Find-LANHosts -ClearARPCache .EXAMPLE 1..254 | % {"10.1.1.$_"} | Find-LANHosts -DelayMS 5 .LINK https://github.com/mdjx/PSLANScan #> function Find-LANHosts { [Cmdletbinding(DefaultParameterSetName = "IPBlock")] Param ( [Parameter(Mandatory = $false, ValueFromPipeline, ParameterSetName = "IPBlock")] [string[]]$IP = $null, [Parameter(Mandatory = $false, ValueFromPipeline, ParameterSetName = "Interface")] [CimInstance[]]$NetAdapter = $null, [Parameter(Mandatory = $false, Position = 2)] [ValidateRange(0, 15000)] [int]$DelayMS = 2, [switch]$ClearARPCache ) Begin { $ASCIIEncoding = New-Object System.Text.ASCIIEncoding $Bytes = $ASCIIEncoding.GetBytes("!") $UDP = New-Object System.Net.Sockets.Udpclient if ($ClearARPCache) { $ARPClear = arp -d 2>&1 if (($ARPClear.count -gt 0) -and ($ARPClear[0] -is [System.Management.Automation.ErrorRecord]) -and ($ARPClear[0].Exception -notmatch "The parameter is incorrect")) { Throw $ARPClear[0].Exception } } $IPList = [System.Collections.ArrayList]@() $Timer = [System.Diagnostics.Stopwatch]::StartNew() Write-Verbose "Beginning scan" } Process { if (($null -eq $IP) -and ($null -eq $NetAdapter)) { if ($VerbosePreference -eq "SilentlyContinue") { [array]$IP = Get-IPs -ReturnIntRange } else {[array]$IP = Get-IPs -ReturnIntRange -Verbose } } if ($PsCmdlet.ParameterSetName -eq "Interface") { if ($VerbosePreference -eq "SilentlyContinue") {[array]$IP = Get-IPs -NetAdapter $NetAdapter -ReturnIntRange } else { [array]$IP = Get-IPs -NetAdapter $NetAdapter -ReturnIntRange -Verbose } } if ($IP.Count -lt 1) { Write-Error "IP Count is less than 1, please check provided IPs or Adapter for valid address space" } if ($null -ne $IP.FirstIPInt) { $IP | ForEach-Object { $CurrentIPInt = $_.FirstIPInt Do { $CurrIP = [IPAddress]$CurrentIPInt $CurrIP = ($CurrIP).GetAddressBytes() [Array]::Reverse($CurrIP) $CurrIP = ([IPAddress]$CurrIP).IPAddressToString $UDP.Connect($CurrIP, 1) [void]$UDP.Send($Bytes, $Bytes.length) [void]$IPList.Add($CurrIP) if ($DelayMS) { [System.Threading.Thread]::Sleep($DelayMS) } $CurrentIPInt++ } While ($CurrentIPInt -le $_.LastIPInt) } } else { $IP | ForEach-Object { $UDP.Connect($_, 1) [void]$UDP.Send($Bytes, $Bytes.length) [void]$IPList.Add($_) if ($DelayMS) { [System.Threading.Thread]::Sleep($DelayMS) } } } } End { $Hosts = arp -a $Timer.Stop() if ($Timer.Elapsed.TotalSeconds -gt 15) { Write-Warning "Scan took longer than 15 seconds, ARP entries may have been flushed. Recommend lowering DelayMS parameter" } $Hosts = $Hosts | Where-Object { $_ -match "dynamic" } | % { ($_.trim() -replace " {1,}", ",") | ConvertFrom-Csv -Header "IP", "MACAddress" } $Hosts = $Hosts | Where-Object { $_.IP -in $IPList } Write-Output $Hosts } } |