windows下使用winpcap解析网络数据包

21 篇文章 6 订阅
9 篇文章 0 订阅

windows下库文件,可以读取windows网卡中数据包,并进行解析。

操作如下:

vs2019添加winpcap库

1、https://www.winpcap.org/devel.htm下载winpcap SDK包

2、winpcap SDK包解压出来之后,有lib跟include目录

3、将include、lib添加到vs2019项目环境下

4、vs项目中增加头文件与lib库路径

 

 

5、上代码

 

#define WIN32
#define HAVE_REMOTE
#include "pcap.h"
#include<winsock2.h>
#pragma comment(lib,"wpcap.lib")   
#pragma comment(lib,"packet.lib")   
#pragma comment(lib,"ws2_32.lib")   

/* 4字节的IP地址 */
typedef struct ip_address {
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
}ip_address;

/* IPv4 首部 */
typedef struct ip_header {
	u_char  ver_ihl;        // 版本 (4 bits) + 首部长度 (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 首部 
typedef struct udp_header 
{
	u_short sport;          // 源端口(Source port)
	u_short dport;          // 目的端口(Destination port)
	u_short len;            // UDP数据包长度(Datagram length)
	u_short crc;            // 校验和(Checksum)
}udp_header;

//TCP首部
typedef struct tcp_header
{
	unsigned short src_port;    //源端口号   
	unsigned short dst_port;    //目的端口号   
	unsigned int seq_no;        //序列号   
	unsigned int ack_no;        //确认号   
#if LITTLE_ENDIAN   
	unsigned char reserved_1 : 4; //保留6位中的4位首部长度   
	unsigned char thl : 4;        //tcp头部长度   
	unsigned char flag : 6;       //6位标志   
	unsigned char reseverd_2 : 2; //保留6位中的2位   
#else   
	unsigned char thl : 4;        //tcp头部长度   
	unsigned char reserved_1 : 4; //保留6位中的4位首部长度   
	unsigned char reseverd_2 : 2; //保留6位中的2位   
	unsigned char flag : 6;       //6位标志    
#endif   
	unsigned short wnd_size;    //16位窗口大小   
	unsigned short chk_sum;     //16位TCP检验和   
	unsigned short urgt_p;      //16为紧急指针   
}tcp_hdr;


/* 回调函数原型 */
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";
	char packet_filter[] = "ip";
	struct bpf_program fcode;

	/* 获得设备列表 */
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{
		fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
		exit(1);
	}

	/* 打印列表 */
	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 WinPcap is installed.\n");
		return -1;
	}

	printf("Enter the interface number (1-%d):", i);
	scanf("%d", &inum);

	if (inum < 1 || inum > i)
	{
		printf("\nInterface number out of range.\n");
		/* 释放设备列表 */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* 跳转到已选设备 */
	for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

	/* 打开适配器 */
	if ((adhandle = pcap_open(d->name,  // 设备名
		65536,     // 要捕捉的数据包的部分 
				   // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
		PCAP_OPENFLAG_PROMISCUOUS,         // 混杂模式
		1000,      // 读取超时时间
		NULL,      // 远程机器验证
		errbuf     // 错误缓冲池
	)) == NULL)
	{
		fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n");
		/* 释放设备列表 */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* 检查数据链路层,为了简单,我们只考虑以太网 */
	if (pcap_datalink(adhandle) != DLT_EN10MB)
	{
		fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
		/* 释放设备列表 */
		pcap_freealldevs(alldevs);
		return -1;
	}

	if (d->addresses != NULL)
		/* 获得接口第一个地址的掩码 */
		netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
	else
		/* 如果接口没有地址,那么我们假设一个C类的掩码 */
		netmask = 0xffffff;


	//编译过滤器
	if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
	{
		fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
		/* 释放设备列表 */
		pcap_freealldevs(alldevs);
		return -1;
	}

	//设置过滤器
	if (pcap_setfilter(adhandle, &fcode) < 0)
	{
		fprintf(stderr, "\nError setting the filter.\n");
		/* 释放设备列表 */
		pcap_freealldevs(alldevs);
		return -1;
	}

	printf("\nlistening on %s...\n", d->name);

	/* 释放设备列表 */
	pcap_freealldevs(alldevs);

	/* 开始捕捉 */
	pcap_loop(adhandle, 0, packet_handler, NULL);

	return 0;
}

/* 回调函数,当收到每一个数据包时会被libpcap所调用 */
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;
	tcp_header* th;

	u_int ip_len;
	u_short sport, dport;
	time_t local_tv_sec;

	/* 将时间戳转换成可识别的格式 */
	local_tv_sec = header->ts.tv_sec;
	ltime = localtime(&local_tv_sec);
	strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

	/* 打印数据包的时间戳和长度 */
	printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

	/* 获得IP数据包头部的位置 */
	ih = (ip_header*)(pkt_data + 14); //以太网头部长度  14字节为mac头长度
	//printf("proto:%d\n", ih->proto);
	
	printf(" src : %d.%d.%d.%d, dst : %d.%d.%d.%d",
		ih->saddr.byte1,
		ih->saddr.byte2,
		ih->saddr.byte3,
		ih->saddr.byte4,
		ih->daddr.byte1,
		ih->daddr.byte2,
		ih->daddr.byte3,
		ih->daddr.byte4);

	if (IPPROTO_TCP == ih->proto)
	{
		ip_len = (ih->ver_ihl & 0xf) * 4;
		
		u_short tlen = ntohs(ih->tlen);
		printf("tlen:%d\n", tlen);
		th = (tcp_header*)((u_char*)ih + ip_len);

		sport = ntohs(th->src_port);
		dport = ntohs(th->dst_port);
		printf(" tcp sport:%d, dport:%d\n", sport, dport);
	}

	if (IPPROTO_UDP == ih->proto)
	{
		ip_len = (ih->ver_ihl & 0xf) * 4;
		uh = (udp_header*)((u_char*)ih + ip_len);

		sport = ntohs(uh->sport);
		dport = ntohs(uh->dport);
		printf(" udp sport:%d, dport:%d\n", sport, dport);
	}
}

 

6、执行结果:需要先手动选择网卡,然后会打印经过此网卡的tcp、udp数据包

 

 

int pcap_findalldevs_ex(char* source,  struct pcap_rmtauth *auth,  
pcap_if_t** alldevs,   char* errbuf );
功能:查询所有网络设备
返回值:0表示查找成功,-1表示查找失败
参数说明:
source:
指定是本地适配器或者远程适配器
本地适配器:'rpcap://'
远程适配器:'rpcap://host:port'
抓包文件。'file://c:/myfolder/'.

ip首部长度 ip_len = (ih->ver_ihl & 0xf) * 4;

 

参考:

winpcap技术文档:http://www.ferrisxu.com/WinPcap/html/index.html

IP首部长度:https://blog.csdn.net/fl_dream/article/details/78761713

 

 

  • 8
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以给你提供一些思路和代码示例。 首先,你需要安装 WinPcap 库,并且用 C/C++ 编写代码。WinPcap 库可以让你在 Windows 操作系统上捕获和分析网络数据包。 接下来,你需要创建一个 WinPcap 句柄并打开一个网络接口,例如: ``` pcap_t *handle; char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program fp; char filter_exp[] = "tcp"; bpf_u_int32 net; bpf_u_int32 mask; // 打开网络接口 handle = pcap_open_live("eth0", BUFSIZ, 1, 1000, errbuf); // 编译过滤器表达式 pcap_compile(handle, &fp, filter_exp, 0, net); // 设置过滤器 pcap_setfilter(handle, &fp); ``` 其中,"eth0" 是你要监听的网络接口名字,BUFSIZ 是缓冲区大小,1 是混杂模式,1000 是超时时间,"tcp" 是过滤器表达式,表示只捕获 TCP 数据包。 接下来,你需要定义一个回调函数,用于处理捕获到的数据包。例如: ``` void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) { struct ip_header *ih; struct tcp_header *th; u_int ip_len; // 解析 IP 头部 ih = (struct ip_header *)(pkt_data + 14); // 偏移 14 个字节,跳过以太网头部 ip_len = (ih->ver_ihl & 0xf) * 4; // 解析 TCP 头部 th = (struct tcp_header *)((u_char*)ih + ip_len); printf("Source: %s:%d\n", inet_ntoa(ih->saddr), ntohs(th->sport)); printf("Destination: %s:%d\n", inet_ntoa(ih->daddr), ntohs(th->dport)); printf("Flags: 0x%x\n", th->flags); } ``` 其中,ip_header 和 tcp_header 是自定义的结构体,用于解析 IP 和 TCP 头部。inet_ntoa 函数可以将一个 IP 地址转换为字符串格式。 最后,你需要调用 pcap_loop 函数开始捕获数据包,并且传入回调函数进行处理。例如: ``` pcap_loop(handle, 0, packet_handler, NULL); ``` 完整的代码示例可以参考以下链接: https://www.winpcap.org/docs/docs_412/html/group__wpcap__tut6.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值