使用libpcap分析网络报文

来源:互联网 发布:淘宝打包员好做吗 编辑:程序博客网 时间:2024/05/17 03:31

最近要对tcpdump抓到的报文进行分析,开始的时候用wireshark的命令行工具tshark把分析的结果保存成文本文件然后再用正则表达式匹配需要的字段,这样好处是不用自己分析协议,只要抓取需要的字段就行了,缺点是相当地慢,330M的tcpdump文件经过tshark处理后得到5G+的文本文件,还要从这5G+文本中做字符串匹配……

于是往下走一层,发现wireshark和tcpdump都使用了同样的库--libpcap,来实现抓包操作。对于抓包需要用到的libpcap函数并不多,主要是自己分析协议部分比较麻烦。网上找了些libpcap的资料,在这里总结一下。

简介(参考资料[1])

libpcap是一个C语言库,libpcap的英文意思是 Packet Capture library,即数据包捕获函数库,其功能是通过网卡抓取网络以太网中的数据包。这个库为不同的平台提供了一致的c函数编程接口,在安装了 libpcap 的平台上,以 libpcap 为接口写的程序、应用,能够自由地跨平台使用。它支持多种操作系统。libpcap 结构简单,使用方便;它提供了20多个api封装函数,我们利用这些api函数即可完成本网络探测器所需的网络数据包监听功能。

第一个pcap程序

#include <stdio.h>#include <pcap.h> int main(){   pcap_t* pd;   char ebuf[PCAP_ERRBUF_SIZE], *dev;   const u_char* pkt;   struct pcap_pkthdr ph;    dev = pcap_lookupdev(ebuf);   if (!dev) {      fprintf(stderr, "%s\n", ebuf);      return -1;   }    printf("get net device -> %s\n", dev);    pd = pcap_open_live(dev, 65535, 0, 0, ebuf);   if (!pd) {      fprintf(stderr, "%s\n", ebuf);      return -1;   }    pkt = pcap_next(pd, &ph);   printf("A packet is captured.\n");    pcap_close(pd);    return 0;

使用libpcap的函数要包含头文件pcap.h,编译时加上链接选项-lpcap。函数

char *pcap_lookupdev(char *errbuf);

用来获取当前机器上可用的网络接口名称,如果找到则返回名称,找不到则返回NULL,失败原因保存在errbuf中。errbuf的长度至少为PCAP_ERRBUF_SIZE。这个函数好像只查找以太网卡,并没有把我机器上的无线网卡也列出来,不知道如果有多块以太网卡的时候会怎样。

找到设备之后就可以进行监听了。函数

pcap_t *pcap_open_live(const char *device, int snaplen,      int promisc, int to_ms, char *errbuf);
对pcap_lookupdev()找到的设备进行监听。参数device是可用的设备名称;snaplen指定捕获报文的最大长度;promisc设置网卡是否处于混杂模式,0为非混杂模式,1为混杂模式;to_ms设置捕获的时间,如果该时间段内没有数据包就超时返回,如果有就返回该时间段内捕获到的所有数据包,如果该参数为0说明如果没有数据到来时处于阻塞模式,直到收到符合要求的数据包为止;errbuf用于保存出错信息。

如果要打开tcpdump或wireshark保存的数据文件可以使用函数

pcap_t *pcap_open_offline(const char *fname, char *errbuf);
其中fname是要打开的文件,errbuf用于保存出错时的信息。

接下来开始捕获报文。函数

const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);
接收两个参数:第一个是使用pcap_open_live()或pcap_open_offline()得到的pcap_t指针,第二个参数是pcap为捕获到的每个包加上的信息。一般来说,由pcap捕获的数据包格式像下面这样:
+---------------------+-------------------+-----+| struct ether_header | ip/arp/... header | ... |+---------------------+-------------------+-----+

pcap_next()会把pcap加上的信息填充到参数h指向的结构体中,并且返回指向实际数据包的指针(也就是struct ether_header的起始位置)。函数的返回时间取决于在pcap_open_live中的to_ms参数值,对于直接打开文件来说则不需要阻塞。

当pcap_next()返回后打印信息”A packet is captured.”(因为在pcap_open_live()中设置超时间隔为0,也就是说pcap_next()会一直阻塞直到有数据包到达)。

当分析完成后使用函数

void pcap_close(pcap_t *p);
释放资源。

另一个pcap程序:

#include <stdio.h>#include <pcap.h> static void printer(u_char* arg, const struct pcap_pkthdr* ph,      const u_char* packet){   printf("A packet is captured.\n");} int main(){   pcap_t* pd;   char ebuf[PCAP_ERRBUF_SIZE], *dev;    dev = pcap_lookupdev(ebuf);   if (!dev) {      fprintf(stderr, "%s\n", ebuf);      return -1;   }    printf("get net device -> %s\n", dev);    pd = pcap_open_live(dev, 65535, 0, 0, ebuf);   if (!pd) {      fprintf(stderr, "%s\n", ebuf);      return -1;   }    pcap_dispatch(pd, 0, printer, NULL);   pcap_close(pd);    return 0;

 

前面打开设备的部分与第一个程序相同,不同的是捕获的部分使用了另一个函数

int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
pcap_dispatch()直到读取完cnt个数据包,或者读取超时,或者读到文件或缓冲区的结尾时返回。如果cnt为-1或0表示读取全部数据包,直到读取超时或到结尾时返回。参数user是用户自定义的参数,用于传递给回调函数。每次捕获一个数据包时会调用回调函数callback,其中pcap_handler的定义如下:
typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h,      const u_char *bytes);
第一个参数user就是传递给pcap_dispatch()的最后一个参数;第二个参数是指向pcap数据包起始位置的struct pcap_pkthdr的指针(见前面的图),bytes(或者叫packet比较符合实际)指向实际的网络报文起始位置(在前面的图中就是struct ether_header的位置),其中整个报文的长度(从bytes指向的位置开始到整个报文的结束)由struct pcap_pkthdr中的caplen指定。struct pcap_pkthdr的定义为:
struct pcap_pkthdr {   struct timeval ts;      /* time stamp */   bpf_u_int32 caplen;     /* length of portion present */   bpf_u_int32 len;        /* length this packet (off wire) */};

第一个参数ts为捕获报文的时间,第三个参数len没搞明白,但是从实际输出来看和caplen的值一样。

从实际输出看到,每次捕获到一个报文都会调用回调函数printer()打印消息。

另外还有一个和pcap_dispatch()功能相似的函数

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);

这个函数在读取超时的时候不会返回,只在捕获了cnt个数据包或读取出错时才返回。

另外可以使用函数pcap_dump_open(),pcap_dump()和pcap_dump_close()把报文保存在文件中。tcpdump和wireshark保存的报文格式都遵循pcap格式,所以它们可以打开对方的数据文件,不过直接使用pcap函数保存的文件会在文件开头加上一些pcap的信息,这个文件不能直接用tcpdump或wireshark打开,要把开头的pcap内容删掉才行。

好了,目前用到的函数主要就是这些,更多函数可以参考[1]和pcap.h的内容。

参考资料

[1] libpcap函数库
[2] The Sniffer’s Guide to Raw Traffic (a libpcap tutorial)

 

转自 http://ouonline.net/libpcap-1