winpcap抓取IP、ARP包并解析

问题记录

  1. 无法打开包括文件: “sys/time.h”
    “pcap.h”内有:
#ifndef lib_pcap_pcap_h
#define lib_pcap_pcap_h

#if defined(WIN32)
  #include 
#elif defined(MSDOS)
  #include 
  #include   /* u_int, u_char etc. */
#else /* UN*X */
  #include 
  #include 
#endif /* WIN32/MSDOS/UN*X */

pcap支持WIM32、MSDOS、UNIX,所以在首部添加#define WIN32即可。

  1. 包过滤器的设置
    过滤串表达式语法。

  2. ntohs()
    没有分析哪个字段需要由网络字节顺序转换为主机字节顺序,直接按照输出加上了ntohs,具体分析日后再写。

源码

#define WIN32
#include "pcap.h"
#include
#include

#define MAX_PRINT 80
#define MAX_LINE 16

#define LINE_LEN 16

struct ether_header//以太网首部
{
	u_int8_t ether_dhost[6]; //目的Mac地址   
	u_int8_t ether_shost[6]; //源Mac地址   
	u_int16_t ether_type;    //协议类型   
};

/* IP报文包数据结构 */
struct ip_header{
#if defined(WORDS_BIENDIAN)   //考虑大端序与小端序
	u_int8_t   ip_version : 4,
		ip_header_length : 4;
#else   
	u_int8_t   ip_header_length : 4,//首部长度
		ip_version : 4;//版本
#endif   
	u_int8_t    ip_tos;//服务类型
	u_int16_t   ip_length;//总长度
	u_int16_t   ip_id;//标识
	u_int16_t   ip_off;//片偏移
	u_int8_t    ip_ttl;//生存时间
	u_int8_t    ip_protocol;//协议
	u_int16_t   ip_checksum;//首部检验和
	struct in_addr ip_souce_address;//源地址
	struct in_addr ip_destination_address;//目的地址
};

/* ARP报文包数据结构 */
struct ether_arp{
	u_int16_t ar_hrd;//硬件类型
	u_int16_t ar_pro;//协议类型
	u_int8_t ar_hln;//硬件地址长度(6)
	u_int8_t ar_pln;//协议地址长度(4)
	u_int16_t ar_op;//操作类型
	u_int8_t arp_sha[6];//发送者硬件地址
	struct in_addr arp_spa;//发送者IP地址
	u_int8_t arp_tha[6];//目标硬件地址
	struct in_addr arp_tpa;//目标IP地址
};

void ip_protool_packet(struct pcap_pkthdr *header, const u_char *pkt_data, FILE *fp);
void arp_protool_packet(struct pcap_pkthdr *header, const u_char *pkt_data, FILE *fp);

int ip_counter, icmp_counter, arp_counter;

int main(int argc, char **argv)
{
	time_t start, now;//计时
	long float time_sum;
	pcap_if_t *alldevs, *d;
	pcap_t *fp;
	u_int inum, i = 0;
	struct bpf_program fcode;
	u_int netmask;
	char errbuf[PCAP_ERRBUF_SIZE];
	int res;
	struct pcap_pkthdr *header;//抓到的包的一般信息
	const u_char *pkt_data; //抓到的包的数据
	u_short ethernet_type;//以太网类型 
	struct ether_header *ethernet_protocol;
	FILE *stream;//文件流

	ip_counter = icmp_counter = arp_counter = 0;

	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)//查找网卡
	{
		fprintf(stderr, "Error in pcap_findalldevs_ex: %s\n", errbuf);
		system("pause");
		return -1;
	}

	for (d = alldevs; d; d = d->next)
	{
		printf("%d. %s\n    ", ++i, d->name);

		if (d->description)
			printf(" (%s)\n", d->description);
		else
			printf(" (No description available)\n");
	}

	if (i == 0)
	{
		fprintf(stderr, "No interfaces found! Exiting.\n");
		system("pause");
		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);
		system("pause");
		return -1;
	}

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

	/* 打开设备 */
	if ((fp = pcap_open(d->name,
		100 /*snaplen*/,
		PCAP_OPENFLAG_PROMISCUOUS /*flags*/,
		20 /*read timeout*/,
		NULL /* remote authentication */,
		errbuf)
		) == NULL)
	{
		fprintf(stderr, "\nError opening adapter\n");
		system("pause");
		return -1;
	}

	/* 获取掩码 */
	if (d->addresses != NULL)
		netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
	else
		netmask = 0xffffff;	/* 255.255.255.0 */

	//编译过滤串表达式,语法参见:http://www.tcpdump.org/manpages/pcap-filter.7.html
	if (pcap_compile(fp, &fcode, "ip or arp or icmp", 1, netmask) < 0)
	{
		fprintf(stderr, "\nError compiling filter: wrong syntax.\n");
		system("pause");
		return -1;
	}

	//绑定过滤器
	if (pcap_setfilter(fp, &fcode)<0)
	{
		fprintf(stderr, "\nError setting the filter\n");
		system("pause");
		return -1;
	}
	

	/* 以只读形式打开输出文件 */
	if ((stream = fopen("capture.txt", "w")) == NULL) {
		printf("The file 'capture.txt' was not opened\n");
		system("pause");
		return -1;
	}

	start = time(NULL);
	int counter = 0;
	/* 抓包 */
	while ((res = pcap_next_ex(fp, &header, &pkt_data)) >= 0)
	{
		now = time(NULL);
		if ((time_sum = difftime(now, start)) > 60)//计时一分钟
			break;;

		if (kbhit()) {//键盘监听,回车键退出,两个getch()的问题(vs平台)参见:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getch-getwch?view=vs-2017
			char ch = getch();
			getch();
			if (ch == '\n' || ch == '\r')
				break;
		}

		if (res == 0)
			/* 超时,获取的包无效 */
			continue;

		printf("历时%2.0lf秒\n", time_sum);

		ethernet_protocol = (struct ether_header*)pkt_data;//获得数据包内容
		ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网类型 
		switch (ethernet_type){
			case 0x0800:
				++ip_counter;
				printf("No.%-4d上层协议:IP协议\n", counter++);
				fprintf(stream, "No.%-4d上层协议:IP协议\n", counter);
				ip_protool_packet(header, pkt_data, stream);
				break;
			case 0x0806:
				++arp_counter;
				printf("No.%-4d上层协议:ARP协议\n", counter++);
				fprintf(stream, "No.%-4d上层协议:ARP协议\n", counter);
				arp_protool_packet(header, pkt_data, stream);
				break;
			case 0x8035:
				printf("No.%-4d上层协议:RARP协议\n", counter++);
				fprintf(stream, "No.%-4d上层协议:RARP协议\n", counter);
				for (i = 1; (i < header->caplen + 1); i++)
				{
					fprintf(stream, "%.2x ", pkt_data[i - 1]);
					if ((i % LINE_LEN) == 0) 
						fprintf(stream, "\n");
				}
				fprintf(stream, "\n\n");
				break;
			default:
				break;
		}

		printf("\n\n");

	}

	fprintf(stream, "计时:%.0lf秒\nIP:%d、ICMP:%d、ARP:%d\n", time_sum,ip_counter, icmp_counter, arp_counter);
	fclose(stream);

	if (res == -1)
	{
		fprintf(stderr, "Error reading the packets: %s\n", pcap_geterr(fp));
		system("pause");
		return -1;
	}

	system("pause");
	return 0;
}

void ip_protool_packet(struct pcap_pkthdr *header, const u_char *pkt_data, FILE *fp)
{
	struct ip_header *ip_protocol;
	u_int header_length;
	u_int offset;
	u_char tos;
	u_int16_t checksum;

	ip_protocol = (struct ip_header *) (pkt_data + 14);//以太网首部14位
	checksum = ntohs(ip_protocol->ip_checksum);
	tos = ip_protocol->ip_tos;
	offset = ntohs(ip_protocol->ip_off);

	fprintf(fp, "版本号:%d\n", ip_protocol->ip_version);
	fprintf(fp, "首部长度:%d\n", ip_protocol->ip_header_length);
	fprintf(fp, "服务质量:%d\n", tos);
	fprintf(fp, "总长度:%d\n", ntohs(ip_protocol->ip_length));
	fprintf(fp, "标识:%d\n", ntohs(ip_protocol->ip_id));
	fprintf(fp, "偏移:%d\n", (offset & 0x1fff) * 8);
	fprintf(fp, "生存时间:%d\n", ip_protocol->ip_ttl);
	fprintf(fp, "协议类型:%d", ip_protocol->ip_protocol);
	switch (ip_protocol->ip_protocol)
	{
		case 1: fprintf(fp, "(上层协议是ICMP协议)\n"); ++icmp_counter; break;
		case 2: fprintf(fp, "(上层协议是IGMP协议)\n"); break;
		case 6: fprintf(fp, "(上层协议是TCP协议)\n"); break;
		case 17: fprintf(fp, "(上层协议是UDP协议)\n"); break;
		default:break;
	}
	fprintf(fp, "检验和:%d\n", checksum);
	fprintf(fp, "源IP地址:%s\n", inet_ntoa(ip_protocol->ip_souce_address));
	fprintf(fp, "目的地址:%s\n", inet_ntoa(ip_protocol->ip_destination_address));
	fprintf(fp, "\n");
}

void arp_protool_packet(struct pcap_pkthdr *header, const u_char *pkt_data, FILE *fp)
{
	struct ether_arp *arp_protocol;
	arp_protocol = (struct ether_arp *)(pkt_data + 14);
	u_int8_t *arp_sha = arp_protocol->arp_sha;
	u_int8_t *arp_tha = arp_protocol->arp_tha;

	fprintf(fp, "硬件类型:%d\n", ntohs(arp_protocol->ar_hrd));
	fprintf(fp, "协议类型:0x%04x\n", ntohs(arp_protocol->ar_pro));
	fprintf(fp, "硬件地址长度:%d\n", arp_protocol->ar_hln);
	fprintf(fp, "协议地址长度:%d\n", arp_protocol->ar_pln);
	fprintf(fp, "操作类型:%d\n", ntohs(arp_protocol->ar_op));
	fprintf(fp, "发送者硬件地址:%02x:%02x:%02x:%02x:%02x:%02x\n", *arp_sha, *(arp_sha + 1), *(arp_sha + 2), *(arp_sha + 3), *(arp_sha + 4), *(arp_sha + 5));
	fprintf(fp, "发送者IP地址:%s\n", inet_ntoa(arp_protocol->arp_spa));
	fprintf(fp, "目标硬件地址:%02x:%02x:%02x:%02x:%02x:%02x\n", *arp_tha, *(arp_tha + 1), *(arp_tha + 2), *(arp_tha + 3), *(arp_tha + 4), *(arp_tha + 5));
	fprintf(fp, "目标IP地址:%s\n", inet_ntoa(arp_protocol->arp_tpa));
	fprintf(fp, "\n");
}
	```

你可能感兴趣的:(计算机网络,winpcap)