【笔记】wincap处理脱机堆文件

 

  WinPcap提供了很多函数来将网络数据流保存到文件并读取它们。堆文件的格式是libpcap的一种。这种格式中,包含了被捕捉到的包的二进制数据,并且,这种格式是许多网络工具所使用的一种标准,这些工具包括WinDump,Etheral和Snort。

  一、保存数据包到堆文件
  首先,如何将一个数据包写成libpcap的格式。接下来的例子讲从一个选定的接口捕获数据包,并且将它们保存到用户指定的文件中。
///////////////////////////////////////////////////////////////////////////// // Name: DumpFile.cpp // Purpose: wincap处理脱机堆文件。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include "pcap.h" #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") /* 定义处理数据的函数原形 */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); int main(int argc, char **argv) { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; pcap_t *adhandle;//定义文件句柄 char errbuf[PCAP_ERRBUF_SIZE]; pcap_dumper_t *dumpfile; /* 检查命令行参数 是否带有文件名*/ if(argc != 2){ printf("usage: %s filename", argv[0]); return -1; } /* 获得网卡的列表 */ if ( pcap_findalldevs( &alldevs, errbuf ) == -1 ) { fprintf_s( stderr, "pcap_findalldevs调用错误: %s/n", errbuf ); exit(1); } /* 打印网卡信息 */ for( d = alldevs; d; d = d->next ) { printf_s( "%d. %s", ++i, d->name ); if ( d->description ) printf_s( " (%s)/n", d->description ); else printf_s( " (无可用描述)/n" ); } if(i==0) { printf_s( "/n没有发现适配器! 确保安装了WinPcap./n" ); return -1; } printf_s( "选择适配器 (1-%d):", i ); scanf_s( "%d", &inum ); // 输入要选择打开的网卡号 if( inum < 1 || inum > i ) // 判断号的合法性 { printf_s( "/n输入有误,没有此网卡./n" ); /* 释放资源 */ pcap_freealldevs( alldevs ); return -1; } /* 找到要选择的网卡结构 */ for( d = alldevs, i = 0; i < inum-1; d = d->next, i++ ); /* 打开选择的网卡 */ if ( (adhandle= pcap_open_live( d->name, // 设备名称 65536, // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容 1, // 混杂模式 1000, // 读超时为1秒 errbuf // 错误缓冲 ) ) == NULL) { fprintf_s( stderr, "/n无法打开适配器. %s 不被WinPcap支持./n", errbuf ); /* 释放资源 */ pcap_freealldevs( alldevs ); return -1; } /* 打开文件 */ dumpfile = pcap_dump_open( adhandle, argv[1] ); if( dumpfile == NULL ) { fprintf_s( stderr, "/n输出文件打开失败./n" ); return -1; } printf_s("/n开始监听 %s.../n", d->description); /* 释放资源 */ pcap_freealldevs(alldevs); /* 循环捕获数据并调用packet_handler函数把数据存储到堆文件 */ pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile); return 0; } /* 回调函数,用来处理数据包 */ void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data) { /* 此函数功能将数据报存储到堆文件 */ pcap_dump(dumpfile, header, pkt_data); } 
说明:
  1、只有当接口打开时,调用 pcap_dump_open() 才是有效的。 这个调用将打开一个堆文件,并将它关联到特定的接口上。 
  2、数据包将会通过pcap_dump()函数写入堆文件中,这个函数是packet_handler()的回调函数。pcap_dump()的参数和pcap_handler()函数中的参数是一一对应的。
  二、
从堆文件中读取数据包
  既然有了可用的堆文件,那就能读取它的内容了。以下代码将打开一个WinPcap/libpcap的堆文件,并显示文件中每一个包的信息。文件通过pcap_open_offline()打开,然后使用 pcap_loop() 来有序获取数据包。你可以看到,从脱机文件中读取数据包和从物理接口中接收它们是很相似的。
  这个例子还会介绍另一个函数:pcap_createsrcsrc()。这个函数用于创建一个源字符串,这个源字符串以一个标志开头,这个标志会告诉WinPcap这个源的类型。比如,使用"rpcap://"标志来打开一个适配器,使用"file://"来打开一个文件。如果 pcap_findalldevs_ex() 已经被使用,那么这部是不需要的,因为其返回值已经包含了这些字符串。然而,在这个例子中需要它。因为文件的名字来自于用户的输入。
  程序源代码:
///////////////////////////////////////////////////////////////////////////// // Name: DumpFile.cpp // Purpose: wincap处理脱机堆文件。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include "pcap.h" #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") #define LINE_LEN 16 void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *); int main( int argc, char **argv ) { pcap_t *fp; char errbuf[PCAP_ERRBUF_SIZE]; if( argc != 2 ) { printf_s( "usage: %s filename", argv[0] ); return -1; } /* 打开一个存储有数据的堆文件 */ if ( ( fp = pcap_open_offline( argv[1], errbuf ) ) == NULL ) { fprintf_s( stderr, "/n打开堆文件失败./n" ); return -1; } // 读取数据直到遇到EOF标志。 pcap_loop( fp, 0, dispatcher_handler, NULL ); return 0; } void dispatcher_handler( u_char *temp1, const struct pcap_pkthdr *header, const u_char *pkt_data ) { u_int i=0; /* 打印pkt时间戳和pkt长度 */ printf_s( "%ld:%ld (%ld)/n", header->ts.tv_sec, header->ts.tv_usec, header->len ); /* 打印数据包 */ for ( i = 1; ( i < header->caplen + 1 ); i++ ) { printf_s("%.2x ", pkt_data[i-1]); if ( ( i % LINE_LEN ) == 0 ) printf_s( "/n" ); } printf_s( "/n/n" ); } 
  下面的代码具有一样的作用,只不过是用pcap_next_ex()来代替pcap_loop()循环读取数据而已。
///////////////////////////////////////////////////////////////////////////// // Name: DumpFile.cpp // Purpose: wincap处理脱机堆文件。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include "pcap.h" #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") #define LINE_LEN 16 int main( int argc, char **argv ) { pcap_t *fp; char errbuf[PCAP_ERRBUF_SIZE]; struct pcap_pkthdr *header; const u_char *pkt_data; u_int i = 0; int res; if( argc != 2 ) { printf_s( "usage: %s filename", argv[0] ); return -1; } /* 打开一个存储有数据的堆文件 */ if ( ( fp = pcap_open_offline( argv[1], errbuf ) ) == NULL ) { fprintf_s( stderr, "/n打开堆文件失败./n" ); } /* 从文件获取数据包 */ while( ( res = pcap_next_ex( fp, &header, &pkt_data ) ) >= 0 ) { /* 打印pkt时间戳和pkt长度 */ printf_s( "%ld:%ld (%ld)/n", header->ts.tv_sec, header->ts.tv_usec, header->len ); /* 打印数据包 */ for ( i = 1; ( i < header->caplen + 1 ); i++ ) { printf_s("%.2x ", pkt_data[i-1]); if ( (i % LINE_LEN) == 0) { printf_s("/n"); } } printf_s( "/n/n" ); } if( res == -1 ) { printf( "数据包读取错误: %s/n", pcap_geterr(fp) ); } return 0; } 
  三、使用pcap_live_dump将包写入堆文件
  
注意: 此时,由于新内核缓冲的一些问题,这个特性可能不可用。  

  WinPcap的最近几个版本提供了一个更好的途径,将数据流保存到磁盘,那就是pcap_live_dump()函数。pcap_live_dump()函数有3个参数:文件名,文件最大的大小(字节为单位)和文件可以允许存储的数据包的最大数量。0表示没有限制。注意,在调用pcap_live_dump()将数据流保存下来之前,程序可以设置过滤器(使用pcap_setfilter(),详情请参见 过滤数据包这部分),这样,就可以定义要保存的那部分数据流了。
  pcap_live_dump()不会被阻塞,因此,它开始堆处理后会立即返回。堆处理以异步的方式进行,直到文件达到最大大小或者存储的数据包达到最大数量。 
  应用程序可以使用pcap_live_dump_ended()来检查数据是否存储完毕。 

   特别注意: sync 参数必须是非零的,如果它们是0,那么程序将永远被阻塞。
  代码如下:
///////////////////////////////////////////////////////////////////////////// // Name: DumpFile.cpp // Purpose: wincap处理脱机堆文件。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include "pcap.h" #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") int main(int argc, char **argv) { pcap_if_t *alldevs, *d; pcap_t *fp; u_int inum, i=0; char errbuf[PCAP_ERRBUF_SIZE]; printf("kdump: saves the network traffic to file using WinPcap kernel-level dump faeature./n"); printf("/t Usage: %s [adapter] | dump_file_name max_size max_packs/n", argv[0]); printf("/t Where: max_size is the maximum size that the dump file will reach (0 means no limit)/n"); printf("/t Where: max_packs is the maximum number of packets that will be saved (0 means no limit)/n/n"); if(argc < 5) { /* 用户没有提供数据源。获取设备列表 */ if ( pcap_findalldevs( &alldevs, errbuf ) == -1 ) { fprintf_s( stderr, "函数pcap_findalldevs出错: %s/n", errbuf ); exit(1); } /* 打印列表 */ for( d = alldevs; d; d = d->next ) { printf_s( "%d. %s", ++i, d->name ); if ( d->description ) printf_s( "(%s)/n", d->description ); else printf_s( "(无可用描述信息)/n" ); } if(i==0) { printf_s( "/n没有找到设备,确认WinPcap是否安装?/n" ); /* 释放列表 */ pcap_freealldevs(alldevs); return -1; } printf_s( "输入设备编号(1-%d):", i ); scanf_s( "%d", &inum ); if ( inum < 1 || inum > i ) { printf_s( "/n设备范围选择有误./n" ); /* 释放列表 */ pcap_freealldevs(alldevs); return -1; } /* 跳转到所选的设备 */ for( d = alldevs, i = 0; i < inum - 1; d = d->next, i++ ); /* 打开设备 */ if ( ( fp = pcap_open_live( d->name, 100, 1, 20, errbuf ) ) == NULL ) { fprintf_s( stderr, "/n适配器打开失败./n" ); /* 释放列表 */ pcap_freealldevs(alldevs); return -1; } /* 释放设备列表 */ pcap_freealldevs(alldevs); /* 开始堆过程 */ if( pcap_live_dump( fp, argv[1], atoi( argv[2] ), atoi( argv[3] ) ) == -1 ) { printf_s( "无法开始堆过程, %s/n", pcap_geterr( fp ) ); return -1; } } else { /* 打开设备 */ if ( ( fp = pcap_open_live( argv[1], 100, 1, 20, errbuf ) ) == NULL ) { fprintf_s( stderr, "/n设备打开失败./n" ); return -1; } /* 开始堆过程 */ if ( pcap_live_dump( fp, argv[0], atoi( argv[1] ), atoi( argv[2] ) ) == -1 ) { printf_s( "无法开始堆过程, %s/n", pcap_geterr( fp ) ); return -1; } } /* 等待,知道堆过程完成,也就是当数据到达max_size或max_packs的时候 */ pcap_live_dump_ended( fp, TRUE ); /* 关闭适配器,这样,就可以将数据立刻输出到文件了 */ pcap_close(fp); return 0; }  
  pcap_live_dump()和pcap_dump()的不同从设置的最大极限来说就是性能的问题。pcap_live_dump()采用WinPcap NPF驱动来从内核级的层次上向文件中写数据,从而使内存拷贝最小化。
  显然,这些特点当前在其他的操作系统下是不能够实现的,pcap_live_dump()是WinPcap所特有的,而且只能够应用于Win32环境。

 

你可能感兴趣的:(struct,header,null,存储,FP,compiler)