基于npcap与Qt的C++抓包工具:从编译到二次开发实战
发布时间:2026/9/28 12:00:53来源:尧图网络
简介这是一份基于npcap与Qt开发的网络抓包工具源码模仿Wireshark的核心功能面向具备一定网络编程与C基础的开发者用于学习数据包捕获、协议解析与图形界面集成。资源包共86个文件以cpp与h源码、obj与tlog编译中间文件、vcxproj工程配置、qrc资源脚本及ui界面文件为主另含sln解决方案与可执行程序压缩包约2.48MB结构完整可直接导入Visual Studio继续开发。目前已有378人学习下载。读者可从中掌握npcap抓包接口调用、Qt信号与槽驱动界面刷新、数据包过滤与展示的完整实现思路并参考工程组织方式将抓包能力嵌入自有系统适合作为网络监控与协议分析类项目的实践参考。1. 拿到 sniffer.zip 之后这套 npcapQt 抓包工具到底能干什么如果你手头正好有一个sniffer.zip解压后看到sniffer.sln、sniffer目录和x64文件夹那基本可以确定这是一套 Windows 平台下用 C 写的抓包工程底层依赖 npcap界面层用 Qt。它的定位很明确不是让你替代 Wireshark 做日常排障而是给你一个能自己改、能嵌进别的系统里的抓包骨架。Wireshark 强在协议解析和可视化但它的插件体系和二次开发门槛不低这套 sniffer 的价值在于源码可控你可以把抓包、过滤、展示这条链路拆开换成自己的解析逻辑或上报通道。适合谁需要把抓包能力集成进自有监控工具的后端开发、想研究 npcap 回调机制和 Qt 模型视图配合的 C 工程师以及被 Wireshark 界面限制住、想自己控制展示字段的从业者。不适合只想点开就抓包、完全不碰代码的人。下面按“先跑起来、再看懂结构、最后改得动”的顺序拆。2. 把工程跑起来npcap 环境、Qt 版本与 sln 编译链路2.1 先确认 npcap 装对了别急着开 Qt Creatornpcap 是这套工具的命根子它负责从网卡驱动层把数据包捞上来。很多人编译能过、一运行就报failed to create the npcap service: 0x800f0203本质是 npcap 驱动没装好或版本不匹配。常见做法是去 npcap 官方渠道下载安装包安装时勾选 “Install Npcap in WinPcap API-compatible Mode”这样老代码里pcap_open_live这类 WinPcap 风格调用也能用。装完先别开工程用命令行验证一下# 查看 npcap 是否识别到网卡以及驱动版本 npcap_check.bat # 或者直接用 Wireshark 自带的 dumpcap 列接口能列出来说明 npcap 层通了 dumpcap -Ddumpcap -D会输出类似\Device\NPF_{GUID}的接口列表。如果这里报错Qt 工程里再怎么调pcap_findalldevs都是白搭。注意npcap 安装包和 SDK 是两回事编译需要的是 SDK 里的pcap.h和wpcap.lib运行需要的是驱动服务。两者版本尽量对齐别用 1.60 的 SDK 配 0.99 的驱动。2.2 Qt 版本选择别用太新的5.15.2 是甜点工程里如果带.pro或.vcxproj先看它写的 Qt 模块。这套 sniffer 常见依赖是core、gui、widgets、network不涉及 QML 和 WebEngine。热搜里那个unknown module(s) in qt: webenginewidgets就是典型的模块没装或版本不对跟本工程无关别被带偏。我一般会固定用 Qt 5.15.2 MSVC 2019 的组合原因是npcap SDK 的库是 MSVC 编译的MinGW 链接wpcap.lib容易出符号不兼容Qt 6 对部分老式QRegExp和QString::SplitBehavior有改动老工程直接迁会报一堆弃用错误。安装时勾选 MSVC 2019 64-bit 套件即可。# 配置时显式指定 npcap SDK 路径避免找不到 pcap.h qmake sniffer.pro INCLUDEPATHC:/npcap-sdk/Include LIBS-LC:/npcap-sdk/Lib/x64 -lwpcap -lPacketINCLUDEPATH指向 SDK 的 Include 目录LIBS里-lwpcap是抓包主库-lPacket是 npcap 特有的发包库只抓包的话后者可以不加。路径里的反斜杠在 qmake 里建议写成正斜杠否则容易被当转义符吃掉。2.3 用 sniffer.sln 还是 qmake两条编译路线的取舍解压后看到sniffer.sln说明作者可能用 Visual Studio 直接管理工程。这时候你有两个选择路线适用场景注意点直接开 sniffer.sln已装 VS Qt VS Tools需在 VS 里配置 Qt 版本和 npcap 库目录用 qmake 重新生成只有 Qt Creator需要自己补 .pro 里的库路径如果走 sln先在 VS 的 “Qt VS Tools → Qt Versions” 里加好 5.15.2 的路径再右键项目属性在 “链接器 → 输入” 里确认wpcap.lib在列。走 qmake 的话把源码目录里的.cpp/.h全部纳入重点确认pcap.h能被找到。两条路最后产出的 exe 行为一致区别只是谁管依赖。编译通过后第一次运行如果界面能弹出但网卡下拉框是空的回到 2.1 检查 npcap 服务如果直接闪退用 Debug 模式跑看是不是pcap_findalldevs返回了空指针却没判空。3. 读懂抓包主链路从 pcap_open_live 到 Qt 表格刷新3.1 npcap 回调线程与 Qt 主线程的边界抓包的核心是一个死循环pcap_loop或pcap_next_ex不断从网卡取包每来一个包触发一次回调。这个回调运行在 npcap 自己的线程里而 Qt 的界面控件只能在主线程更新。直接在工作线程里ui-tableWidget-setItem(...)是经典翻车点表现是界面随机卡死或崩溃。常见做法是回调里只做两件事把pcap_pkthdr和包数据拷贝出来通过信号槽抛给主线程。下面是一个可抄的结构// CaptureWorker 运行在独立 QThread 中 void CaptureWorker::startCapture(const QString devName) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle pcap_open_live(devName.toLocal8Bit().data(), 65535, // snaplen抓全包别截断 1, // promisc混杂模式 1000, // read timeout毫秒 errbuf); if (!handle) { emit errorOccurred(QString(open failed: %1).arg(errbuf)); return; } pcap_loop(handle, 0, CaptureWorker::packetHandler, reinterpret_castu_char*(this)); } // 静态回调只负责转发不碰 UI void CaptureWorker::packetHandler(u_char *user, const pcap_pkthdr *hdr, const u_char *data) { auto *self reinterpret_castCaptureWorker*(user); QByteArray payload(reinterpret_castconst char*(data), hdr-caplen); emit self-packetCaptured(hdr-ts, payload); // 跨线程信号 }pcap_open_live的第二个参数snaplen设 65535 是为了不截断大包设小了后面解析 TCP 载荷会缺字节。第三个参数promisc1让网卡进混杂模式能抓到非本机地址的帧交换机镜像口场景必须开。第四个read timeout影响回调触发频率1000ms 偏保守实时性要求高可以降到 100。信号packetCaptured在连接时要用Qt::QueuedConnectionQt 会自动把参数拷贝到主线程队列。3.2 协议解析从以太头到 IP/TCP 的偏移计算抓到原始帧后要模仿 Wireshark 的列表展示至少得解出源 MAC、目的 MAC、源 IP、目的 IP、协议类型。以太网头固定 14 字节其中前 12 字节是两个 MAC第 13-14 字节是以太类型。类型为0x0800才是 IPv40x0806是 ARP0x86DD是 IPv6。struct EthHeader { uint8_t dst[6]; uint8_t src[6]; uint16_t type; }; struct Ipv4Header { uint8_t ver_ihl; // 高4位版本低4位首部长度 uint8_t tos; uint16_t total_len; uint16_t id, frag; uint8_t ttl, proto; uint16_t checksum; uint8_t src[4], dst[4]; }; QString parsePacket(const QByteArray frame) { if (frame.size() 14) return truncated; const auto *eth reinterpret_castconst EthHeader*(frame.constData()); uint16_t ethType ntohs(eth-type); if (ethType ! 0x0800) return QString(non-IPv4 type0x%1).arg(ethType, 4, 16); const auto *ip reinterpret_castconst Ipv4Header*(frame.constData() 14); int ihl (ip-ver_ihl 0x0F) * 4; // 首部长度以4字节为单位 QString src QString(%1.%2.%3.%4).arg(ip-src[0]).arg(ip-src[1]) .arg(ip-src[2]).arg(ip-src[3]); // 传输层从 14 ihl 开始proto6 是 TCP17 是 UDP return QString(%1 - %2 proto%3).arg(src).arg(...).arg(ip-proto); }ver_ihl低 4 位乘 4 才是 IP 首部真实字节数因为 IP 选项会让首部超过 20 字节写死 20 是常见错误。ntohs处理网络字节序Windows 下要#include winsock2.h且必须在windows.h之前包含否则会报重定义。解析函数只做偏移和取值别在里面做字符串拼接以外的重活否则高流量下表格刷新会拖垮抓包线程。3.3 过滤规则BPF 表达式在 pcap_compile 里怎么用Wireshark 的过滤框背后就是 BPF。npcap 提供pcap_compile把表达式编译成内核过滤指令再pcap_setfilter挂上去。这样不匹配的包在驱动层就被丢掉不用传到用户态性能差别很大。struct bpf_program fp; // 只抓 80 端口且目标是 192.168.1.10 的 TCP 包 const char *filter tcp port 80 and dst host 192.168.1.10; if (pcap_compile(handle, fp, filter, 0, PCAP_NETMASK_UNKNOWN) -1) { emit errorOccurred(QString(compile filter failed: %1) .arg(pcap_geterr(handle))); return; } if (pcap_setfilter(handle, fp) -1) { emit errorOccurred(set filter failed); return; } pcap_freecode(fp); // 编译结果挂上去后即可释放pcap_compile第四个参数optimize1会做表达式优化建议开。第五个netmask在抓本机网段广播时有用不确定就传PCAP_NETMASK_UNKNOWN。pcap_freecode必须在setfilter之后调用否则内存泄漏。注意过滤器语法和 Wireshark 显示过滤器不是一套ip.addr x这种是显示过滤BPF 里要写host x。4. 避坑与排查编译、运行、抓包三类高频问题4.1 编译期报 cannot find -lwpcap 或找不到 pcap.h现象链接阶段报cannot find -lwpcap或编译报pcap.h: No such file。 原因npcap SDK 没装或 qmake/VS 里的库路径写错32/64 位混用。 解决确认 SDK 解压目录下有Include/pcap.h和Lib/x64/wpcap.libqmake 里LIBS的-L路径指向Lib/x64工程套件也必须是 64 位。32 位工程配 64 位库必然链接失败。4.2 运行报 failed to create the npcap service: 0x800f0203现象程序启动或点“开始抓包”时弹这个错误码。 原因npcap 驱动服务没注册或安装时没勾 WinPcap 兼容模式或被杀软拦了驱动加载。 解决重装 npcap勾选兼容模式用管理员权限运行一次确认系统服务里npcap处于运行状态。这个错误码和代码无关别去改源码。4.3 界面卡死或抓一会儿就闪退现象抓包开始后界面无响应或几分钟后进程消失。 原因在 npcap 回调线程里直接操作了 Qt 控件或包数据没做深拷贝回调返回后指针失效。 解决回调里只emit信号槽函数在主线程更新 UIQByteArray构造时带上长度做深拷贝别只存裸指针。高流量下还要限制表格行数比如只保留最近 1000 条否则内存和刷新都扛不住。4.4 抓不到任何包但 Wireshark 能抓现象接口列表正常开始抓包后一条不显示。 原因选了错误的网卡过滤器写太严或没开混杂模式导致只看到本机流量。 解决先用空过滤器抓确认链路通再逐步加 BPF 条件。虚拟机场景注意选对虚拟网卡抓物理网卡是看不到虚拟机内部流量的。4.5 Qt 版本混用导致 fatal: cannot mix incompatible Qt library现象运行时报cannot mix incompatible Qt library (version 0x...)。 原因exe 旁边混入了不同版本的 Qt DLL或系统 PATH 里有另一套 Qt。 解决用windeployqt把依赖收拢到 exe 同目录运行前清掉 PATH 里的其他 Qt 路径。发布时别只拷 exeplatforms/qwindows.dll必须一起带。5. 二次开发与验证把 sniffer 改成你自己的抓包模块5.1 加一个自定义字段列从解析到表格映射想模仿 Wireshark 的 “Info” 列把 TCP 标志位或 HTTP 方法显示出来改动点集中在三处解析函数多返回一个字段、信号多带一个参数、表格多插一列。下面以提取 TCP 目的端口为例// 在 parsePacket 里proto6 时继续往下解 TCP 头 const uint8_t *tcp reinterpret_castconst uint8_t*(frame.constData()) 14 ihl; uint16_t dstPort (tcp[2] 8) | tcp[3]; // TCP 头第3-4字节是目的端口 // 信号改为携带结构体 struct PacketInfo { QString src, dst; quint16 dport; QString proto; }; emit packetCaptured(info);表格初始化时setColumnCount(5)在槽函数里setItem(row, 4, new QTableWidgetItem(QString::number(info.dport)))。注意setItem每次 new 一个 item旧 item 由表格接管释放但行数上限要自己控制超出时removeRow(0)。5.2 验证抓包正确性和 Wireshark 对拍改完解析逻辑别凭感觉说“对了”。我一般会做一次对拍同一张网卡、同一时间段sniffer 和 Wireshark 同时抓然后比对包数量和关键字段。步骤是在 sniffer 里清空过滤开始抓包同时 Wireshark 选同一网卡开始。制造固定流量比如ping -n 20 8.8.8.8或访问一个固定 HTTP 地址。停止后看两边 ICMP/TCP 包数量是否一致源目 IP、端口是否对得上。若 sniffer 少包检查 snaplen 是否截断、过滤器是否误伤、回调是否丢包。对拍能快速定位是解析错还是抓取漏。曾经有一次我这边 TCP 端口全是 0对拍后发现是ihl没乘 4偏移算错血泪经验。5.3 发布与依赖收拢别让用户装 Qt这套工具最终要给别人用不能要求对方装 Qt。用windeployqt自动收依赖windeployqt --release --no-translations sniffer.exe执行后 exe 同目录会出现Qt5Core.dll、platforms/qwindows.dll等。npcap 的wpcap.dll和Packet.dll如果没在系统目录也要一起拷。最后在一台没装 Qt、只装了 npcap 的干净机器上跑一遍确认能启动、能列网卡、能抓包才算真正交付。从那以后我每次改完抓包逻辑都强制走一遍“Wireshark 对拍 干净机启动”这两步不再靠单机自测下结论。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网