Win10 下基于 Npcap 的抓包程序详解
1. 背景
wireshark 是最常用的是网络抓包程序 ,不过,wireshark 也有无法满足我的时候。试想下,我们发送的 UDP 数据包的负载(payload)是自定义内容,此时的 wireshark 只能看到 payload 的原始数据。虽然可以使用 wireshark dissastor 将协议可视化(参考:Wireshark C 插件开发之插件框架),但是工作量大且繁琐。那有没有一种相对方便的手段实现监控数据包并解析 payload 的功能呢?有!我们可以自己实现抓包程序。
2. wireshark 抓包原理
Document:https://wiki.wireshark.org/libpcap
wireshark 调用 libpcap 实现监控网络数据包的功能。Win10 环境下,libpcap 的功能由 Npcap 提供。
3. Npcap 简介
Document:https://npcap.com/guide/index.html
Npcap 是专为 Windows 开发的一款网络抓包 SDK,该 SDK 提供了被应用程序调用的库文件和系统驱动程序。通过 Npcap,我们可以得到原始(raw)网络数据,即未经过 TCP/IP 协议栈的数据,也就是网卡收到的数据。同时呢,我们也可以通过 Npcap 设置接收过滤器,这样收到的数据就是我们感兴趣的数据,比如某个端口的数据。而且,Npcap 还提供发送原始(raw)网络数据的功能。
4. Npcap 原理
Document:https://npcap.com/guide/npcap-internals.html#npcap-internals-driver
Npcap 实现了 Win10 驱动程序,叫做 NPF(Netgroup Packet Filter),该驱动从 Win10 miniport 驱动获取网卡数据实现监控网络数据包的功能(Win10 使用 miniport 驱动控制网卡)。

5. Npcap 使用
Npcap SDK 使用起来很简单,一共分为三步。
5.1 安装 visual studio
我使用的是 visual studio 2019。
5.2 安装 npcap 到 win10
安装 Npcap 1.71 installer 到 win10 系统中,主要是安装了 NPF 驱动和 dll 文件(Packet.dll 和 Wpcap.dll)。
下载地址:https://npcap.com/#download
5.3 下载 Npcap SDK
Npcap SDK 中提供了 lib 和头文件,我们编写抓包程序时需要用到这些。
5.4 例程
这里,我以 Npcap SDK 中的 npcap-sdk-1.13\Examples-pcap\UDPdump 为例进行说明,UDPdump 用于监控收到的 UDP 数据包。
5.4.1 udpdump.c
在原有文件的基础上我添加了 #pragma comment(lib,"ws2_32.lib")
语句,否则 ntohs() 会导致编译失败。
/*
* Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
* Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Politecnico di Torino, CACE Technologies
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifdef _MSC_VER
/*
* we do not want the warnings about the old deprecated and unsecure CRT functions
* since these examples can be compiled under *nix as well
*/
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <pcap.h>
#include <time.h>
#include <winsock.h>
#pragma comment(lib,"ws2_32.lib")
#ifdef _WIN32
#include <tchar.h>
BOOL LoadNpcapDlls()
{
_TCHAR npcap_dir[512];
UINT len;
len = GetSystemDirectory(npcap_dir, 480);
if (!len) {
fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
return FALSE;
}
_tcscat_s(npcap_dir, 512, _T("\\Npcap"));
if (SetDllDirectory(npcap_dir) == 0) {
fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
return FALSE;
}
return TRUE;
}
#endif
/* 4 bytes IP address */
typedef struct ip_address
{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address;
/* IPv4 header */
typedef struct ip_header
{
u_char ver_ihl; // Version (4 bits) + Internet header length (4 bits)
u_char tos; // Type of service
u_short tlen; // Total length
u_short identification; // Identification
u_short flags_fo; // Flags (3 bits) + Fragment offset (13 bits)
u_char ttl; // Time to live
u_char proto; // Protocol
u_short crc; // Header checksum
ip_address saddr; // Source address
ip_address daddr; // Destination address
u_int op_pad; // Option + Padding
}ip_header;
/* UDP header*/
typedef struct udp_header
{
u_short sport; // Source port
u_short dport; // Destination port
u_short len; // Datagram length
u_short crc; // Checksum
}udp_header;
/* prototype of the packet handler */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);
int main()
{
pcap_if_t* alldevs;
pcap_if_t* d;
int inum;
int i = 0;
pcap_t* adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
char packet_filter[] = "ip and udp";
struct bpf_program fcode;
#ifdef _WIN32
/* Load Npcap and its functions. */
if (!LoadNpcapDlls())
{
fprintf(stderr, "Couldn't load Npcap\n");
exit(1);
}
#endif
/* Retrieve the device list */
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
/* Print the list */
for (d = alldevs; d; d = d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}
if (i == 0)
{
printf("\nNo interfaces found! Make sure Npcap is installed.\n");
return -1;
}
printf("Enter the interface number (1-%d):", i);
scanf("%d", &inum);
/* Check if the user specified a valid adapter */
if (inum < 1 || inum > i)
{
printf("\nAdapter number out of range.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/* Jump to the selected adapter */
for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);
/* Open the adapter */
if ((adhandle = pcap_open_live(d->name, // name of the device
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
1, // promiscuous mode (nonzero means promiscuous)
1000, // read timeout
errbuf // error buffer
)) == NULL)
{
fprintf(stderr, "\nUnable to open the adapter: %s\n", errbuf);
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/* Check the link layer. We support only Ethernet for simplicity. */
if (pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
if (d->addresses != NULL)
/* Retrieve the mask of the first address of the interface */
netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
else
/* If the interface is without addresses we suppose to be in a C class network */
netmask = 0xffffff;
//compile the filter
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
{
fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
//set the filter
if (pcap_setfilter(adhandle, &fcode) < 0)
{
fprintf(stderr, "\nError setting the filter.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
printf("\nlistening on %s...\n", d->description);
/* At this point, we don't need any more the device list. Free it */
pcap_freealldevs(alldevs);
/* start the capture */
pcap_loop(adhandle, 0, packet_handler, NULL);
return 0;
}
/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data)
{
struct tm* ltime;
char timestr[16];
ip_header* ih;
udp_header* uh;
u_int ip_len;
u_short sport, dport;
time_t local_tv_sec;
/*
* unused parameter
*/
(VOID)(param);
/* convert the timestamp to readable format */
local_tv_sec = header->ts.tv_sec;
ltime = localtime(&local_tv_sec);
strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);
/* print timestamp and length of the packet */
printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);
/* retireve the position of the ip header */
ih = (ip_header*)(pkt_data +
14); //length of ethernet header
/* retireve the position of the udp header */
ip_len = (ih->ver_ihl & 0xf) * 4;
uh = (udp_header*)((u_char*)ih + ip_len);
/* convert from network byte order to host byte order */
sport = ntohs(uh->sport);
dport = ntohs(uh->dport);
/* print ip addresses and udp ports */
printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
sport,
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4,
dport);
}
5.4.2 visual studio 2019 工程设置
在 visual studio 工程中需要指定 Npcap SDK 中头文件和库文件的目录,以及需要链接的库文件。
5.4.2.1 指定头文件位置

5.4.2.2 指定库文件(lib)位置

5.4.2.2 指定链接的库

注意图中的“延迟加载的 DLL wpcap.dll”。如果系统中同时存在 WinPcap 和 Npcap 的情况,设置该选项,同时在代码中使用 SetDllDirectory() 设置 Npcap 的 dll 路径后(C:\Windows\System32\Npcap\),程序执行时会优先使用 Npcap 提供的 dll 文件。
5.5 执行
比如监控无线网卡收到的数据。

6. 总结
Npcap SDK 提供了多个例程,不只有在线监控,也可以读取离线文件。更多例程请查看 SDK 中的 Examples-pcap/ 目录。
转:https://zhuanlan.zhihu.com/p/573366847
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律