我们电脑上的网卡有一个重要功能就是判断每一个到自己这的数据帧是不是发给自己的,如果不是的话会主动丢弃,从而保证了因特网的一定的安全性。而利用win_pcap将网卡设置为混杂模式可以捕获网卡上的所有经过的数据包。
首先利用pcap_findalldevs()函数获取已连接的网络适配器列表。
int pcap_findalldevs( pcap_if_t * * alldevsp, char *errbuf ) **alldevsp是一个指向网卡链表头一个节点的指针,errbuf返回的是错误信息。pcap_if_t结构体的定义如下:
这样可以打印出网卡列表从而选择一个网卡进行数据捕获。
然后利用pcap_open_live()打开网络设备,这个函数定义入下:
pcap_t * pcap_open_live ( char * device, //网络设备 int snaplen, //捕获的数据包长度,最大65535 int promisc, //网络设备工作模式,1为混杂模式 int to_ms, //捕获的时间间隔 char *ebuf //错误缓冲区 ) 返回值是winpcap句柄再利用pcap_compile()和pcap_setfilter()设置过滤规则。
int pcap_compile( pcap_t * p, //WinPcap句柄 struct bpf_program * fp, //BPF规则 char * str, //过滤规则字符串 int optimize, //优化 bpf_u_int32 netmask //掩码 )如果我们只想捕获IP数据包的话,就把过滤规则字符串设置为“IP”,编译过后的过滤规则将被返回至struct bpf_program * fp,这是一个编译过的过滤规则结构体。 int pcap_setfilter( pcap_t * p, //WinPcap句柄 struct bpf_program * fp //BPF规则 ) 给winpcap句柄设置过滤规则。接下来就利用pcap_loop函数对数据包进行捕获,并进行处理。
int pcap_loop( pcap_t *p, //WinPcap句柄 int cnt, // 捕获的个数,负值表示无限捕获 pcap_handler call-back, //回调函数 u_char* user //回调函数的参数 ) 回调函数相当于是一个安卓中的事件监听器,当事件发生后自动执行。回调函数call_back的原型:
typedef void( * ) pcap_handler ( u_char * user, //传递进来的用户参数 const struct pcap_pkthdr * pkt_header, //数据包的简单信息 const u_char * pkt_data //数据包的原始数据 ) pcap_pkthdr结构体的定义: struct pcap_pkthdr{ struct timeval ts; //捕获数据包的时间 bpf_u_int32 caplen; //捕获的长度 bpf_u_int32 len; //数据包长度 } 利用上面这些函数,就可以实现IP包的捕获与解析了。完整源代码如下:
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <pcap.h> #define MAX_PRINT 80 #define MAX_LINE 16 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; }; struct ether_header { u_int8_t ether_dhost[6]; //目的Mac地址 u_int8_t ether_shost[6]; //源Mac地址 u_int16_t ether_type; //协议类型 }; void ip_protool_packet_callback(u_char*, const struct pcap_pkthdr*, const u_char*); void ethernet_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr* packet_header, const u_char* packet_content) { u_short ethernet_type; struct ether_header *ethernet_protocol; u_char *mac_string; static int packet_number = 1; printf("----------------------------------------------\n"); printf("捕获第%d个网络数据包\n", packet_number); printf("数据包长度:\n"); printf("%d\n", packet_header->len); printf("---------以太网协议---------\n"); ethernet_protocol = (struct ether_header*)packet_content;//获得数据包内容 printf("以太网类型:\n"); ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网类型 printf("x\n", ethernet_type); switch (ethernet_type) { case 0x0800: printf("上层协议是IP协议\n"); break; case 0x0806: printf("上层协议是ARP协议\n"); break; case 0x8035: printf("上层协议是RARP协议\n"); break; default:break; } printf("MAC帧源地址:\n"); mac_string = ethernet_protocol->ether_shost; printf("x:x:x:x:x:x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5)); printf("MAC帧目的地址:\n"); mac_string = ethernet_protocol->ether_dhost; printf("x:x:x:x:x:x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5)); if (ethernet_type == 0x0800)//继续分析IP协议 { ip_protool_packet_callback(argument, packet_header, packet_content); } printf("----------------------------------------------\n"); packet_number++; } void ip_protool_packet_callback(u_char *argument, const struct pcap_pkthdr* packet_header, const u_char* packet_content) { struct ip_header *ip_protocol; u_int header_length; u_int offset; u_char tos; u_int16_t checksum; //MAC首部是14位的,加上14位得到IP协议首部 ip_protocol = (struct ip_header *) (packet_content + 14); checksum = ntohs(ip_protocol->ip_checksum); tos = ip_protocol->ip_tos; offset = ntohs(ip_protocol->ip_off); printf("---------IP协议---------\n"); printf("版本号:%d\n", ip_protocol->ip_version); printf("首部长度:%d\n", ip_protocol->ip_header_length); printf("服务质量:%d\n", tos); printf("总长度:%d\n", ntohs(ip_protocol->ip_length)); printf("标识:%d\n", ntohs(ip_protocol->ip_id)); printf("偏移:%d\n", (offset & 0x1fff) * 8); printf("生存时间:%d\n", ip_protocol->ip_ttl); printf("协议类型:%d\n", ip_protocol->ip_protocol); switch (ip_protocol->ip_protocol) { case 1: printf("上层协议是ICMP协议\n"); break; case 2: printf("上层协议是IGMP协议\n"); break; case 6: printf("上层协议是TCP协议\n"); break; case 17: printf("上层协议是UDP协议\n"); break; default:break; } printf("检验和:%d\n", checksum); printf("源IP地址:%s\n", inet_ntoa(ip_protocol->ip_souce_address)); printf("目的地址:%s\n", inet_ntoa(ip_protocol->ip_destination_address)); } int main() { pcap_t *fp; pcap_if_t *alldev, *d; char errbuf[30]; u_char user[30]; int retval, i, inum; bpf_u_int32 net_mask; //掩码地址 bpf_u_int32 net_ip; //网络地址 struct bpf_program bpf_filter;//BPF过滤规则 char bpf_filter_string[] = "ip"; retval = pcap_findalldevs(&alldev, errbuf); if (retval == -1) { printf("find all devs failed\n"); } i = 0; for (d = alldev; d != NULL; d = d->next) { printf("%d. %s", i, d->name); i++; if (d->description == NULL) { printf("description: none\n"); } else { printf("description: %s\n", d->description); } } if (i == 0) { printf("no network dev avaliable\n"); } printf("choose one interface number:"); scanf("%d", &inum); for (d = alldev, i = 0; i < inum ; d = d->next, i++); memset(errbuf, 0, sizeof(errbuf)); fp = pcap_open_live(d->name, 65535, 1, 1000, errbuf); if (fp == NULL) { printf("open failed\n"); } pcap_lookupnet(d->name, &net_ip, &net_mask, errbuf); pcap_compile(fp, &bpf_filter, bpf_filter_string, 0, net_mask); pcap_setfilter(fp, &bpf_filter); retval = pcap_loop(fp, 10, ethernet_protocol_packet_callback, user); //printf("%d\n", retval); return 0; }部分代码转自:http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2623641.htmlwin_pcap中文官网文档:http://www.ferrisxu.com/WinPcap/html/index.html