您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > 利用Winpcap捕获发送数据包
利用winpcap捕获数据包、发送数据包在上一章里面,我们学会了如何获取适配器的相关配置信息,在这一章里面,我们将继续更有意义的内容,就是捕获和发送数据包。3.1winpcap捕获数据包流程与相关函数计算机是通过网卡和网络中其他的主机进行通信的,网卡相当于数据包进出的大门,我们平时讲的数据包的捕获相当于大门的门卫在检查进出的行人一样。在网络基础我们学习过,数据包的发送是一个封装的过程,而数据包的接收则是解封装的过程,但是封装和解封装都是在OS内核来完成的,一般的应用程序没办法获取数据包原始的内容,而Winpcap却能提供这样的功能,在数据链路层捕获数据包,提供最原始的信息。其中Winpcap捕获数据的原理在第一章已经介绍过了,大家可以回顾下。另外,数据捕获只能捕获通过本主机网卡的数据,没法捕获其他主机上网卡的数据。下面先看看Winpcap捕获数据时的工作流程。选择捕获数据的网卡打开该网卡开始捕获结束数据捕获的流程1.发现网络设备的函数(find_dev_ex)以前已经介绍过了。2.打开网卡的函数打开设备的函数是pcap_open()。下面是参数snaplen,flags和to_ms的解释说明pcap_t*pcap_open(constchar*source,//指定的网卡的名称intsnaplen,//帧的长度intflags,//网卡捕获的模式intread_timeout,//超时structpcap_rmtauth*auth,//是否要求认证char*errbuf//错误信息存储)snaplen制定要捕获数据包中的哪些部分。在一些操作系统中(比如xBSD和Win32),驱动可以被配置成只捕获数据包的初始化部分:这样可以减少应用程序间复制数据的量,从而提高捕获效率。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。flags:最最重要的flag是用来指示适配器是否要被设置成混杂模式。一般情况下,适配器只接收发给它自己的数据包,而那些在其他机器之间通讯的数据包,将会被丢弃。相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他主机的所有的数据包。大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,我们也会在下面的范例中,使用混杂模式。to_ms指定读取数据的超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用pcap_dispatch()或pcap_next_ex())都会在to_ms毫秒时间内响应,即使在网络上没有可用的数据包。在统计模式下,to_ms还可以用来定义统计的时间间隔。将to_ms设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。如果设置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。3.通过回调方式捕获数据的函数程序功能:打开指定的网卡捕获数据帧,输出数据帧的捕获时间和数据帧的大小等信息。程序运行结果如下图所示:代码如下://@filename:PacketCap1.cpp//程序功能:在指定的网卡上捕获数据帧,并输出数据帧的长度和捕获时间#includepcap.h#includeremote-ext.h#pragmacomment(lib,wpcap)//包处理函数voidpacket_handler(u_char*param,conststructpcap_pkthdr*header,constu_char*pkt_data);//主函数intmain(){pcap_if_t*alldevs;//设备列表pcap_if_t*d;//网卡节点指针intinum;inti=0;pcap_t*adhandle;//要打开的网卡句柄charerrbuf[PCAP_ERRBUF_SIZE];//错误消息//获取本机设备列表if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&alldevs,errbuf)==-1){fprintf(stderr,Errorinpcap_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);elseprintf((Nodescriptionavailable)\n);}if(i==0){printf(\nNointerfacesfound!MakesureWinPcapisinstalled.\n);return-1;}//选择监听的网卡printf(Entertheinterfacenumber(1-%d):,i);scanf(%d,&inum);if(inum1||inumi){printf(\nInterfacenumberoutofrange.\n);//释放设备列表pcap_freealldevs(alldevs);return-1;}//跳转到选中的适配器for(d=alldevs,i=0;iinum-1;d=d-next,i++);//打开设备if((adhandle=pcap_open(d-name,//设备名65536,//65535保证能捕获到不同数据链路层上的每个数据包的全部内容0,//0表示普通模式,1表示混杂模式1000,//读取超时时间NULL,//远程机器验证errbuf//错误信息缓存))==NULL){fprintf(stderr,\nUnabletoopentheadapter.%sisnotsupportedbyWinPcap\n,d-name);//释放设备列表pcap_freealldevs(alldevs);return-1;}printf(\nlisteningon%s...\n,d-description);//释放设备列表pcap_freealldevs(alldevs);//开始捕获pcap_loop(adhandle,0,packet_handler,NULL);return0;}//每次捕获到数据包时,libpcap都会自动调用这个回调函数voidpacket_handler(u_char*param,conststructpcap_pkthdr*header,constu_char*pkt_data){structtm*ltime;chartimestr[16];time_tlocal_tv_sec;//将时间戳转换成可识别的格式local_tv_sec=header-ts.tv_sec;ltime=localtime(&local_tv_sec);strftime(timestr,sizeoftimestr,%H:%M:%S,ltime);printf(%s,%.6dlen:%d\n,timestr,header-ts.tv_usec,header-len);}函数pcap_loop用户捕获数据,有一个回调参数,packet_handler指向一个可以接收数据包的函数。这个函数会在收到每个新的数据包并收到一个通用状态时被libpcap所调用(与函数pcap_loop()和pcap_dispatch()中的user参数相似),数据包的首部一般有一些诸如时间戳,数据包长度的信息,还有包含了协议首部的实际数据。注意:冗余校验码CRC不再支持,因为帧到达适配器,并经过校验确认以后,适配器就会将CRC删除,与此同时,大部分适配器会直接丢弃CRC错误原始帧-ts:structtimeval未指定-caplen:bpf_u_int32未指定-len:bpf_u_int32未指定pcap_pkthdr原始帧头部图1Winpcap捕获数据帧时的过程其中pcap_pkthdr为Winpcap添加上的头部,在pcap.h头文件中定义,其定义如下:Structpcap_pkthdr{structtimevalts;//时间bpf_u_int32caplen;//长度bpf_u_int32len;//帧长度};其中timeval的结构如下:structtimeval{longtv_sec;/*seconds*/longtv_usec;/*andmicroseconds*/};Bpf_u_int32结构如下:typedefu_intbpf_u_int32;为了以后简化相关的程序编写过程,我们对上述程序的相关功能做了一个函数封装,简化了主函数的代码。主要包含以下3个函数:(1)pcap_if*GetAllAdapters();/**函数功能:获取本主机网络列表的头指针参数:返回值:函数成功,返回网卡节点的头指针,失败返回NULL*/(2)pcap_if*SelectAdapter(pcap_if_t*alldev);/**函数功能:显示网卡的列表,并获取用户的要选择侦听的网卡参数:pcap_if*p指向网卡列表的头指针返回值:返回用户选择的网卡的pcap_if指针*/(3)pcap_t*OpenAdapter(pcap_if*p,intmode=0);/**函数功能:打开选定的网卡,可以进行侦听或者在该网卡发送数据参数:pcap_if*p网卡列表的头指针intindex要操作的网卡索引号intmode设置网卡的模式,0表示普通模式,1表示混杂模式,默认为普通模式返回值:成功返回该网卡的句柄,失败返回NULL*/那么主程序的代码如下:#includecommon/PcapTools.hvoidPacketHandle(u_char*param,pcap_pkthdr*header,constu_char*pk_data);//主函数intmain(){pcap_if*alldev;//网卡列表头节点指针pcap_if*d;//选择的网卡序号pcap_t*handle;//网卡句柄alldev=GetAllAdapters();//获取网卡列表d=SelectAdapter(alldev);//提示用户选择侦听的网卡printf(在网卡%s\n侦听....\n,GetDesc(d-description).c_str());handle=OpenAdapter(d);//打开该网卡//通过回调函数捕获数据pcap_loop(handle,0,pcap_handler(PacketHandle),NULL);return0;}//回调函数voidPacketHandle(u_char*param,pcap_pkthdr*header,constu_char*pk_data){staticinti=0;//输出数据包长度printf(数据包%d\t长度:%d字节\t时间:%s\n,++i,header-len,GetTime(header-ts).c_str());}程序执行的结果:4.非回调函数捕获数据的函数本讲的范例程序所实现的功能和效果和上一讲的非常相似(打开适配器并捕获数据包),但本讲将用pcap_next_ex()函数代替上一讲的pcap_loop()函数。pcap_loop()函数是基于回调的原理来进行数据捕获,这是一种精妙的方法,并且在某些场合中,它是一种很好的选择。然而,处理回调有时候并不实用--它会增加程序的复杂度,特别是在拥有多线程的C++程序中。可以通过直接调用pcap_next_ex()函数来获得一个数据包--只有当编程人员使用了pcap_next_ex()函数才能收到数据包。这个函数的参数和捕获回调函数的参数是一样的--它包含一个网络适配器的描述符和两个可以初始化和返回给用户的指针(一个指向pcap_pkthdr结构体,另一个指向数据报数据的缓冲)。intpc
本文标题:利用Winpcap捕获发送数据包
链接地址:https://www.777doc.com/doc-2608886 .html