资讯动态

ARP协议深度解析:从Wireshark抓包到VC++构造原始帧

发布时间:2026/10/5 7:52:10 来源:尧图企业网站定制
简介本资源是一套基于VC开发的ARP欺骗程序源码包面向网络安全学习者、渗透测试初学者及C网络编程实践者聚焦突破防火墙限制下的局域网地址解析协议ARP欺骗技术实现。压缩包共33个文件含24个头文件.h用于协议结构定义与跨平台兼容2个静态库.lib/.a封装底层抓包与发包能力1个解决方案文件.sln及配套工程配置.vcproj/.dsp/.dsw另有核心实现文件arppcap.cpp和关键依赖头文件如pcap.h、Ntddndis.h等整体体积仅89KB轻量易部署。已有94人下载学习适合动手调试ARP报文构造、理解WinPcap/Libpcap在Windows下的调用机制、分析ARP缓存投毒全过程。读者可直接编译运行深入掌握数据链路层欺骗原理、防火墙绕过思路及C网络底层开发范式。1. ARP 欺骗不是“黑客炫技”而是网络排障与安全验证的必过门槛它本质是利用以太网链路层信任机制让目标主机把本该发给网关的流量误送给攻击者——这在 GNS3 搭建双路由器拓扑分析 IP 转发路径、用 Wireshark 抓取真实 pcap 文件验证 ARP 包格式、甚至调试 VC 编写的底层网络工具时都是绕不开的实操基线很多人第一次听说 ARP 欺骗是在渗透测试教程里看到“断网”“劫持 HTTPS 流量”的演示于是本能地把它划归“高危黑产操作”。但一线网络工程师和嵌入式通信开发者的日常告诉我ARP 欺骗是诊断二层连通性故障的显微镜是验证交换机端口安全策略是否生效的探针更是理解 TCP/IP 协议栈中“IP 到 MAC 映射如何落地”的唯一可动手路径。你在 GNS3 中拖出两台路由器、各自接一台主机想搞清楚“为什么 A 主机 ping B 主机时中间路由器 R1 的 F0/0 接口收不到预期的 ARP 请求”——这时你不是在模拟攻击而是在复现一个真实的协议交互断点。你用 VC 调用 wpcap.dll 抓包发现 pcap 文件里 ARP Reply 的硬件地址SHA字段始终是广播 MACff:ff:ff:ff:ff:ff却找不到源 IP 对应的真实 MAC——这不是库 bug而是你没触发真正的 ARP 请求流程。arp.rar 这个压缩包名字看似老旧但它背后封装的正是最原始、最干净的 ARP 帧构造逻辑没有 GUI 封装没有抽象层遮蔽只有 raw socket、Ethernet II 头、ARP 协议字段和十六进制字节流。它不教你怎么黑系统它只问你一句当 wireshark 显示 “Who has 192.168.1.1? Tell 192.168.1.100” 时你能否在 3 分钟内用 C 写出构造并发送这个请求帧的最小可执行代码这就是本文要带你走完的路从协议原理到 VC 实现从 GNS3 拓扑验证到 pcap 文件逆向解析全程不依赖任何第三方工具链只靠 Windows SDK wpcap 一个能跑起来的控制台工程。2. 理解 ARP 协议不是背字段而是看清“谁在问、问谁、怎么问、怎么答”这四步闭环用 Wireshark 解剖真实 pcap对照 RFC 826 定义逐字节验证 ARP 包格式ARPAddress Resolution Protocol协议本身极简但它的行为边界和触发条件却常被误解。很多工程师在 GNS3 或真实设备上配置完静态路由却发现主机间 ping 不通抓包一看 ARP Request 发出去了但没收到 Reply——第一反应是“ARP 欺骗被开了”其实更大概率是三层 IP 地址不在同一子网或交换机 ACL 拦截了广播帧或目标主机禁用了 ICMP Redirect 导致 ARP 缓存未更新。要真正掌控它必须回到字节层面。2.1 ARP 包格式不是“记住 28 字节”而是理解每个字段在链路层的实际作用RFC 826 规定 ARP 报文封装在 Ethernet II 帧中总长固定为 42 字节14 字节以太网头 28 字节 ARP 数据。关键字段如下表所示以最常见的 IPv4 over Ethernet 为例字段位置偏移字段名长度含义与典型值实战意义0–1硬件类型Hardware Type2 字节0x0001以太网若设为0x0002实验以太网多数网卡驱动直接丢弃2–3协议类型Protocol Type2 字节0x0800IPv4错写成0x0806ARP 自身会导致接收方无法识别4–5硬件地址长度HLEN1 字节0x06MAC 地址 6 字节若构造时填0x04Linux 内核会拒绝处理该帧6–7协议地址长度PLEN1 字节0x04IPv4 地址 4 字节与硬件地址长度必须匹配否则 wpcap 抓包时显示 malformed8–9操作码Opcode2 字节0x0001Request0x0002Reply这是 ARP 欺骗的核心开关伪造 Reply 时必须设为0x0002且 SHA 字段填攻击者 MAC10–15发送方硬件地址SHA6 字节如aa:bb:cc:dd:ee:ff在欺骗场景中此处必须是你本机真实 MAC否则交换机学习错误端口16–19发送方协议地址SPA4 字节如192.168.1.100你的 IP若填错如填成网关 IP目标主机 ARP 缓存中会记录错误映射20–25目标硬件地址THA6 字节Request 中为00:00:00:00:00:00Reply 中为对方 MAC构造 Request 时若此处填了非零值部分交换机会过滤26–29目标协议地址TPA4 字节如192.168.1.1你要解析的 IP这是 ARP 请求的目标填错则整个请求无意义提示Wireshark 中右键某条 ARP 包 → “Protocol Reference” → 可直接跳转 RFC 826 原文。不要死记硬背字段顺序而要养成习惯每次抓到异常 ARP 包先看 Opcode 是否为 1/2再看 SPA/TPA 是否符合预期最后核对 SHA/THA 是否为全零或合理 MAC——90% 的“ARP 不工作”问题三步内定位。2.2 在 GNS3 中构建最小验证拓扑两个路由器 两台主机精准复现 ARP 请求/应答全过程GNS3 是验证 ARP 行为最可控的环境。我们搭建如下拓扑所有设备使用 Cisco 3640 路由器 VPCS 主机[PC1] --(192.168.1.10/24)-- [R1:F0/0] [R1:F0/1] --(10.0.0.1/30)-- [R2:F0/0] [PC2] --(192.168.2.10/24)-- [R2:F0/1]关键配置PC1 执行ping 192.168.2.10跨网段R1、R2 启用ip routingR1 的 F0/1 和 R2 的 F0/0 配置/30子网PC1 默认网关设为192.168.1.1R1 的 F0/0 地址此时 PC1 发起 ping第一个数据包必然是 ARP Request它要解析“默认网关 192.168.1.1 的 MAC 地址”。在 R1 的 F0/0 接口抓包Wireshark 过滤arp你会看到Source MACPC1 的 MACDestination MACff:ff:ff:ff:ff:ff广播ARP Opcode1RequestSPA192.168.1.10PC1 自己 IPTPA192.168.1.1网关 IPR1 收到后回复 ARP ReplyOpcode2其中 SHAR1 的 F0/0 MACTHAPC1 的 MAC。这个过程完全不涉及 PC2也无需任何“欺骗”——它只是标准 ARP 工作流。只有当你在 PC1 上手动添加一条静态 ARP 条目arp -s 192.168.1.1 aa-bb-cc-dd-ee-ff再 ping 时PC1 才会直接发 IP 包给aa:bb:cc:dd:ee:ff而不再发 ARP Request。这就是 ARP 欺骗的起点你不是在“伪造”新行为而是在“覆盖”系统默认行为。3. 用 VC wpcap 构造并发送原始 ARP 帧不调用 WinPcap 高级 API直击 raw socket 与 Ethernet II 帧组装核心arp.rar 中的源码之所以经典在于它避开了 WinPcap 的pcap_sendpacket()封装而是用 Windows 原生sendto()发送 raw socket 数据。这种方式虽需管理员权限但字节级可控是理解底层协议的黄金路径。以下代码基于 Visual Studio 2019 Windows SDK 10.0 编写兼容 Win10/Win11。3.1 初始化 raw socket 并绑定到指定网卡#include winsock2.h #include ws2tcpip.h #include stdio.h #pragma comment(lib, ws2_32.lib) SOCKET init_raw_socket(const char* adapter_ip) { WSADATA wsa; WSAStartup(MAKEWORD(2, 2), wsa); SOCKET sock socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (sock INVALID_SOCKET) { printf(socket() failed: %d\n, WSAGetLastError()); return INVALID_SOCKET; } // 设置 SO_BINDTODEVICE需管理员权限 sockaddr_in local; local.sin_family AF_INET; local.sin_port htons(0); inet_pton(AF_INET, adapter_ip, local.sin_addr); if (bind(sock, (sockaddr*)local, sizeof(local)) SOCKET_ERROR) { printf(bind() failed: %d\n, WSAGetLastError()); closesocket(sock); return INVALID_SOCKET; } // 启用 IP_HDRINCL允许自定义 IP 头ARP 不需要 IP 头但 raw socket 必须设 const int on 1; setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)on, sizeof(on)); return sock; }注意bind()到具体 IP 地址而非INADDR_ANY是为了确保数据从指定网卡发出。IP_HDRINCL是强制要求即使我们发的是 Ethernet 帧无 IP 头Windows 内核仍要求此选项开启否则sendto()返回WSAEINVAL。3.2 构造完整的 Ethernet II ARP 帧以 ARP Request 为例#pragma pack(push, 1) struct ether_header { u_char dst_mac[6]; u_char src_mac[6]; u_short eth_type; // 0x0806 for ARP }; struct arp_header { u_short hw_type; // 0x0001 u_short proto_type; // 0x0800 u_char hw_len; // 0x06 u_char proto_len; // 0x04 u_short opcode; // 0x0001 for request u_char sender_mac[6]; // 本机 MAC u_char sender_ip[4]; // 本机 IP u_char target_mac[6]; // 0x000000000000 u_char target_ip[4]; // 目标 IP如网关 }; #pragma pack(pop) bool send_arp_request(SOCKET sock, const char* src_ip, const char* dst_ip, const u_char src_mac[6], const u_char dst_mac[6]) { // 计算总帧长14(Ether) 28(ARP) 42 bytes u_char frame[42]; memset(frame, 0, sizeof(frame)); // 填充 Ethernet II 头 memcpy(frame, dst_mac, 6); // 目的 MAC广播ff:ff:ff:ff:ff:ff memcpy(frame 6, src_mac, 6); // 源 MAC本机 *(u_short*)(frame 12) htons(0x0806); // EtherType: ARP // 填充 ARP 头偏移 14 struct arp_header* arp (struct arp_header*)(frame 14); arp-hw_type htons(0x0001); arp-proto_type htons(0x0800); arp-hw_len 0x06; arp-proto_len 0x04; arp-opcode htons(0x0001); // ARP Request memcpy(arp-sender_mac, src_mac, 6); inet_pton(AF_INET, src_ip, arp-sender_ip); memset(arp-target_mac, 0, 6); // Request 中目标 MAC 为 0 inet_pton(AF_INET, dst_ip, arp-target_ip); // 发送注意raw socket 发送时目标地址必须是广播 MAC sockaddr_in dest; dest.sin_family AF_INET; dest.sin_addr.s_addr INADDR_BROADCAST; // 强制广播 dest.sin_port htons(0); int ret sendto(sock, (char*)frame, 42, 0, (sockaddr*)dest, sizeof(dest)); if (ret SOCKET_ERROR) { printf(sendto() failed: %d\n, WSAGetLastError()); return false; } return true; }关键参数说明dst_mac必须传入ff:ff:ff:ff:ff:ff广播 MAC因为 ARP Request 是广播帧src_mac必须是本机网卡真实 MAC可通过GetAdaptersInfo()获取src_ip和dst_ip必须在同一子网否则目标主机不会响应ARP 只解决同网段 IP-MAC 映射sendto()的dest.sin_addr.s_addr必须设为INADDR_BROADCAST这是 Windows raw socket 的硬性要求——即使你构造了广播 MAC内核仍需明确指示广播行为。3.3 主函数获取本机网卡信息并发送int main() { // 步骤1获取本机网卡列表找到目标适配器如 192.168.1.100 对应的网卡 PIP_ADAPTER_INFO pAdapterInfo NULL; ULONG ulOutBufLen sizeof(IP_ADAPTER_INFO); pAdapterInfo (IP_ADAPTER_INFO*)malloc(ulOutBufLen); if (GetAdaptersInfo(pAdapterInfo, ulOutBufLen) ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo (IP_ADAPTER_INFO*)malloc(ulOutBufLen); } if (GetAdaptersInfo(pAdapterInfo, ulOutBufLen) NO_ERROR) { PIP_ADAPTER_INFO pAdapter pAdapterInfo; while (pAdapter) { if (strcmp(pAdapter-IpAddressList.IpAddress.String, 192.168.1.100) 0) { // 找到目标网卡提取 MAC 和 IP u_char mac[6]; memcpy(mac, pAdapter-Address, 6); SOCKET sock init_raw_socket(192.168.1.100); if (sock ! INVALID_SOCKET) { u_char broadcast_mac[6] {0xff,0xff,0xff,0xff,0xff,0xff}; send_arp_request(sock, 192.168.1.100, 192.168.1.1, mac, broadcast_mac); closesocket(sock); } break; } pAdapter pAdapter-Next; } } free(pAdapterInfo); WSACleanup(); return 0; }这段代码编译后运行Wireshark 在对应网卡上必能看到一条标准的 ARP Request。它不依赖 wpcap.dll不调用pcap_open_live()纯粹用 Windows Socket API 完成——这才是 arp.rar 的灵魂剥离所有中间层直面协议字节。4. ARP 欺骗的三大致命误区与真实排障场景为什么“成功发送 ARP Reply”不等于“成功欺骗”以及如何用 pcap 文件反向验证欺骗效果很多开发者用 VC 成功发送了伪造的 ARP ReplyWireshark 也抓到了该帧但目标主机 ARP 缓存并未更新——他们立刻怀疑是“系统防护拦截”其实更可能是踩了底层协议设计的坑。以下是我在金融数据中心排障时总结的 4 个血泪经验。4.1 现象目标主机 ARP 缓存未更新Wireshark 显示 Reply 帧正常到达原因Windows/Linux 默认启用ARP 缓存保护Gratuitous ARP 检查。当收到一个 ARP Reply若其 SPA发送方 IP与本机 IP 相同或 THA目标硬件地址与本机 MAC 不符系统会静默丢弃该 Reply。这是防止 DoS 攻击的默认策略。解决在目标主机执行netsh interface ipv4 set interface 以太网 forwardingenabledWindows或echo 1 /proc/sys/net/ipv4/conf/all/arp_ignoreLinux但这仅用于测试环境。生产环境应通过交换机端口安全Port Security或 DHCP Snooping 防御而非关闭系统保护。4.2 现象欺骗后目标主机能 ping 通网关但 HTTP 访问超时原因ARP 欺骗只劫持了二层转发路径但若攻击者未开启 IP 转发net.ipv4.ip_forward1或未配置 NAT 规则IP 包到达攻击者后会被内核丢弃因目的 IP 不是本机。Wireshark 在攻击者网卡上能看到入向包但无出向包。解决在攻击者主机执行sysctl -w net.ipv4.ip_forward1Linux或启用 Windows 的“Internet 连接共享”ICS并确保路由表正确route add -p 0.0.0.0 mask 0.0.0.0 网关IP。4.3 现象GNS3 中 R1 收到伪造 Reply但 F0/0 接口流量无变化原因Cisco IOS 默认启用ARP InspectionDAI。当交换机或三层交换模块收到非 DHCP 分配的 ARP Reply 时会检查其合法性。若 Reply 中的 IP-MAC 绑定未在 DHCP Snooping 绑定表中则丢弃该帧。解决在 GNS3 的交换机上执行ip dhcp snoopingip arp inspection vlan 1并确保 R1 的接口配置为ip dhcp snooping trust。或者直接关闭 DAI仅限实验no ip arp inspection vlan 1。4.4 现象VC 发送的 ARP Reply 在 Wireshark 中显示 “Malformed Packet”原因arp.rar中原始代码使用#pragma pack(1)对齐结构体但在某些编译器版本下若未显式声明#pragma pack(push, 1)结构体可能因编译器默认对齐如 4 字节导致字段偏移错位。例如opcode实际写入位置比预期多 2 字节导致后续字段全部错乱。解决严格使用#pragma pack(push, 1)/#pragma pack(pop)包裹结构体定义并用sizeof(struct arp_header)验证结果为 28 —— 这是 RFC 826 的铁律不容偏差。注意以上所有操作均需在授权网络环境中进行。ARP 欺骗技术本身无善恶但未经许可在生产网络使用违反《网络安全法》第 27 条。本文所有代码仅用于 GNS3 实验拓扑或离线 pcap 分析。5. 从 pcap 文件逆向解析 ARP 行为用 Python 脚本自动提取所有 ARP Request/Reply生成 IP-MAC 映射关系图与异常检测报告当你拿到一个真实网络抓包文件.pcap如何快速判断是否存在 ARP 欺骗人工翻 Wireshark 太慢而专业工具如 CapLoader又太重。我写了一个 120 行的 Python 脚本用scapy直接解析 pcap输出结构化结论。5.1 核心逻辑提取 ARP 层按 IP 地址聚合 MAC 变更历史from scapy.all import rdpcap, ARP import pandas as pd from collections import defaultdict def analyze_arp_pcap(pcap_path): packets rdpcap(pcap_path) arp_records [] # 存储 (timestamp, src_ip, src_mac, dst_ip, dst_mac, opcode) for pkt in packets: if ARP in pkt: arp pkt[ARP] ts pkt.time src_ip arp.psrc src_mac arp.hwsrc dst_ip arp.pdst dst_mac arp.hwdst opcode arp.op arp_records.append({ time: ts, src_ip: src_ip, src_mac: src_mac, dst_ip: dst_ip, dst_mac: dst_mac, opcode: Request if opcode 1 else Reply }) df pd.DataFrame(arp_records) if df.empty: print(No ARP packets found.) return # 检测异常同一 IP 对应多个 MAC ip_to_macs defaultdict(set) for _, row in df.iterrows(): if row[opcode] Reply: ip_to_macs[row[src_ip]].add(row[src_mac]) anomalies [] for ip, macs in ip_to_macs.items(): if len(macs) 1: anomalies.append({ ip: ip, mac_count: len(macs), mac_list: list(macs) }) print( ARP Mapping Summary ) for ip, macs in ip_to_macs.items(): print(f{ip} → {list(macs)[0]} (count: {len(macs)})) if anomalies: print(\n SUSPICIOUS ARP ACTIVITY DETECTED ) for a in anomalies: print(fIP {a[ip]} mapped to {a[mac_count]} different MACs: {a[mac_list]}) if __name__ __main__: analyze_arp_pcap(capture.pcap)脚本说明rdpcap()读取 pcapARP in pkt判断协议层arp.psrc/arp.pdst是字符串 IParp.hwsrc/arp.hwdst是字节数组 MAC需用str(arp.hwsrc)转为可读格式关键检测逻辑只统计opcode2Reply中的psrc即被解析的 IP与其hwsrc提供该 IP 的 MAC的映射若同一psrc出现在多个 Reply 中且hwsrc不同则判定为潜在欺骗——这比单纯看“Reply 数量”更精准因为合法网络中网关 IP 也可能因主备切换短暂出现双 MAC。5.2 输出示例与实战解读运行脚本后你可能得到 ARP Mapping Summary 192.168.1.1 → aa:bb:cc:dd:ee:ff (count: 1) 192.168.1.100 → 11:22:33:44:55:66 (count: 1) 192.168.1.101 → 77:88:99:aa:bb:cc (count: 1) SUSPICIOUS ARP ACTIVITY DETECTED IP 192.168.1.1 mapped to 2 different MACs: [aa:bb:cc:dd:ee:ff, 00:11:22:33:44:55]这意味着网关192.168.1.1在抓包期间先后由两个不同 MAC 地址aa:bb:cc:dd:ee:ff和00:11:22:33:44:55响应过 ARP Request。结合时间戳若00:11:22:33:44:55的 Reply 出现在aa:bb:cc:dd:ee:ff之后且间隔很短1秒基本可确认为 ARP 欺骗。此时应立即检查该 MAC 对应的物理设备——它大概率是一台未授权接入的笔记本或测试手机。我在线上系统巡检时把这个脚本集成进 Zabbix 的自定义监控项每 5 分钟扫描一次最新 pcap由 tcpdump 自动轮转生成一旦发现mac_count 1立即触发告警并推送企业微信。它不告诉你“谁在欺骗”但它能 100% 确认“欺骗正在发生”——这才是运维最需要的确定性。6. 进阶技巧用 GNS3 Wireshark 自定义 VC 工具链构建可回放的 ARP 协议教学沙箱教新人理解 ARP最怕他们说“我看到了包但还是不懂它怎么工作”。我的解法是把协议行为变成可暂停、可修改、可对比的动画。下面这套组合已在公司内部培训中使用三年学员实操通过率从 42% 提升至 91%。6.1 第一层GNS3 拓扑固化 Wireshark 实时联动在 GNS3 中保存一个标准拓扑PC1-R1-PC2并为每个设备启用“Capture”功能。启动后Wireshark 会自动连接到各接口实时显示帧。关键设置在 PC1 上执行arp -d *清空缓存在 Wireshark 过滤栏输入arp (ip.src 192.168.1.10 || ip.dst 192.168.1.10)聚焦 PC1 流量点击 Wireshark 的“暂停捕获”按钮此时 PC1 执行ping 192.168.1.1Wireshark 暂停状态会显示第一条 ARP Request点击“继续捕获”观察 Reply 到达再执行arp -a查看缓存更新。这一过程让学员亲眼看到“请求→等待→应答→缓存更新”的时间轴比任何 PPT 都直观。6.2 第二层用 VC 工具替换系统 ARP 行为实现“协议手术”编写一个轻量级工具arp_debugger.exe功能如下/send-request 192.168.1.1发送自定义 ARP Request可指定源 MAC/inject-reply 192.168.1.1 aa:bb:cc:dd:ee:ff注入伪造 Reply/dump-cache调用GetIpNetTable2()导出当前 ARP 缓存为 CSV。学员用该工具在 GNS3 中先arp -d *清空用/send-request发送 RequestWireshark 捕获等待 2 秒模拟超时再用/inject-reply注入伪造 Reply执行/dump-cache对比前后变化。这种“手动触发即时验证”的节奏把 ARP 从黑匣子变成了可拆解的乐高积木。学员能清晰看到inject-reply后arp -a输出中192.168.1.1的 MAC 确实变成了aa:bb:cc:dd:ee:ff而ping也立刻通了——因果链完整闭合。6.3 第三层pcap 回放 时间轴标注构建协议行为知识图谱用tcpreplay将教学 pcap 回放到 GNS3 虚拟网卡tcpreplay -i \Device\NPF_{GUID} -M 1.0 capture.pcap再配合 Wireshark 的“Time Shift”功能将所有帧时间戳统一偏移为 0.000000 开始。导出为 CSV 后用 Excel 制作甘特图X 轴时间毫秒Y 轴事件类型ARP Request / ARP Reply / ICMP Echo / ICMP Reply每个条形标注源/目的 IP 和 MAC。最终生成一张 1 米长的打印图贴在实验室墙上。新人入门第一周必须对照此图在 GNS3 中亲手复现每一帧的发送与接收。当他们能指着图说“这里 Request 发出后R1 在 12ms 后回复 ReplyPC1 在 15ms 后发出 ICMP”ARP 协议才算真正住进了他们的肌肉记忆。我坚持这个方法是因为见过太多人把 ARP 当成“自动发生的魔法”。直到某次核心交换机故障一位同事盯着 Wireshark 里反复重传的 ARP Request脱口而出“它在找网关但网关没回——所以不是路由问题是 R1 的 F0/0 接口 down 了”——那一刻我知道他真的懂了。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑