基于WinPcap的UDP发包实战:亲手构造并发送原始数据包

发布时间:2026/10/19 5:37:23
基于WinPcap的UDP发包实战:亲手构造并发送原始数据包 简介这是一份基于WinPcap库实现的UDP发包程序源码包面向网络编程学习者、协议研究人员以及需要底层发包能力的安全测试人员。它重点演示如何绕过常规Socket接口直接通过WinPcap与网卡交互来构造并发送UDP数据报可服务于网络协议实验、丢包测试、实时数据传输等场景。包体共349个文件约5.94MB包含cpp/h核心源码、lib静态库、sln/vcxproj工程文件同时附带js/html/css等页面资源及jpg/png/gif图片便于查看演示效果或作为辅助界面。压缩包内还有libwpcap.a、libpacket.a等链接库读者可在Windows环境直接编译运行WinPcap_UDP_Test工程跟踪数据包从初始化、设备选择到发送的完整流程。目前已有188人学习适合具备一定C/C基础、希望深入理解WinPcap工作机制的开发者通过修改和扩展源码可快速定制自己的UDP发包、抓包或网络监控工具。1. 说到网络发包新手最先卡住的往往是这一步很多刚接触网络编程的朋友第一反应是“我不就想往网络里发点东西吗怎么还要学一堆协议”但你立刻会发现系统根本不让你随便发——你写一个普通程序用现成的接口往外发数据系统会帮你把很多事情都做掉这听起来很方便可一旦你想控制“发出的报文长什么样”就完全插不上手。今天要讲的「基于WinPcap实现的UDP发包程序」正好就是打破这个黑匣子的一把钥匙它绕开系统的层层封装让你能亲手构造一个 UDP 数据包然后从网卡直接发出去。这份资源适合谁适合刚学网络协议、想看看真实报文长什么样又不想一上来就啃几百页协议文档的初学者。你不需要很深的编程基础只要会一点 C 语言今天就能跑出第一个包。2. 先懂两个概念UDP 为什么快WinPcap 为什么特殊开始写代码之前有两个词必须弄明白一个是 UDP一个是 WinPcap。如果你以前听说过 TCP、UDP但总觉得它们是“考试要背的东西”这一章我用人话把它们讲清楚。2.1 UDP不打电话直接塞纸条UDP 的全称是 User Datagram Protocol用户数据报协议。怎么理解它你把 TCP 想象成“打电话”要先拨号、对方接听、你说一句对方回一句、最后说再见全程有确认有回应。而 UDP 像是“往对方门缝里塞纸条”你写好了塞进去就完事对方收没收到、收到了是不是完整的你不知道也不负责。这种“不负责任”的设计换来的是两个实实在在的好处低延迟和高效率。因为不需要握手、不需要等确认、不需要维护连接状态数据从你手里到网卡几乎是直出。所以凡是“慢一点就体验崩坏”的场景比如实时对战游戏、视频通话、语音、DNS 查询底层都是 UDP。UDP 的报文头也非常简单固定 8 个字节只有四段信息字段长度作用源端口2 字节发送方端口可填 0 表示不指定目的端口2 字节接收方端口对方程序靠它识别报文长度2 字节UDP 头 数据的长度校验和2 字节可选校验填 0 表示不校验你观察一下这四段里没有任何“连接状态”“序号”“确认号”之类的东西。这意味着什么意味着你手动构造一个 UDP 包非常简单——把这几段填上再塞进 IP 层交给网卡就完成了。这给手工发包程序提供了极大的便利。2.2 WinPcap为什么普通程序抢不到“发原始包”的资格你可能会问既然 UDP 构造这么简单我直接写个程序发不就行了问题出在操作系统不让你直接碰网卡。正常情况下你的程序要发网络数据必须走系统提供的 Socket 接口。系统会把你的数据做一次“加工”再加上它自己生成的各种头部最后才交给网卡。这个过程你无法干预。打个比方你写好一封信系统就像快递公司不仅帮你封装还会偷偷替你在信封上多写几行字。你大部分时候不需要关心这些字但如果你就是想让信封“完全按我的格式来”就做不到。WinPcap 的作用恰好是把“快递公司”绕开。它是一套运行在 Windows 上的网络抓包与发包库最核心的特点是它能在内核态直接和网卡驱动通信。也就是说它可以把网卡收到的原始帧完整交给你也能把你构造好的原始帧原封不动地发出去中间不经过系统的协议栈加工。这一下就解锁了很多玩法你可以自己填以太网头、自己写 IP 头、自己构造 TCP/UDP 头做出一个“系统完全不知道是谁发的”的数据包。网络监控、安全测试、协议分析这些工具能实现靠的正是这样一个能触碰底层的库。2.3 为什么不直接用 Socket 发 UDP非要绕这一圈我估计你会有一个很自然的疑问Socket 发 UDP 也很快啊几行代码就搞定了我为什么要用 WinPcap先看普通 Socket 发 UDP 的代码确实简洁// 伪代码示意 Socket 发 UDP 的写法 int sock socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP 套接字 sendto(sock, buf, len, 0, (struct sockaddr*)addr, sizeof(addr)); // 发送这段代码看起来轻松但它有几个你控制不了的点。第一你无法修改源 IP 地址系统只会填本机网卡的 IP第二你无法自定义以太网层的内容比如目的 MAC 地址系统会自己查询 ARP 帮你填第三如果你想发一个“IP 头里有特殊标志”的包或者想同时在多张网卡上发送一模一样的原始帧Socket 接口完全无能为力。而 WinPcap 的 pcap_sendpacket 这个函数收的是“已经完整构造好的整个数据帧”——从以太网头到 UDP 数据所有字节由你一手填完。它不管你填的是什么拿来就发。这听起来很麻烦但对初学者的价值恰好很大你被迫亲手填一遍 UDP 头、IP 头、以太网头填完之后你对“一个网络包到底由哪几层构成”会有非常直观的体感这比背十遍 TCP/IP 分层模型都管用。提示如果你只是想快速发个聊天消息给自己的程序用 Socket 就够了不用绕 WinPcap。但如果你想知道“网络包的每一字节从哪来”WinPcap 才是对的选择。3. 拿到源码包先做三件事解压、看结构、装环境光说不练没用。这一章带你把这份源码包真正用起来。我第一次拿到类似压缩包的时候一解压看到一堆文件直接懵了不知道该点哪个、不该动哪个。所以我把步骤拆成三步你跟着做就行。3.1 压缩包里到底有哪些文件先分清主次解压后你会看到类似下面这样的文件清单文件名大致作用libwpcap.aWinPcap 的静态库文件程序要链接的网络接口库libpacket.aWinPcap 底层驱动的辅助库链接时也会用到WinPcap_UDP_Test.aps工程资源文件一般是 Visual Studio 项目生成的辅助文件一串数字命名的文件通常是一些运行记录、截图或输出文件不影响主程序初学者最容易犯的错是看到一个文件就去双击。这里明确几点libwpcap.a 和 libpacket.a 你不用打开它们是给编译器在链接阶段用的真正要读的是里面的 .c 或 .cpp 源码文件以及可能的 README 说明文档。这个压缩包把两个库文件也一并收录了这是很贴心的做法。为什么因为 WinPcap 开发包官方下载后默认库文件是 .lib 格式配合 Visual Studio 使用。如果你用的是 MinGW 的 gcc需要 .a 格式的库。这份资源直接准备好了等于帮你绕开了“库格式不匹配导致链接报错”这个新手常见坎。3.2 装好开发环境WinPcap 驱动和编译器缺一不可先明确一个重要区别WinPcap 分成两个部分。第一部分是运行时驱动也就是你电脑上要装的 WinPcap 软件本身它负责让你能抓到网卡的数据第二部分是开发包也就是 WpdPack里面是头文件 wpcap.h 和库文件让你的代码能调用 WinPcap 的函数。有些初学者只装了运行时驱动就跑去编译代码结果报了一堆“找不到 wpcap.h”以为是代码错了。其实不是八成是缺了开发包。所以第二步做的事情是安装 WinPcap 运行时驱动。装完不用管它它在后台工作。下载 WpdPack 开发包解压后得到 Include头文件目录和 Lib库文件目录两个文件夹。把刚才压缩包里自带的 libwpcap.a、libpacket.a 复制到 WpdPack/Lib 里防止编译器找不到。如果你用的是 Visual Studio需要把 Include 和 Lib 路径填到工程的“附加包含目录”和“附加库目录”里。如果你和我一样更喜欢用 gcc 命令行路径更简单编译时用 -I 和 -L 参数指一下就行。3.3 验证环境用一条命令确认编译器能找到库环境配没配好最好的验证方式就是编译一个空白程序。别急着写发包逻辑先建一个新文件里面只写一个空 main 函数然后用下面这条命令链接 WinPcap 库gcc -o empty_test empty_test.c -lwpcap -lpacket -lws2_32命令拆开解释-o 指定输出文件名-lwpcap 告诉编译器链接 libwpcap.a-lpacket 链接 libpacket.a-lws2_32 是因为 WinPcap 内部依赖 Windows 的 Socket 相关函数必须补上否则最后一步链接会报 “undefined reference to …” 之类的错误。如果这条命令没有任何报错恭喜你的环境已经通了。如果报了“找不到 -lwpcap”说明编译器没在搜索路径里找到库文件用 -L 参数把库的路径指给它。这个验证小技巧可以帮你在后续调试中省下很多时间。4. 跑通第一个例子亲手发送一个 UDP 包环境准备好之后最激动人心的一步来了真正发一个 UDP 包。这一章我先把程序的主流程讲清楚再给你一份可以直接跑的完整代码最后告诉你怎么确认“它真的发出去了”。4.1 先看主流程任何 WinPcap 发包程序都只有四步不管源码包里的工程多复杂剥开来看核心流程只有四步用 pcap_findalldevs 获取你电脑上的网卡列表。用 pcap_open 打开其中一张网卡获得一个“句柄”。手动构造一个完整的数据帧以太网头 IP 头 UDP 头 数据。用 pcap_sendpacket 把这一整块字节从网卡发出去。这四步对应到代码结构上非常好认。你去看源码包里那个测试程序它的 main 函数基本就是按这个顺序组织的。很多初学者上来就盯着第二步 pcap_open 的返回值看纠结半天其实应该先从整体顺序理解先找网卡再打开再往缓冲区里填字节最后发出去。4.2 完整代码用 pcap_sendpacket 发一个自定义 UDP 包我平时测试发包时习惯把核心逻辑压缩成一个文件方便快速改、快速跑。你拿到的这份资源里核心机制也是一样的。下面是我整理过的示例你可以直接复制进一个新的 .c 文件里看#include stdio.h #include stdlib.h #include string.h #include pcap.h // 计算 IP 头校验和把数据按 16 位一组相加、回卷、取反 unsigned short checksum(unsigned short *ptr, int len) { unsigned long sum 0; while (len 1) { sum *ptr; len - 2; } if (len 1) sum *(unsigned char *)ptr; sum (sum 16) (sum 0xffff); sum (sum 16); return (unsigned short)(~sum); } int main() { pcap_if_t *alldevs, *dev; pcap_t *handle; char errbuf[PCAP_ERRBUF_SIZE]; unsigned char packet[128]; int i; // 第一步找网卡 if (pcap_findalldevs(alldevs, errbuf) -1) { printf(获取网卡失败: %s\n, errbuf); return 1; } // 简单起见选第一张网卡 dev alldevs; printf(使用网卡: %s\n, dev-description ? dev-description : dev-name); // 第二步打开网卡 handle pcap_open(dev-name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf); if (handle NULL) { printf(打不开网卡: %s\n, errbuf); return 1; } // 第三步手工构造以太网头 14 字节 unsigned char eth[14] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 目的 MAC全网广播 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // 源 MAC随便填的测试值 0x08, 0x00 // 表示上一层是 IPv4 }; memcpy(packet, eth, 14); // 手工构造 IPv4 头 20 字节 unsigned char ip[20] { 0x45, // 版本 4 头长 5 个 32 位字 0x00, // TOS 0x00, 0x2E, // 总长度最后统一填 0x00, 0x00, // 标识 0x00, 0x00, // 标志与分片偏移 0x40, // TTL 64 0x11, // 协议 17表示 UDP 0x00, 0x00, // 校验和填空后统一算 192, 168, 1, 100, // 源 IP可改成你的本机 IP 192, 168, 1, 200 // 目的 IP改成你要发往的机器 }; // 先让校验和字段为 0再整体计算 i 0; // 手动填总长度字段进程内计算避免新手在这里绕弯 ip[2] 0x00; ip[3] 0x2E; // 20(IP头) 8(UDP头) 26(数据) 54注意这里是净长后面统一说明 unsigned short csum checksum((unsigned short *)ip, 20); ip[10] (csum 8) 0xFF; ip[11] csum 0xFF; memcpy(packet 14, ip, 20); // 手工构造 UDP 头 8 字节源端口 12345目的端口 9999 unsigned char udp[8] { 0x30, 0x39, // 12345 0x27, 0x0F, // 9999 0x00, 0x22, // UDP 长度 34826 0x00, 0x00 // 校验和填 0表示不校验 }; memcpy(packet 34, udp, 8); // 数据部分固定字符串 const char *msg hello from winpcap udp test; memcpy(packet 42, msg, strlen(msg)); // 第四步发送整个帧 int total_len 42 strlen(msg); if (pcap_sendpacket(handle, packet, total_len) 0) { printf(发送成功总共 %d 字节\n, total_len); } else { printf(发送失败错误码请查看 errbuf\n); } pcap_close(handle); pcap_freealldevs(alldevs); return 0; }代码看完了我把几个新手最容易困惑的地方拆开讲。以太网头为什么是 14 字节因为它在链路层包含 6 字节目的 MAC、6 字节源 MAC、2 字节类型字段。IPv4 头我写了标准 20 字节协议字段填 17代表上层是 UDP。UDP 头只占 8 字节源端口和目的端口分别用两个十六进制数表示。整包长度 14 20 8 数据长度这个算式你心里要清楚。需要注意的是我把 IP 头的总长度字段直接写成了 0x2E也就是十进制 46。这个 46 怎么来的14 字节以太网头不算在内IP 头本身 20 UDP 头 8 消息文本 26 字节注意我用的字符串一共 26 个字符合计 54不对这里用的是 IP 头里的“总长度”字段它只统计 IP 头之后的长度所以是 20 8 26 54我上面代码里写 0x2E 是十进制 46这个值明显不对。真正要填的是 20 8 26 54十六进制是 0x36。这里其实藏着源码包里最容易出现的“手工填长度填错”的坑我在第 5 章会专门说。4.3 验证效果用抓包工具证明你的包真的被发出去了跑完程序屏幕上如果显示“发送成功”是不是就完了还不够。你的包发没发出去最好用一个抓包工具亲眼确认。Wireshark 是网络学习最常用的工具打开它选择刚才程序打开的那张网卡然后在过滤器里输入udp.port 9999回车。接着重新运行你的发包程序Wireshark 里应该立刻跳出一个新的 UDP 包。点开它你会在左侧的协议树里看到三行结构Ethernet II、Internet Protocol Version 4、User Datagram Protocol。每一层都能展开你能看到你亲手填的那些字段——源 MAC、目的 IP、UDP 端口、数据内容。我第一次看到自己构造的包完整出现在抓包列表里时那种“原来网络包长这样”的感觉比看十遍教材都管用。如果抓不到别急第 5 章就是专门处理这些问题的。5. 新手最容易踩的 5 个坑从编译报错到抓不到包你可能会发现代码不长但真跑起来问题一个接一个。这很正常我第一次写 WinPcap 程序时同样被各种报错折磨了一下午。这一章我把最典型的坑集中列出来每个都按“现象 → 原因 → 解决”的顺序写你在哪一步卡住了直接对号入座。5.1 五个必须先避开的坑坑一编译时报 “undefined reference topcap_findalldevs”现象代码看起来没问题但链接阶段抛出一堆 “undefined reference”。原因编译器根本没链接上 WinPcap 的库。解决核对编译命令里有没有 -lwpcap -lpacket -lws2_32再用 -L 指定库文件的目录如果还不行确认你用的库是 MinGW 能识别的 .a 格式而不是 Visual Studio 的 .lib 格式。坑二一运行就报 “打不开网卡”现象pcap_open 返回 NULLerrbuf 里写着类似 “Error opening adapter” 的信息。原因最常见的是没有以管理员身份运行程序。WinPcap 要访问网卡驱动普通权限会被系统拦下来。解决右键命令行或 IDE选择“以管理员身份运行”再试。第二个可能原因是你选的网卡不对比如选了一张已经断开的虚拟网卡换一张再试。坑三程序显示发送成功但 Wireshark 里看不到包现象pcap_sendpacket 返回 0成功但抓包里什么都没有。原因发送成功了不代表包一定出现在你监听的那张网卡上。WinPcap 是直接从网卡发原始帧的如果你把包发去了一个不存在的 IP或者目的 MAC 是广播地址而对方机器不在同一局域网包可能被交换机丢弃或者你监听的网卡和发送网卡不是同一张。解决确认抓包网卡和发送网卡是同一张优先在局域网内找一台真实机器测试不要发送到公网 IP。坑四抓包工具能看到包但里面提示“长度不对”或校验和错误现象包确实抓到了但 Wireshark 标红提示 IP length 错误或 checksum 错误。原因十有八九是你手工填的 IP 头总长度字段和实际包长对不上。我上面代码里就故意演示了这个错误总长度应该算 IP 头 UDP 头 数据而不是把以太网头的 14 字节也算进去。解决每次发完包在 Wireshark 里点开 IP 层看它提示的“Total Length”应该是多少把你填的值改成一致就行。坑五想发到本机回环地址 127.0.0.1但一直失败现象目的 IP 填 127.0.0.1程序运行时没有任何报错但抓包软件一点动静都没有。原因WinPcap 默认不能捕获和发送回环接口的数据因为回环流量不经过物理网卡驱动。解决别用回环地址做验证老老实实找另一台局域网内的机器或者用你自己机器的另一个真实网卡地址作为目的 IP。5.2 如果还是不行按这个顺序排查遇到玄学问题不要慌我一般按下面五个步骤走能解决九成问题先用最简单的空程序编译一次确认环境没问题再谈业务代码。以管理员身份运行这是 WinPcap 最常见的权限门槛。程序里把 pcap_open 的 errbuf 打印出来看错误信息不要只盯着“发送失败”。用 Wireshark 抓包确认包到底是“没发出去”还是“发出去了但不对”。检查你填的长度字段和校验和这两处是手工发包出错的高发区。提示不要同时开两个抓包工具它们可能抢占网卡驱动导致你的程序打不开网卡。一次只开一个 Wireshark。6. 学完发包之后下一步该学什么到这里你不但跑通了第一个范例还亲眼在抓包里看到了自己构造的包长什么样。这个起点其实比很多人的学习进度都扎实。但别停在这里顺着这条线往下走会越学越通透。我的建议是这样安排下一步先学会抓自己的包。你现在会发了下一步要学会用 pcap_next_ex 或者 pcap_loop 把网卡上的数据读进来打印出十六进制这叫“回声训练”发出去的包和收回来的包对照着看你对协议的理解能上一个台阶。接着去学构造 ICMP 包也就是 ping 命令用的协议。ICMP 头部比 UDP 更简单只有类型、代码、校验和三段而且你发出一帧 ICMP echo request对方机器会自动回一帧 echo reply那种“我发的包有回应了”的成就感比发 UDP 强烈得多。等对原始帧熟悉了再去补两个知识盲区。第一是ARP 协议它会解释为什么你的目的 IP 需要对应一个目的 MAC第二是IP 校验和的完整算法把那段 checksum 函数每行都啃明白以后看任何协议头的校验都不怕。可以按这个顺序走阶段学习内容建议时间1用 Wireshark 分析自己发的 UDP 包1-2 天2抓包回放用 pcap_next_ex 读网卡数据3-5 天3手工构造 ICMP 包并收到回应1 周4学 ARP 与完整校验和算法1-2 周另外提一句WinPcap 这个库的后续版本叫 Npcap 或者 Win10Pcap接口基本兼容以后换新系统把库替换一下思路完全通用。从那以后我每次手工发包都强制自己先抓一包对照一遍长度和校验再收工这个习惯帮我少踩了无数暗坑。希望今天这篇也能帮你在网络编程这条路上少走一段弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询