☰
ARP欺骗代码实战:C++与WinPcap伪造ARP报文全解析
2026/10/5 5:54:07 网站建设 项目流程

简介:这是一份面向网络协议学习者和安全初学者的ARP欺骗研究代码包,基于C++实现,涵盖从ARP地址解析、报文构造到欺骗攻击逻辑的完整过程。包体共10个文件,以cpp源码、h头文件和lib链接库为主,另含Visual Studio工程配置及辅助文件,总大小仅23KB,便于快速查看与二次编译调试。已有177人学习,资源虽小但结构典型,涉及Packet32抓包、WS2_32网络编程等关键接口,适合配合抓包工具观察ARP请求/应答与中间人攻击流程。通过阅读源码可理解伪造ARP报文触发地址映射篡改的原理,并据此思考网关绑定、动态ARP检测等防护手段,有助于打通网络协议、Windows网络编程与网络安全三方面知识。

1. ARP欺骗代码包:它到底能让你学会什么

如果你翻过网络协议的书,一定见过ARP那一章只有几十行文字:IP要换MAC,广播问一下,被问的人回一下。这种描述配上抓包图,看着明白,可真到自己动手写,才发现一个问题:什么函数能把一个ARP包发出去?怎么让应答包里的“我是192.168.1.1”骗得过对端?这份arp.rar里的arp欺骗代码,就是回答这些问题的——它用C++把WinPcap发包、抓包、伪造ARP应答全串起来了。压缩包里是VC6工程,直接能编译,适合两类人:一是刚读完TCP/IP协议栈、想亲手构造一个ARP报文的初学者,二是要做局域网安全实验但不想从零写抓包循环的从业者。它的价值不在“能攻陷什么”,而在把ARP协议从文字变成可运行的代码。

2. 拆包看结构:从文件清单反推代码的技术栈与编译方式

2.1 arp.cpp与Packet32.h:WinPcap抓包库的核心用法

拿到压缩包,先别急着双击arp.dsw。先看文件名单,里面藏着这个项目的技术选型:arp.cpp是唯一源码文件,说明整个ARP欺骗逻辑集中在一个文件里,没有分模块。它include了Packet32.h,这个头文件来自WinPcap开发者包,是驱动级抓包接口的入口。注意它不是pcap.h,而是Packet32.h——这意味着代码很可能不是用libpcap的跨平台API,而是直接用了WinPcap的底层PacketSendPacket、PacketReceivePacket之类函数。这样做的好处是更贴近NDIS驱动,缺点是只能在Windows下编译运行。

为什么不用WSAIoctl那套?因为ARP欺骗需要发送一个“不受系统协议栈影响”的原始帧。WinSock2虽然能发原始套接字,但Windows从XP SP2开始限制RAW Socket收发,而且系统会干扰ARP包的处理。WinPcap的Packet32接口直接旁路协议栈,从NDIS驱动层面收发光以太网帧,这才是ARP欺骗能成立的关键。arp.cpp里大概率有一段初始化网卡、读取MAC地址的代码,用的就是PacketOpenAdapter,然后配合pcap_sendpacket的底层版本PacketSendPacket发包。

2.2 项目文件与链接库:Visual C++ 6.0时代的编译模型

看文件后缀就能判断这是Visual C++ 6.0工程:arp.dsw是工作区文件,arp.dsp是项目文件,arp.ncb是类浏览信息缓存,arp.opt是工程选项缓存,arp.plg是构建日志。这些文件告诉你,作者当时用的是VC6+WinPcap 3.x/4.x时代的环境。编译时,工程依赖两个库:WS2_32.LIB和Packet.lib。前者是Windows Socket 2库,提供socket、inet_addr、htons这类函数;后者是WinPcap的Packet接口静态库,对应Packet32.h。

在现代编译器里打开这个工程,最直接的坑是VC6的预编译头设置。老工程默认把stdafx.h作为预编译头,但源文件清单里没看到stdafx.h,说明作者可能关闭了预编译选项,或者直接在source文件里手动声明了Windows头文件。编译报错C1010(未找到预编译头)时,请在工程设置里把Yc改成Yu,或者直接设置“不使用预编译头”。另外,项目默认字符集是ANSI,如果代码里有中文注释,在中文版系统上打开会有乱码,但不影响编译。

2.3 代码包的运行边界:它为什么只做ARP欺骗而不做TCP转发

很多人拿到ARP欺骗代码,第一句就问:能劫持HTTP吗?能抓微信密码吗?看文件清单就知道,这个项目没有实现数据转发。它只做两件事:持续发送伪造的ARP应答,告诉目标主机“网关的MAC是我”,同时告诉网关“目标主机的MAC是我”。至于流经目标主机的TCP数据包,代码没有做IP转发,也没有开启Windows的IPEnableRouter注册表项。也就是说,受害者在欺骗期间会断网,而不是被中间人悄无声息地转发流量。

这不是作者的疏忽,而是学习型代码的合理边界:专注演示ARP状态表的篡改过程,把复杂的数据转发、会话重建留给读者自己扩展。真要做中间人,至少还需要:启用系统路由功能、用libnet或自己构造IP包转交数据,还要处理TTL减1、TCP序列号透传。所以如果目标是想读懂ARP欺骗的报文交互,这个包很合适;如果是想做一个攻击工具,它只是起点。运行它你会看到效果:受害者ping不通外网,但能ping通攻击机,因为网关和受害者之间的双向MAC映射都被改成攻击机的MAC了。

3. 核心代码还原:WinPcap下ARP请求与应答的构造流程

3.1 构造ARP报文:用结构体做内存映射

ARP帧头固定42字节:14字节以太网头,28字节ARP数据。常见实现是定义两个结构体,然后把它们fwrite到缓冲区。注意以太网头要先写目标MAC,再写源MAC,最后是0x0806类型。下面这段代码是典型写法,你在这个包里看到的arp.cpp应该就是类似结构。

// 以太网帧头 typedef struct _ETH_HDR { BYTE dst_mac[6]; // 目的MAC,伪造应答时填受害者MAC BYTE src_mac[6]; // 源MAC,填攻击机网卡MAC WORD ether_type; // 0x0806 = ARP } ETH_HDR; // ARP报文体,按RFC 826 typedef struct _ARP_HDR { WORD hw_type; // 1 = Ethernet WORD proto_type; // 0x0800 = IP BYTE hw_len; // 6 BYTE proto_len; // 4 WORD opcode; // 1=请求 2=应答 BYTE sender_mac[6]; // 发送方MAC BYTE sender_ip[4]; // 发送方IP BYTE target_mac[6]; // 目标MAC BYTE target_ip[4]; // 目标IP } ARP_HDR;

逻辑说明:把固定的协议字段写进结构体,直接用memcpy把值填入偏移位置,比逐字节拼接更不容易错。发送时,ETH_HDR里的ether_type必须转网络字节序,ARP_HDR里所有WORD字段也要用htons。否则抓包软件里看到的类型是0x0800(IP)而不是0x0806(ARP),opcode从2变成0x0200,欺骗直接失效。

参数说明:hw_type填1,proto_type填0x0800,这两个值都是固定协议号。hw_len和proto_len是地址长度,6和4。opcode是灵魂:请求填1,应答填2。做欺骗时,你主动发的全是应答(opcode=2),而且不受收到请求的限制——这也是ARP无状态协议的缺陷:任何人都能主动向受害者发应答,受害者无条件相信。sender_mac、sender_ip就是你要伪造的“身份”。注意sender_mac填的是攻击机网卡真实MAC,不是伪造IP对应的真机MAC,因为二层帧的源MAC必须能被交换机学习,否则网关直接丢弃。

3.2 收发循环:pcap_sendpacket与pcap_next_ex怎么配合

构造完包体之后,主循环一般是这样:定时发两个应答,一个给受害者(告诉它“网关IP是我的MAC”),一个给网关(告诉它“受害者IP是我的MAC”)。循环间隔通常设1到2秒,目的是对抗ARP缓存老化。下面是基于Packet32接口的伪代码,也是这个项目最可能用的模式。

// 打开网卡:0为第一个适配器 LPADAPTER adapter = PacketOpenAdapter("\\Device\\NPF_{网卡GUID}"); if (!adapter) { printf("Open adapter failed\n"); return -1; } // 构造一个60字节的buffer,前面填ETH_HDR,后面填ARP_HDR char sendbuf[60]; ETH_HDR eth; ARP_HDR arp; memcpy(eth.dst_mac, victim_mac, 6); // 欺骗受害者时,目的MAC填受害者 memcpy(eth.src_mac, attacker_mac, 6); eth.ether_type = htons(0x0806); // ... 填充arp结构,opcode = htons(2) // 打到发到网卡的netmon_buffer memcpy(sendbuf, &eth, 14); memcpy(sendbuf + 14, &arp, 28); // 补够60字节(最小以太网帧长) for (int i = 42; i < 60; i++) sendbuf[i] = 0; // 持续发送 while (1) { PacketSendPacket(adapter, (PPACKET)sendbuf, 60, TRUE); Sleep(1000); // 1秒一次,抗老化 }

逻辑说明:PacketSendPacket的第一个参数是网卡句柄,第二个参数要的是一个PACKET结构,而不是裸buffer。这里为了简化,示意为直接传buffer,真实代码会先把sendbuf拷贝到PACKET的Data成员里,然后设置Length字段。Sleep(1000)不能去掉,也不能太长——ARP缓存表默认老化时间在Windows上是45秒,但在收到新应答时会立即更新;1秒一次是为了保证受害者表项始终指向你,网络恢复时又能迅速还原。

参数说明:接收部分用PacketReceivePacket或pcap_next_ex都可能。如果是学习用,更推荐pcap,因为PACKET结构更靠近libpcap,将来移植到Linux只改接口名。注意循环里最好加一个检测:如果受害者发来的流量里已经有ARP请求,你可以实时回复一个伪造应答,这样更逼真。但这个包大概率是死循环发,不做请求响应——这正是它实现简单的体现。

3.3 关键参数:网卡索引、IP/MAC映射、伪造源地址的填写

真正跑起来会卡住的第一个参数是网卡名字。PacketOpenAdapter需要的是设备路径,形如\Device\NPF_{7E1A0D2B-...},而不是“本地连接”。获取方式有两种:用PacketFindAllAdapters遍历,或者用WinPcap自带的wpcap.dll里的pcap_findalldevs_ex。代码里如果写死了网卡名,你就要去适配器管理里查GUID,然后手动替换。更常见的做法是代码里遍历,让用户选序号。

第二个参数是ARP缓存表里的MAC地址。攻击机不一定知道受害者的真实MAC,所以代码里至少要有两个分支:一个是用一个ARP请求去探测目标(发广播请求“谁是这个IP”),从回包提取MAC;另一个是直接从网络抓包流量里学。很多ARP欺骗代码会硬编码受害者MAC,这在静态环境演示没问题,但换个实验环境就翻车。建议拿到包后,先把网卡混合模式打开,抓到受害者发来的数据帧,自动记录source MAC,这样就不用手动填了。

第三个参数是IP地址长度。ARP_HDR里sender_ip是BYTE[4],如果用char*保存“192.168.1.1”,一定要先inet_addr转成u_long,再memcpy。很多人在这里直接strcpy,导致IP字节序错乱,欺骗时受害者收到的“网关IP”变成1.1.168.192,协议栈直接丢弃。这个坑在抓包软件里看ARP包内容时特别明显。

4. 避坑与常见问题:旧项目在Win10/新WinPcap下复活

很多人把arp.rar下载下来,编译时报错一片,第一个念头是“代码太老”。实际多数是自己环境没配对。这里列几个最常踩的坑,按现象→原因→解决写。

4.1 问题一:头文件找不到,编译报错fatal error C1083

现象:打开arp.dsp直接编译,提示无法打开Packet32.h或DEVIOCTL.H。原因:VC6默认在工程目录和系统Include目录里找头文件,但WinPcap开发者包的头文件装在WinPcap\Include目录,工程没有添加这个路径。解决:把Packet32.h和DEVIOCTL.H复制到源码同目录,或者在VC菜单Tools→Options→Directories里把Include和Lib路径加上。DEVIOCTL.H其实是NDIS驱动访问IOCTL的头文件,WinPcap头文件依赖它,单独复制就行。顺带检查一下Packet32.h里的#include <windows.h>,老版本可能写的是<windows.h>,在VC里没问题,但VC2005以后要改成<Windows.h>才不会因为大小写混淆。

4.2 问题二:lib文件链接顺序和命名约束

现象:编译过了,链接报错LNK2001 unresolved external symbol __imp__PacketSendPacket@16,或者WS2_32.LIB里的socket函数未解析。原因:Packet.lib和WS2_32.LIB没有添加到工程依赖,或者添加了但顺序不对。VC6里链接顺序会影响解析,Packet.lib依赖WS2_32。解决:在Project Settings→Link→Object/library modules里写“Packet.lib WS2_32.LIB”(注意先Packet后WS2_32)。如果你用的是WinPcap 4.1.2,Packet.lib在WpdPack\Lib\x86目录下,要选x86版本,别选x64——VC6本来就是32位编译器,选了x64的lib直接报找不到符号。

4.3 问题三:抓不到自己的包,pcap_next_ex一直超时

现象:运行代码后,发送循环在发包,但注释掉的抓包代码收不到任何ARP包,抓包循环里timeout一直触发。原因:网卡没开混合模式(promiscuous mode)。Packet32接口在打开网卡后,需要调用PacketSetHwFilter设置NDIS_PACKET_TYPE_PROMISCUOUS,而不是仅调pcap_next_ex。大多数人的抓包循环里写了pcap_setfilter过滤只收ARP,但忘了底层需要混合模式。解决:在OpenAdapter后加一行PacketSetHwFilter(adapter, NDIS_PACKET_TYPE_PROMISCUOUS)。如果是Win10,还需要检查“允许应用程序绕过系统网络栈”的WinPcap服务是否启动,命令是net start npf_ndis或者npcap的服务。

4.4 问题四:欺骗不生效,受害者ARP表里的网关MAC一直不变

现象:代码在跑,受害者ARP表里网关MAC还是真实网关,ping外网正常。稍等会儿,又变成了攻击机MAC,然后一会儿又变回去。原因:这个代码只发了单向欺骗——只改了受害者的表,没改网关的ARP表。网关发现自己到受害者的包发不出去,会重新发ARP请求,学习到受害者真实MAC并覆盖。解决:这个代码包里如果只有一条发包循环,那就是作者自己打错方向了。应在同一循环里发送两条应答:一条发给受害者(VICTIM_IP→ATTACKER_MAC),一条发给网关(GATEWAY_IP→ATTACKER_MAC)。如果你拿到的版本里没有这条,需要自己在发包循环里加第二个sendbuf。这个坑很容易忽略,但一旦出现,用抓包软件一看“双向流量不对称”就明白了。

4.5 问题五:VC6编译通过但运行时提示0xc000007b或找不到入口点

现象:VC6下编译、链接全过,生成exe,双击运行弹“应用程序无法正常启动0xc000007b”,或者提示“找不到wpcap.dll”。原因:WinPcap库分为Packet.dll和wpcap.dll,运行时从System32或程序目录加载。VC6默认用动态加载Packet.lib,如果系统装了Npcap而不是WinPcap,会出现接口不兼容。Npcap的npf服务是Npcap Loopback Adapter,跟老代码的设备路径不符。解决:切换到Npcap的WinPcap兼容模式——在Npcap安装时勾选“Install Npcap in WinPcap API-compatible Mode”,或者把Packet.lib改为静态链接。更省事的方法是卸载Npcap,装回WinPcap 4.1.3。如果你是Win10/11,推荐用Wireshark自带Npcap,然后在代码里调用pcap_lookupdev动态获取网卡名,别写死\Device\NPF_,因为新版本GUID变了。

5. 进阶:从欺骗代码到诊断工具——验证与防护思路

拿到这份代码,别急着拿它去折腾同事的电脑。先把它当学习样本,用三步验证你是否真的理解了它。第一步,用Wireshark抓包看发送循环里的ARP报文——只看opcode=2的包,观察sender_mac是不是你的网卡MAC,sender_ip是不是你伪造的网关IP。如果你能看到受害者发来的ARP请求,说明你的网卡混合模式正常。第二步,在受害者机器上打开cmd,输入arp -a,看网关IP对应的MAC是不是你机器物理地址。第三步,做双向欺骗后再从受害者机器ping外网,观察是否全丢包——丢包正常,不丢包说明你的欺骗被网关的反向ARP请求修复了。

接下来想做点正事,这里有三个改进方向。第一个是把Sleep(1000)改成动态适应ARP老化:你可以周期性发送ARP请求去探测,频率从2秒降到500毫秒再提到2秒,观察受害者ARP缓存更新情况,直到找出稳定欺骗的最小频率。第二个是增加被动学习:在发包循环里监听收到的ARP包,实时提取受害者IP和MAC的映射,这样不需要硬编码受害者MAC。第三个是做防护练习:在同一台机器上写一段ARP攻击检测代码——连续N秒收到两个不同IP对应两个不同MAC的ARP应答,就报警“网关被欺骗”。这段代码和攻击代码用的是同一组Packet32函数,能把破坏性学习变成防御性学习。

使用这份代码还有一条底线:不要在别人的网络里做测试,尤其是公司网、学校网,ARP欺骗即便只发几秒,也会打断整个子网所有主机的TCP连接,网关日志和网管告警都查得到。我自己的习惯是,在虚拟机里搭两台Ubuntu,一个Windows宿主用Bridge,专门开一个隔离网段来跑这套代码。第一次跑通时,看到受害者虚拟机里的arp -a变成自己的MAC,那种感觉确实挺震撼,但也让我意识到ARP协议在设计上完全没有认证机制——从那以后我每调一次网络排障,都会先在交换机上看ARP表是否突变,也算是被这份代码改变了看网络的方式。这份资源适合你下载后,把arp.cpp打开,逐行对着RFC 826注释着读,比任何教材都直观。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询