介绍程序模块前,这一节再复习一下WinPcap
WinPcap开发一个嗅探器的主要步骤如下:
(1)获取嗅探设备
WinPcap提供了pcap_findalldevs_ex() 函数来实现这个功能: 这个函数返回一个pcap_if 结构的链表,每个这样的结构都包含了一个适配器的详细信息:
1 /* 获取本地机器设备列表 */ 2 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1) 3 { 4 fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf); 5 exit(1); 6 }
(2)打开设备,建立嗅探会话
打开设备的函数是 pcap_open() ,snaplen 制定要捕获数据包中的哪些部分,flag是用来指示适配器是否要被设置成混杂模式,to_ms 指定读取数据的超时时间,以毫秒计:
1 /* 打开设备 */ 2 if ( (adhandle= pcap_open(d->name, // 设备名 3 65536, // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容 4 PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式 5 1000, // 读取超时时间 6 NULL, // 远程机器验证 7 errbuf // 错误缓冲池 8 ) ) == NULL) 9 { 10 fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); 11 /* 释放设备列表 */ 12 pcap_freealldevs(alldevs); 13 return -1; 14 }
(3)设置过滤器
用来过滤数据包的函数是 pcap_compile() 和 pcap_setfilter(),pcap_compile() 它将一个高层的布尔过滤表达式编译成一个能够被过滤引擎所解释的低层的字节码,pcap_setfilter() 将一个过滤器与内核捕获会话向关联。当 pcap_setfilter() 被调用时,这个过滤器将被应用到来自网络的所有数据包,并且,所有的符合要求的数据包 (即那些经过过滤器以后,布尔表达式为真的包) ,将会立即复制给应用程序:
1 compile the filter 2 if (pcap_compile(adhandle, &fcode, "ip and tcp", 1, netmask) < 0) 3 { 4 fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n"); 5 /* 释放设备列表 */ 6 pcap_freealldevs(alldevs); 7 return -1; 8 } 9 10 set the filter 11 if (pcap_setfilter(adhandle, &fcode) < 0) 12 { 13 fprintf(stderr,"\nError setting the filter.\n"); 14 /* 释放设备列表 */ 15 pcap_freealldevs(alldevs); 16 return -1; 17 }
(4)捕获网络数据包
pcap_loop()函数是基于回调的原理来进行数据捕获,这是一种精妙的方法,并且在某些场合中,它是一种很好的选择。 然而,处理回调有时候并不实用 -- 它会增加程序的复杂度,特别是在拥有多线程的C++程序中。可以通过直接调用pcap_next_ex() 函数来获得一个数据包:
1 pcap_next_ex( adhandle, &header, &pkt_data)
返回值的意义如下:
- 1 if the packet has been read without problems
- 0 if the timeout set with pcap_open_live() has elapsed. In this case pkt_header and pkt_data don't point to a valid packet
- -1 if an error occurred
- -2 if EOF was reached reading from an offline capture
(5)处理网络数据包
(6)释放设备列表
void pcap_freealldevs(pcap_if_t * * alldevs // 前面获取的设备列表)
其流程大致可以概括为这样: