资讯动态

ScanLan 扫局域网:为什么 ARP 表比 ping 扫段更靠谱

发布时间:2026/10/8 11:14:20 来源:尧图企业网站定制
简介这是一份面向网络管理初学者与运维人员的VC局域网扫描工具源码核心功能是快速发现同一网段内所有主机的IP与MAC地址可用于网络设备盘点、IP冲突排查与未授权设备识别等场景。压缩包共15个文件约13KB以C源码为主体包含5个h头文件与4个cpp实现文件另有rc、rc2资源脚本、ico图标、dsw与dsp工程文件及clw类向导文件构成一套可直接用Visual C打开编译的完整工程。据描述该工具借助ARP或ICMP协议探测约30秒即可扫完一个网段的255台机器并可能通过进程间通信机制提升扫描效率。目前已有733人学习下载适合想理解局域网二层通信原理、动手实践网络扫描编程的读者参考源码结构与实现思路。1. ScanLan 扫局域网为什么 ARP 表比 ping 扫段更靠谱很多人第一次写局域网扫描思路都很直接拿到本机网段比如 192.168.1.0/24然后 for 循环 ping 一遍能通的就记下来。跑完发现只扫到七八台可路由器后台明明挂着二十多个设备。这不是代码写错了是方向选偏了——ping 依赖 ICMP 回显而大量设备手机、打印机、杂牌摄像头、部分 IoT 模块默认就把 ICMP 关了你 ping 它它装死。ScanLan 这类工具要解决的核心问题是在一个局域网里把「当前活着的设备」连同它们的 IP 和 MAC 地址一起捞出来。IP 告诉你它在网络层的身份MAC 告诉你它在链路层的物理身份两者配对之后你才能做后续的事ip冲突排查、找不到对方设备时定位、局域网 ip 查询、识别杂牌摄像头、给静态绑定做台账。适合谁运维、做局域网联机调试的开发者、家里设备多到记不清的人以及需要给设备做资产登记的安全从业者。关键认知只有一句同一广播域内想知道谁在线问 ARP 比问 ICMP 有效得多。因为只要设备接在同一个二层网络里、协议栈正常它就会响应 ARP 请求哪怕它屏蔽了 ping。这就是 ScanLan 这类扫描器能比 ping 扫段多扫出一倍设备的原因。2. ScanLan 的底层逻辑ARP、ICMP 与网段边界2.1 为什么 ARP 能扫到 ping 扫不到的机器ARP地址解析协议的工作是我知道一个 IP但不知道对应 MAC于是向广播域发一个 ARP Request「谁是这个 IP请把 MAC 告诉我」持有该 IP 的设备回一个 ARP Reply。这个过程发生在链路层绝大多数设备的协议栈都会无条件响应因为它不响应就没法正常通信。ping 走的是 ICMP属于网络层是否回应由操作系统策略决定。Windows 防火墙默认就拦入站 ICMP 回显很多 Linux 发行版也默认关闭。所以你会看到「能上网、能共享文件但 ping 不通」的设备这不是故障是策略。提示ARP 只能在同一广播域同一 VLAN、同一网段内工作。跨网段扫描必须靠路由或 SNMP这是 ScanLan 的天然边界别指望它扫穿三层。2.2 主动扫描和被动监听的区别主动扫描自己构造 ARP Request 发出去收 Reply速度快、可控ScanLan 默认走这条路。被动监听把网卡设为混杂模式只听不主动发靠抓别人通信时的 ARP 广播来建表。被动方式隐蔽、不打扰网络但慢且设备不说话就抓不到。实操里我一般用主动扫描做全量发现用被动监听做补充验证。两者结合漏报率能压到很低。2.3 网段边界怎么确定扫描前必须先算清楚自己所在的网段和掩码。常见误区是直接假设 /24但企业网里 /22、/23 很常见你按 /24 扫就漏掉一大半。用下面这段 Python 拿到本机网段import ipaddress import socket import fcntl import struct def get_ip_mask(ifnameeth0): 通过 ioctl 获取指定网卡的 IP 和掩码Linux 环境 s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 0x8915 SIOCGIFADDR取 IP ip socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, struct.pack(256s, ifname[:15].encode()))[20:24]) # 0x891b SIOCGIFNETMASK取掩码 mask socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x891b, struct.pack(256s, ifname[:15].encode()))[20:24]) return ip, mask ip, mask get_ip_mask(eth0) net ipaddress.IPv4Network(f{ip}/{mask}, strictFalse) print(f本机 {ip}网段 {net}可用主机数 {net.num_addresses - 2})逻辑说明SIOCGIFADDR和SIOCGIFNETMASK是两个标准 ioctl 请求码分别取网卡 IP 和子网掩码。拿到后交给ipaddress模块算出网络号。参数上ifname要换成你实际的网卡名Windows 上得用socket.gethostbyname配合netifaces库ioctl 那套不通用。算出网段后主机地址范围就是net.hosts()遍历它逐个发 ARP 请求即可。注意 /16 这种大网段有六万多地址全量扫会拖很久实际要分片或加超时控制。3. 用 Python 手搓一个 ScanLan从发包到出表3.1 环境准备与依赖选择自己实现 ARP 扫描核心依赖是scapy它能让你在用户态直接构造和发送二层帧。安装pip install scapyLinux 下需要 root 或CAP_NET_RAW权限因为要操作原始套接字。Windows 上要装 Npcap并把它的驱动选上。这一步翻车的人不少报PermissionError基本都是权限或驱动没到位。3.2 构造并发送 ARP 请求from scapy.all import ARP, Ether, srp def arp_scan(network): 对指定网段发 ARP 请求返回 IP-MAC 列表 # Ether 的 dst 设为广播地址ARP 的 pdst 设为目标网段 pkt Ether(dstff:ff:ff:ff:ff:ff) / ARP(pdstnetwork) # srp 在二层收发timeout 控制等待retry 控制重试 answered, _ srp(pkt, timeout2, retry1, verboseFalse) result [] for sent, recv in answered: result.append({ip: recv.psrc, mac: recv.hwsrc}) return result for item in arp_scan(192.168.1.0/24): print(f{item[ip]:16} {item[mac]})逻辑说明Ether(dstff:ff:ff:ff:ff:ff)把帧发往广播地址保证同网段所有设备都能收到。ARP(pdstnetwork)里pdst是目标协议地址填网段时 scapy 会自动展开成逐个地址发。srp是二层收发函数返回「已应答」和「未应答」两组。参数说明timeout2是每轮等待秒数局域网内 1~2 秒足够retry1表示超时后再补发一次能显著降低丢包导致的漏报verboseFalse关掉 scapy 的刷屏输出。网段越大timeout要适当放大否则尾部地址还没回包就结束了。3.3 结果去重、排序与导出扫出来的结果可能有重复重试导致也可能顺序乱。整理一下import csv def dedup_and_export(records, pathscan_result.csv): seen {} for r in records: seen[r[ip]] r[mac] # 同 IP 以最后一次为准 rows sorted(seen.items(), keylambda x: tuple(map(int, x[0].split(.)))) with open(path, w, newline) as f: w csv.writer(f) w.writerow([IP, MAC]) w.writerows(rows) return rows逻辑说明用字典按 IP 去重天然覆盖重复项。排序时把 IP 按点分段转成整数元组避免字符串排序出现192.168.1.100排在192.168.1.2前面的问题。导出 CSV 方便后续做资产台账或和路由器 DHCP 列表比对。参数说明path是输出路径newline是 Windows 下防止 CSV 多空行的标准写法。如果要做 ip冲突排查把这份结果和路由器后台的绑定表对照同一 MAC 出现两个 IP、或同一 IP 出现两个 MAC就是冲突信号。3.4 厂商识别从 MAC 前缀看设备是谁MAC 地址前三个字节是 OUI能查出厂商。这一步对识别杂牌摄像头、判断设备类型很有用# OUI 库可用 pip install mac-vendor-lookup from mac_vendor_lookup import MacLookup lookup MacLookup() # 首次使用需更新数据库 # lookup.update_vendors() def vendor_of(mac): try: return lookup.lookup(mac) except Exception: return 未知厂商 print(vendor_of(b8:27:eb:11:22:33)) # 树莓派基金会逻辑说明MacLookup内部维护 OUI 到厂商的映射表lookup传入完整 MAC 返回厂商名。首次使用要update_vendors()拉一次数据库之后可离线用。参数说明MAC 格式要统一大小写和分隔符不一致会查不到建议先mac.lower().replace(-, :)归一化。查不到不代表设备有问题很多白牌设备用的是未注册或复用的 OUI。4. 避坑与排查ScanLan 扫不全、扫不准的 5 个真实原因4.1 现象只扫到网关和本机其他全无原因网卡选错了。多网卡机器有线无线虚拟网卡上scapy 默认可能从错误的接口发包包发到了没有目标设备的网络里。解决显式指定接口。srp(pkt, ifaceeth0, timeout2)接口名用ip link或ifconfig确认。虚拟机里尤其要注意NAT 模式和桥接模式扫出来的结果完全不同桥接才能看到物理网段。4.2 现象能扫到 IP但 MAC 全是同一个原因跨了三层或者中间有代理 ARP。典型场景是你在 NAT 网络里扫外部网段回包的都是网关的 MAC。解决确认自己在同一广播域。如果必须跨网段改用 SNMP 查路由器的 ARP 表或者登录交换机看 MAC 地址表别硬用 ARP 扫。4.3 现象结果时多时少不稳定原因timeout太短或者网段太大导致尾部地址没等到回包。无线网络下丢包更明显。解决把timeout提到 3~5 秒retry加到 2。大网段分片扫比如 /16 拆成 256 个 /24 逐个来。无线环境建议多扫几轮取并集。4.4 现象扫到了但设备名、厂商全是未知原因OUI 数据库没更新或者设备用了随机 MAC。手机和部分 IoT 设备为了隐私会启用 MAC 随机化每次连接都换。解决更新 OUI 库对随机 MAC 设备靠 IP、开放端口、DHCP 主机名综合判断别只依赖 MAC。这也是为什么资产台账要 IP、MAC、主机名三列一起记。4.5 现象扫描把网络搞卡了原因发包速率太高广播风暴。在设备多、交换机性能差的网络里全速 ARP 扫描会明显影响通信。解决加发包间隔。scapy 的srp支持inter参数控制包间隔inter0.01表示每包间隔 10 毫秒。生产网络里宁可慢一点也别把业务网扫挂。5. 进阶把 ScanLan 做成能长期用的资产发现习惯单次扫描只是快照真正有价值的是把它变成周期性动作配合对比找出「新出现的设备」和「消失的设备」。我一般会写一个对比脚本把本次结果和上次结果做差集def diff_scan(old_rows, new_rows): old {ip: mac for ip, mac in old_rows} new {ip: mac for ip, mac in new_rows} added [(ip, new[ip]) for ip in new if ip not in old] removed [(ip, old[ip]) for ip in old if ip not in new] changed [(ip, old[ip], new[ip]) for ip in new if ip in old and old[ip] ! new[ip]] return added, removed, changed逻辑说明三个差集分别对应「新上线」「已下线」「IP 没变但 MAC 变了」。第三种最值得警惕可能是 IP 被重新分配也可能是有人手动改了配置做 ip冲突排查时这是第一手线索。参数说明old_rows和new_rows都是(ip, mac)元组列表直接来自前面的扫描结果。实际用的时候把每次结果按日期存 CSV对比时读最近两次即可。再往上走一层可以把扫描结果和路由器的 DHCP 租约表、交换机的 MAC 地址表做三方比对。三者一致说明台账干净不一致的地方就是你要去现场确认的点。我踩过最深的坑是曾经完全信任扫描结果结果一台设备因为 MAC 随机化每次扫描都显示成新设备台账里堆了几十条重复记录。后来加了「按主机名开放端口聚类」的逻辑才压下去。验证扫描是否可信有个简单办法拿一台你知道 MAC 的设备手动在路由器上做静态绑定然后扫描看结果里的 MAC 和绑定的是否一致。一致说明链路层抓取没问题不一致先查接口和网段别急着怀疑代码。这套东西的价值不在于扫一次而在于把它变成习惯——每周跑一次存好历史设备一有异动你第一时间就知道。我自己是把它挂在树莓派上定时跑结果推到本地表格里省了太多「找不到对方设备」的来回折腾。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑