找回密码
 立即注册
首页 业界区 业界 硬核实用!Windows下使用 PowerShell 编写局域网设备扫 ...

硬核实用!Windows下使用 PowerShell 编写局域网设备扫描器

公西颖初 2026-1-17 04:15:00
前言

在进行网络调试或设备管理时,我们经常需要知道局域网内有哪些设备在线。传统的 ping 命令逐个扫描太慢,而专业的扫描工具又显得大材小用。今天,我们通过一段 PowerShell 脚本,利用网络协议的底层特性,打造一个局域网扫描工具。
原理剖析:为什么这个脚本比普通 Ping 更快、更准?

大多数人认为扫描局域网就是简单地给每个 IP 发送 Ping 包。但这种方式有两大弊端:

  • 效率低:等待不在线的设备超时会耗费大量时间。
  • 不准确:许多设备(如开启防火墙的 Windows 电脑)会禁掉 ICMP 回显,导致明明在线却显示“离线”。
本脚本采用了 “异步探测 + ARP 表解析” 的双重机制,其工作原理流程图如下:
1. 异步探测(Asynchronous Probing)

脚本并没有等待每一个 Ping 的结果。它使用 SendPingAsync 向网段内的所有 254 个 IP 地址快速抛出探测包。

  • 目的:我们并不在乎这些包是否真的收到了回复,其真正的目的是触发操作系统的 ARP 广播
2. ARP 协议的妙用

当你的电脑尝试向局域网内某个 IP 发送数据时,它首先需要知道对方的 MAC 地址(物理地址)。即便对方防火墙拦截了 Ping 包,只要该设备存在,它通常也会对 ARP 请求做出响应。

  • ARP 表:一旦设备响应,它的 IP 和 MAC 地址就会被记录在操作系统的 ARP 缓存表中。
3. 状态判定算法

脚本通过 Get-NetNeighbor 检查系统的 ARP 记录,将状态分为三类判定:

  • Ping 在线:设备响应了 ICMP。
  • ARP 在线:设备虽不响应 Ping,但在 ARP 表中状态为 Reachable 或 Stale。
  • 离线:ARP 表无记录或显示缓存失效。
完整代码

点击查看 PowerShell 脚本代码
  1. Write-Host "==========================================" -ForegroundColor Green
  2. Write-Host "         局域网设备扫描器         " -ForegroundColor Green
  3. Write-Host "==========================================" -ForegroundColor Green
  4. # -----------------------------
  5. # 在线检测函数(核心)
  6. # -----------------------------
  7. function Test-DeviceOnline {
  8.     param([string]$IP)
  9.     $arp = Get-NetNeighbor -IPAddress $IP -ErrorAction SilentlyContinue
  10.     if (-not $arp) {
  11.         return @{ Online = $false; Reason = "无ARP" }
  12.     }
  13.     if ($arp.State -in 'Reachable','Stale','Delay','Probe') {
  14.         if (Test-Connection $IP -Count 1 -Quiet -ErrorAction SilentlyContinue) {
  15.             return @{ Online = $true; Reason = "Ping" }
  16.         }
  17.         return @{ Online = $true; Reason = "ARP在线" }
  18.     }
  19.     return @{ Online = $false; Reason = "缓存失效" }
  20. }
  21. # -----------------------------
  22. # 1. 获取 IPv4 网卡
  23. # -----------------------------
  24. $adapters = Get-NetIPAddress -AddressFamily IPv4 |
  25. Where-Object {
  26.     $_.IPAddress -notmatch '^127\.|^169\.254\.' -and
  27.     $_.PrefixLength -eq 24
  28. }
  29. if (-not $adapters) {
  30.     Write-Host "未发现可用 IPv4 网卡" -ForegroundColor Red
  31.     Read-Host
  32.     exit
  33. }
  34. # -----------------------------
  35. # 2. 生成网段
  36. # -----------------------------
  37. $networks = $adapters.IPAddress |
  38. ForEach-Object {
  39.     if ($_ -match '^(\d+\.\d+\.\d+)\.\d+$') {
  40.         "$($Matches[1]).0/24"
  41.     }
  42. } | Select-Object -Unique
  43. # -----------------------------
  44. # 3. 选择网段
  45. # -----------------------------
  46. if ($networks.Count -eq 1) {
  47.     $network = $networks[0]
  48.     Write-Host "`n仅检测到一个网段,自动使用:$network" -ForegroundColor Yellow
  49. }
  50. else {
  51.     do {
  52.         Write-Host "`n检测到多个网段,请选择要扫描的网段:" -ForegroundColor Cyan
  53.         for ($i = 0; $i -lt $networks.Count; $i++) {
  54.             Write-Host "[$($i + 1)] $($networks[$i])"
  55.         }
  56.         $choice = Read-Host "请输入编号 (1-$($networks.Count))"
  57.         $valid  = [int]::TryParse($choice, [ref]$null) -and
  58.                   $choice -ge 1 -and
  59.                   $choice -le $networks.Count
  60.         if (-not $valid) {
  61.             Write-Host "输入无效,请重新选择。" -ForegroundColor Red
  62.         }
  63.     } until ($valid)
  64.     $network = $networks[$choice - 1]
  65. }
  66. $prefix = $network -replace '\.0/24',''
  67. # -----------------------------
  68. # 4. 快速建立 ARP 表
  69. # -----------------------------
  70. Write-Host "`n[1/2] 正在向网段 $network 发送探测包 (Ping)..." -ForegroundColor Cyan
  71. foreach ($i in 1..254) {
  72.     $p = New-Object System.Net.NetworkInformation.Ping
  73.     [void]$p.SendPingAsync("$prefix.$i", 800)
  74. }
  75. Start-Sleep -Seconds 1
  76. # -----------------------------
  77. # 5. 获取本机 IP → MAC
  78. # -----------------------------
  79. $localMapping = @{}
  80. Get-NetIPAddress -AddressFamily IPv4 |
  81. Where-Object { $_.IPAddress -notmatch '^127\.|^169\.254\.' } |
  82. ForEach-Object {
  83.     $adapter = Get-NetAdapter -InterfaceIndex $_.InterfaceIndex -ErrorAction SilentlyContinue
  84.     if ($adapter) {
  85.         $localMapping[$_.IPAddress] = $adapter.MacAddress.Replace("-", ":").ToUpper()
  86.     }
  87. }
  88. # -----------------------------
  89. # 6. 解析 ARP 表
  90. # -----------------------------
  91. $arpRegex = '(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<MAC>([0-9a-f]{2}[:-]){5}[0-9a-f]{2})'
  92. $globalArp = arp -a
  93. $potentialIPs = @()
  94. foreach ($line in $globalArp) {
  95.     if ($line -match $arpRegex) {
  96.         $ip  = $Matches.IP
  97.         $mac = $Matches.MAC.ToUpper().Replace("-", ":")
  98.         if ($ip.StartsWith("$prefix.") -and
  99.             -not $localMapping.ContainsKey($ip) -and
  100.             $ip -notmatch '\.255$') {
  101.             $potentialIPs += [PSCustomObject]@{
  102.                 IP  = $ip
  103.                 MAC = $mac
  104.             }
  105.         }
  106.     }
  107. }
  108. # -----------------------------
  109. # 7. 构建结果
  110. # -----------------------------
  111. $finalList = @()
  112. # 本机
  113. foreach ($ip in $localMapping.Keys) {
  114.     if ($ip.StartsWith("$prefix.")) {
  115.         $finalList += [PSCustomObject]@{
  116.             IP       = "$ip*"
  117.             MAC      = $localMapping[$ip]
  118.             IsOnline = $true
  119.             Reason   = "本机"
  120.             RawIP    = $ip
  121.         }
  122.     }
  123. }
  124. Write-Host "[2/2] 正在判断设备在线状态..." -ForegroundColor Cyan
  125. foreach ($dev in $potentialIPs | Select-Object -Unique IP, MAC) {
  126.     $result = Test-DeviceOnline $dev.IP
  127.     $finalList += [PSCustomObject]@{
  128.         IP       = $dev.IP
  129.         MAC      = $dev.MAC
  130.         IsOnline = $result.Online
  131.         Reason   = $result.Reason
  132.         RawIP    = $dev.IP
  133.     }
  134. }
  135. # -----------------------------
  136. # 8. 输出
  137. # -----------------------------
  138. Write-Host "`n============== 扫描结果 ==============" -ForegroundColor Cyan
  139. $sorted = $finalList | Sort-Object {[version]$_.RawIP}
  140. foreach ($item in $sorted) {
  141.     $status = if ($item.IsOnline) {
  142.                 switch ($item.Reason) {
  143.                         "Ping"     { "在线" }
  144.                         "ARP在线"  { "在线(未响应 Ping)" }
  145.                         "本机"     { "本机" }
  146.                         default    { "在线" }
  147.                 }
  148.         } else {
  149.                 switch ($item.Reason) {
  150.                         "缓存失效" { "离线(缓存失效)" }
  151.                         default    { "离线" }
  152.                 }
  153.         }
  154.     $line = "{0,-20} {1,-22} {2}" -f $item.IP, $item.MAC, $status
  155.     if ($item.IsOnline) {
  156.         Write-Host $line -ForegroundColor Green
  157.     } else {
  158.         Write-Host $line -ForegroundColor DarkGray
  159.     }
  160. }
  161. $online = ($finalList | Where-Object { $_.IsOnline }).Count
  162. $total  = $finalList.Count
  163. Write-Host "`n扫描完成:$online / $total 台设备在线" -ForegroundColor Green
  164. Write-Host "`n按任意键退出..."
  165. $null = [System.Console]::ReadKey()
复制代码
核心代码亮点说明

网段自动识别

脚本会自动排除 127.0.0.1(回环地址)和 169.254.x.x(自动私有 IP),并智能识别当前活跃的网段,无需用户手动输入 IP 范围。
结果美化输出

为了提升使用体验,脚本使用了格式化操作符 -f:
  1. $line = "{0,-20} {1,-22} {2}" -f $item.IP, $item.MAC, $status
复制代码
它可以确保输出的 IP、MAC 地址和状态在控制台中完美对齐,并使用绿色(在线)和灰色(离线)进行视觉区分。
如何使用?


  • 将脚本代码保存为 .ps1 文件,编码UTF8 BOM。也可通过LANScanner局域网扫描器.zip链接进行下载
  • 使用powershell.exe运行,建议使用powershell文件关联后直接运行。
运行截图

1.png

总结

这个脚本不仅是一个工具,更是对 Layer 2(数据链路层)Layer 3(网络层) 协同工作的一次实践。通过利用系统底层的 ARP 缓存,我们实现了比传统扫描器更高效、更具穿透力的探测效果。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

2026-1-20 15:24:27

举报

喜欢鼓捣这些软件,现在用得少,谢谢分享!
6 天前

举报

您需要登录后才可以回帖 登录 | 立即注册