
简介基于MFC与WinPcap开发的原创网络嗅探器源码工程面向网络编程入门者与安全分析人员提供界面友好的抓包工具参考实现。支持IPv4、IPv6、ARP、ICMP、TCP、UDP、HTTP等协议解析适用于学习数据包捕获、协议分析及WinPcap API调用。压缩包共22个文件以7个头文件、4个C源文件为主体包含VS工程文件sln/vcproj、资源文件rc/rc2/ico/png及配置说明整体大小1.92MB目录层级简洁适合直接编译与逐步调试。已有5391人浏览学习足见其作为MFCWinPcap范例的参考价值。通过该工程读者可以梳理网卡枚举、数据包循环抓取、协议头字段解析及界面实时刷新的完整实现思路也能在此框架上扩展其他协议或改进呈现效果是网络工具开发实践的良好起点。 做网络课设那会儿我给自己选了个硬骨头用MFC手写一个基于WinPcap的网络嗅探器sniffer。都说抓包是理解网络协议的捷径但真到动手的时候才发现从驱动部署到二进制解析每一步都能踩出新花样。这篇文章就把整个项目从选型、环境搭建、编码实现到问题排查的完整过程捋一遍重点讲WinPcap安装部署里那些莫名其妙的坑、抓包线程和界面线程的协作方式以及MFC列表控件展示协议信息的细节。如果你正在做课程设计、毕业设计或者单纯想搞懂数据包在网线上到底长什么样这篇应该能帮你少走不少弯路。1. 项目整体构思先想清楚要做什么1.1 需求拆解这个嗅探器要解决什么问题用一句话描述需求选择一个网卡点击开始程序就能把经过该网卡的数据包实时抓下来显示在表格里并且能解析出源IP、目的IP、协议类型、端口、数据长度这些关键信息。再进阶一点双击某一行能看到这个数据包的十六进制原始内容。把需求拆开看整个项目其实就三件事WinPcap负责跟网卡驱动打交道提供原始报文数据一个独立线程负责从WinPcap缓冲区里读包MFC界面负责把报文解析成人类能看懂的信息。三者各司其职哪怕界面卡了抓包线程也能继续工作数据不丢。这里必须先理解一个关键概念混杂模式。默认情况下网卡只把发给自己的帧或者广播帧交给操作系统别人的包直接就丢了。抓包工具要做的是让网卡进入混杂模式把链路上经过的每一帧都复制一份给程序。WinPcap的pcap_open_live函数里有个promisc参数干的就是这件事。这也是为什么嗅探器这种工具在权限要求上比较敏感做个学习项目在自己网络环境里用就好。1.2 方案选型为什么是MFCWinPcap这套组合我做这套方案是在VS2013环境下教材和参考代码几乎全是WinPcap的所以沿用经典组合。但放到现在可选方案其实不少列个表对比一下方案组合上手难度跨平台协议解析工作量适用场景MFC WinPcap中仅Windows需手写课程设计、原生界面、老工程维护Python Scapy低跨平台极少内置快速原型、数据分析C# SharpPcap中仅Windows需手写.NET桌面应用Qt libpcap中高跨平台需手写需要漂亮界面的跨平台工具当时选MFCWinPcap还有一个现实原因WinPcap/WpdPack开发包的文档极其齐全从设备枚举到抓包循环都有现成示例。虽然界面不算华丽但作为学习工具足够扎实。顺带一提现在新项目我更建议用Npcap它是WinPcap的继任者支持Windows 10/11还可以安装成WinPcap API兼容模式老代码不用改就能编译。不过很多老设备和软件比如某些EDA工具安装包会捆绑WinPcap还在用4.1.3这个经典版本所以这篇文章里的排查思路依然适用。1.3 整体架构抓包线程、解析、UI刷新三者的协作方式整个程序的运行流程是这样的对话框初始化时枚举网卡并填入下拉框用户点击开始按钮后用选中的网卡名打开设备然后启动一个抓包线程。抓包线程里循环调用pcap_next_ex函数每抓到一个包就做一次基础解析把关键字段打包好通过PostMessage通知界面线程更新列表。为什么不能把抓包循环直接写在按钮消息里因为pcap_next_ex在没有数据包时会一直阻塞直到设置的超时时间要是放在UI线程对话框会直接卡死窗口拖都拖不动。我的做法是抓包线程和界面线程严格分离抓包线程只负责取数据、通知界面线程在消息响应里做字符串格式化、插入列表控件。跨线程直接操作MFC控件是大忌后面在问题排查部分我会细聊。2. 环境准备WinPcap部署与工程配置2.1 安装WinPcap时的几个关键点先去官网下载WinPcap 4.1.3这是Windows平台最后的一个经典版本。安装过程很短但有几个点新手容易懵第一安装过程中它不会让你选择安装路径默认就是C:\Program Files\WinPcap网上有人到处找选择安装路径的选项其实是它根本没打算让你选放心安装就行。第二一定要右键以管理员身份运行安装程序否则NPF驱动服务写不进系统。第三装完可以去服务列表看一眼如果有个叫WinPcap Networkingnpf的服务状态是已启动或者自动说明驱动部分正常。如果你是在Windows 10/11上装这个老版本有一定概率装完仍然看不到NPF服务或者服务启动失败。这类问题我放在第4节详细说。另外提醒一句如果机器上已经装了Npcap再装WinPcap容易冲突最好先二选一。2.2 VS2013工程配置WpdPack开发包光有运行库还不够开发环境需要准备WpdPack开发包。下载解压后你会看到Include、Lib、docs等目录。然后在VS2013工程属性里做五处配置C/C - 常规 - 附加包含目录加上WpdPack的Include目录路径。链接器 - 常规 - 附加库目录加上WpdPack的Lib目录路径。链接器 - 输入 - 附加依赖项填入wpcap.lib和ws2_32.lib。后面这个ws2_32是Winsock库解析IP地址等功能会用到。C/C - 预处理器 - 预处理器定义加上HAVE_REMOTE。如果只用pcap_findalldevs而不用pcap_findalldevs_ex这步可以不配但建议加上。注意Lib目录下面还分x86和x64两个子目录工程是Win32平台就选x86那个x64就选x64那个混着用会链接报错这是很多人第一步就会被卡住的地方。2.3 枚举网卡并填入下拉框对话框初始化时第一件事是把本机所有网卡枚举出来显示在下拉框里。核心代码如下pcap_if_t* alldevs NULL; pcap_if_t* d NULL; char errbuf[PCAP_ERRBUF_SIZE] {0}; if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, alldevs, errbuf) -1) { AfxMessageBox(CString(_T(枚举网卡失败: )) CString(errbuf)); return; } int idx 0; for (d alldevs; d ! NULL; d d-next) { CString desc d-description ? d-description : d-name; m_combo.AddString(desc); idx; } pcap_freealldevs(alldevs);这里有个很容易被忽略的细节有些虚拟网卡或者回环接口description字段可能是空的直接AddString的话下拉框里全是空行。所以务必用三元表达式做一次兜底描述为空就显示name。另外枚举完之后要及时调用pcap_freealldevs释放内存这个链表是WinPcap动态创建的不释放就是内存泄漏。3. 核心实现抓包循环与协议解析3.1 抓包线程pcap_next_ex循环的正确姿势点击开始按钮后需要做三件事根据下拉框选中的索引找到对应的网卡名调用pcap_open_live打开设备最后启动抓包线程。代码结构类似这样pcap_t* fp pcap_open_live(deviceName, 65536, 1, 1000, errbuf); if (fp NULL) { AfxMessageBox(CString(_T(打开设备失败: )) CString(errbuf)); return; } m_fp fp; m_bSniffing TRUE; AfxBeginThread(SniffThreadProc, this);pcap_open_live的参数值得仔细说说。第一个参数是设备名必须是pcap_findalldevs_ex枚举出来的name字段不是description。第二个参数snaplen表示要抓取的最大字节数传65536表示整包都收。第三个参数promisc传1就是开启混杂模式。第四个参数timeout是超时时间单位是毫秒传1000表示pcap_next_ex在没有包时会阻塞最多1秒后返回0。线程函数的骨架如下UINT SniffThreadProc(LPVOID pParam) { CSnifferDlg* dlg (CSnifferDlg*)pParam; pcap_t* fp dlg-m_fp; struct pcap_pkthdr* header NULL; const u_char* pkt_data NULL; while (dlg-m_bSniffing) { int res pcap_next_ex(fp, header, pkt_data); if (res 1) { // 解析并通知UI关键数据拷贝出来 dlg-PostMessage(WM_PACKET_CAPTURED, 0, 0); } else if (res -1) { break; // 出错 } // res 0 表示超时继续循环检查退出标志 } return 0; }这里有个很重要的经验停止抓包时不要直接pcap_close句柄。因为抓包线程可能正阻塞在pcap_next_ex上别的线程把句柄一关程序大概率直接崩。我的做法是设置m_bSniffing为FALSE线程等超时返回后自己跳出循环之后在界面线程里再调用pcap_close。另一个方案是用pcap_breakloop来打断阻塞循环效果更及时但要注意它只能在另一个线程安全地调用。3.2 深度拆解从原始字节流里解析出协议字段抓到手的pkt_data实际上是一串原始字节对应着网线上真实的网络帧。解析的第一步就是按以太网头的格式来读。以太网帧头固定14字节6字节目的MAC、6字节源MAC、2字节协议类型。为了代码清晰我习惯用紧凑结构体直接强转#pragma pack(push,1) struct EthHeader { BYTE dstMac[6]; BYTE srcMac[6]; WORD etherType; }; struct IpHeader { BYTE verIhl; BYTE tos; WORD totalLen; WORD id; WORD fragOffset; BYTE ttl; BYTE protocol; WORD checksum; ULONG srcIp; ULONG dstIp; }; struct TcpHeader { WORD srcPort; WORD dstPort; ULONG seq; ULONG ack; BYTE offset; BYTE flags; WORD window; WORD checksum; WORD urgent; }; #pragma pack(pop)解析逻辑的关键点有三个。第一所有2字节和4字节的字段都要用ntohs、ntohl转换成主机字节序。网络字节序是大端而x86是小端不转换的话端口号、IP地址全是反的看着像是乱码。第二以太网类型字段要先转序再判断0x0800是IPv40x0806是ARP0x86DD是IPv6。如果解析到不是IPv4的包不能直接读后面的IP头否则会解析出一堆垃圾数据。第三IP头的长度不是固定20字节头部长度是verIhl字段的低4位乘以4比如0x45表示20字节。TCP/UDP头部长度同理需要有偏移量的概念。另外在界面里显示MAC地址时建议格式化成xx-xx-xx-xx-xx-xx的样式直接用%02X格式化每个字节就行。3.3 列表控件与自定义按钮把界面收拾得体面一点CListCtrl是这个项目的核心展示控件。初始化列表时一定要设置扩展样式否则默认的列表模式既没有网格线也不能整行选中体验很糟糕m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER); m_list.InsertColumn(0, _T(序号), LVCFMT_LEFT, 60); m_list.InsertColumn(1, _T(时间), LVCFMT_LEFT, 100); m_list.InsertColumn(2, _T(源地址), LVCFMT_LEFT, 150); m_list.InsertColumn(3, _T(目的地址), LVCFMT_LEFT, 150); m_list.InsertColumn(4, _T(协议), LVCFMT_LEFT, 80); m_list.InsertColumn(5, _T(长度), LVCFMT_LEFT, 80);关于自定义按钮这个点很多新手想在MFC里放一张图片当按钮。最省事的做法是用CBitmapButton准备三态图片正常、按下、悬停后调用AutoLoad绑定按钮ID。网上很多教程会让你从CButton派生一个类自己重绘其实简单项目里CBitmapButton完全够用。考虑到界面在不同分辨率下的表现如果对话框允许拉伸最好在OnSize里MoveWindow调整列表控件的位置和大小要省事的话直接把对话框固定大小抓包工具的关键是功能和稳定性界面朴素一点无妨。4. 常见问题与排查实录4.1 local interfaces are unavailable最常见的WinPcap启动报错在Windows 10/11上跑老程序最容易遇到的就是设备列表枚举失败提示local interfaces are unavailable because winpcap is installed but is no longer...。翻译成大白话就是WinPcap装了但驱动没真正在跑。原因一般有三个NPF服务没启动、系统装了Npcap但没开WinPcap兼容模式、老版本驱动在64位系统上加载失败。排查顺序建议这样先打开管理员命令行执行sc query npf看看服务状态。如果没启动执行sc config npf start demand再net start npf。要是直接提示服务不存在就卸载重装一次WinPcap重装时记得右键管理员身份。如果你主要是做新项目直接装Npcap并勾选Install in WinPcap API-compatible Mode这个问题就与你无关了。4.2 NPF服务启动失败错误码1066和2的解决方法NPF服务启动失败一般报错错误2系统找不到指定的文件或者错误1066服务已返回特定服务错误。错误2通常是驱动文件真的不存在检查C:\Windows\System32\drivers目录下有没有npf.sys这个文件没有就重装。错误1066则复杂一些常见原因是安装了Npcap之后又装了WinPcap两个驱动互相打架或者杀毒软件把npf.sys给隔离了。我的处理办法是先用管理员的sc stop npf和sc delete npf把旧服务清掉卸载干净相关软件重启一次然后再重新安装目标抓包运行库。这样一套组合拳下来绝大多数NPF相关的问题都能解决。4.3 界面卡死与列表控件不刷新线程问题排查如果抓包时发现界面假死或者列表不刷新十有八九是线程职责没划清楚。我有一次图省事直接在抓包线程里调用了CListCtrl的InsertItem结果界面秒崩。原因是MFC控件不是线程安全的必须在创建它的UI线程里操作。正确做法是抓包线程构造一个包含关键信息的数据结构通过PostMessage发给主窗口UI线程在消息处理函数里再做插入操作。PostMessage和SendMessage的区别也要注意PostMessage是异步的发完就返回SendMessage会一直等UI线程处理完才返回。在数据量大的场景用SendMessageUI响应会非常慢帖子直接变成未响应状态。4.4 发布打包给别人的机器也能跑项目做完要打包发给别人的话会碰到两件事一是MFC运行库二是WinPcap运行库。MFC默认是动态链接目标机器没有对应DLL会提示缺少mfc120u.dll之类。我的习惯是项目属性里把使用MFC改成在静态库中使用MFC这样exe自带MFC运行库体积大一点但省心。但wpcap.dll这个文件还是要跟着走或者干脆在目标机器上安装WinPcap/Npcap运行库。更省事的方案是把WpdPack运行库和安装包一起打包写一个安装脚本自动处理。如果你是做课程设计答辩记得提前在答辩电脑上测试一遍不然现场报错很狼狈。5. 一点项目之外的心得做完这个嗅探器最大的收获不是会调了几个WinPcap API而是能把课本上背的协议头字段和真实数据包一一对上了。以前看TCP三次握手的时序图总觉得是理论上的东西直到自己抓包看到SYN、SYNACK、ACK三个包的序列号和标志位才真正理解纸上得来终觉浅这句话。最后分享一个实操小技巧测试时不要一上来就在大网络环境下抓包包量一大列表瞬间刷出成千上万行眼睛看花不说程序性能也会跟吃紧。我在后期调试时都是在自己可控的小网络里或者加一个BPF过滤条件比如pcap_compile设定只抓TCP端口443的包这样既能看到目标协议的完整交互又能避免噪音干扰。本文还有配套的精品资源点击获取