0.
这一次具体讲抓包的两种方法。
不过说明之前得知道几点:
第一,无线网卡的包需要特定网卡驱动才能抓到。
第二,抓以太网上的包的时候,需要禁用无线网卡。(这一点没搞清楚原因,有了解的希望能给个答案,解个惑。感谢。回学校了问问老师。)
第三,(建议)清除ARP表,最好自己写个批处理命令。快一点。
1.0 抓包步骤
步骤很简单:先打开适配器列表 --> 选择适配器 --> 通过遍历链表的方式到达你选择的适配器位置 --> 打开设备 --> 开始抓包。
每一个步骤都是一个函数。了解步骤,后面就好办。
首先,了解一个数据类型pcap/pcap_t。它代表一个打开的设备,理解成它就是适配器就行(实际上是这个适配器的描述符)。这个结构体对用户来说是不透明的,它通过wpcap.dll提供的函数,维护了它的内容。
然后,跳转至你的设备在WinPcap编程(一)中存在,就不多说。
之后,打开设备的函数:
pcap_t* pcap_open ( const char * source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth * auth, char * errbuf )
备注:
第一个参数:source是我们所要打开的设备。当我们获取所有的设备之后,这个source就是d->name。不能为NULL。
第二个参数:snaplen是我们抓取的数据包的大小,100就是抓取整个数据包的前100B,65536最大,把整个包都包括了。
第三个参数:flags设置为混杂模式(PCAP_OPENFLAG_PROMISCUOUS ),即不管数据包是否给我,我都去捕获。这样可以捕捉局域网内所有的包。
第四个参数: read_timeout设置超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取数据操作的时候,不管网络上有没有包,都会在 read_timeout这个时间内响应。
设置为0意味着没有超时。没有数据包到达,读操作将永远不会返回。
设置成-1,无论有没有数据包到达,读操作都会立即返回。
第五个参数:auth远程机登录信息,若本地则为NULL;
第六个参数:errbuf 出错信息。
1.1抓包的两种方式:
1.1.1 回调函数的方法进行抓包
每次抓到包就用回调函数进行处理。处理完接着抓包(如果设置了num的话)。
抓包函数:
int pcap_loop ( pcap_t * p, int cnt, pcap_handler callback, u_char * user )
备注0:
第一个参数:P即是打开的设备。
第二个参数:cnt表示捕捉个数。
第三个参数:回调函数指针。对捕捉的数据包进行操作 。
第四个参数:用户信息,一般为NULL。
函数返回值:
返回值为0,捕捉了cnt次,正常返回。
返回值为-1,发生错误。
返回值为-2,使用pcap_breakloop()结束循环。
回调函数抓包还有一种方法, pcap_dispatch() 。
区别是: pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回,只有当 cnt 数据包被捕获。
所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_dispatch() 函数一般用于比较复杂的程序中。
1.1.2 不利用回调函数
回调函数比较好,但是有时候需要不用回调函数的方法,特别在多线程里面。
我用C# + WPF写的,用回调函数的话一直出现错误。仔细考虑,应该是托管与非托管的问题。看了SharpPcap的源码,貌似也没有好的解决方案。
【有好的方案的看见了希望能够提供一下,感谢。】
不利用回调函数抓包的话,方法比较简单,每次只能抓一个包,加个while()循环就OK了。
int pcap_next_ex ( pcap_t * p, struct pcap_pkthdr ** pkt_header, const u_char ** pkt_data )
备注:
第一个参数:打开的设备。
第二个参数:WinPcap添加的一些信息。(时间戳,捕捉到包的长度,实际包的长度)。
第三个参数:实际数据包。
函数返回值:
返回值为1,正常返回;
返回值为0,超时后返回一个无效的包;
返回值为-1,发生错误;
返回值为-2,读到EOF。
源码:
#define WIN32 #include "pcap.h" #include "winsock.h" #include "time.h" /* 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]; /* 获取本机设备列表 */ 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_s("%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, // 混杂模式 0, // 读取超时时间 NULL, // 远程机器验证 errbuf // 错误缓冲池 )) == NULL) { fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); /* 释放设备列表 */ pcap_freealldevs(alldevs); return -1; } printf("\nlistening on %s...\n", d->description); /* 释放设备列表 */ pcap_freealldevs(alldevs); /* 开始捕获 */ pcap_loop(adhandle, 30, packet_handler, NULL); system("pause"); return 0; } /* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) { struct tm ltime = {0}; char timestr[16]; time_t local_tv_sec; /* 将时间戳转换成可识别的格式 */ local_tv_sec = header->ts.tv_sec; localtime_s(<ime, &local_tv_sec); strftime(timestr, sizeof timestr, "%H:%M:%S", <ime); printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len); }
回调函数
#define WIN32 #include "pcap.h" int main() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i = 0; pcap_t *adhandle; int res; char errbuf[PCAP_ERRBUF_SIZE]; struct tm ltime; char timestr[16]; struct pcap_pkthdr *header; const u_char *pkt_data; time_t local_tv_sec; int j = 10;//限制只抓10个包 /* 获取本机设备列表 */ 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_s("%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", d->name); /* 释放设列表 */ pcap_freealldevs(alldevs); return -1; } printf("\nlistening on %s...\n", d->description); /* 释放设备列表 */ pcap_freealldevs(alldevs); /* 获取数据包 */ while ((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0&&j>0){ if (res == 0) /* 超时时间到 */ continue; /* 将时间戳转换成可识别的格式 */ local_tv_sec = header->ts.tv_sec; localtime_s(<ime,&local_tv_sec); strftime(timestr, sizeof timestr, "%H:%M:%S", <ime); j--; printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len); } if (res == -1){ printf("Error reading the packets: %s\n", pcap_geterr(adhandle)); return -1; } system("pause"); return 0; }
非回调函数