使用WinPcap编写Sniffer程序内容介绍嗅探器原理Winpcap介绍Winpcap安装Winpcap应用程序结构Sniffer(嗅探器)设计原理通常的套接字程序只能响应与自己硬件地址相匹配的或是以广播形式发出的数据帧,对于其他形式的数据帧,比如已到达网络接口但却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取到达的数据包。网络嗅探器的目的恰恰在于从网卡接收所有经过它的数据包,这些数据包既可以是发给本机的也可以是发往别处的。通过将网络接口设置为混杂模式可以使它接收所有经过它的数据包(例如以太网帧将会到达同一局域网的所有网络接口)。但是,此时操作系统不再行底层的细节操作(协议处理,流量均衡等),而是将拆封解释处理接收到的数据帧(frame)的任务交给应用程序完成,这样应用程序就可以灵活的获取各类信息。什么是Winpcap网络数据包捕获库函数直接访问网络,免费、公用工作于驱动层,网络操作高效为应用程序提供了一组API接口编程容易,源码级移植方便Libpcap(UNIX)库Winpcap(Windows)库Winpcap介绍WinPcap主要功能捕获原始数据包将数据包发送给应用程序之前,按照用户规定的规范过滤数据包将捕获到的数据包输出到文件中,并可以对这些文件进行再分析向网络发送原始数据包搜集网络传输统计数据Winpcap介绍哪些应用适合使用WinPcap网络和协议分析networkandprotocolanalyzers网络监控networkmonitors流量记录trafficloggers流量产生trafficgenerators用户级网桥和路由器user-levelbridgesandrouters网络入侵检测networkintrusiondetectionsystems(NIDS)网络扫描networkscanners安全工具securitytoolsWinpcap介绍WinPcap不能胜任的事情WinPcap从主机的协议(如TCP/IP)独立收发数据包。这意味着它不能阻塞、过滤或者处理同一主机上其他程序产生的数据包:它仅仅嗅探网线上传输的数据包。所以它不适合应用于流量均衡、QoS调度和个人防火墙。Winpcap介绍Winpcap的安装下载安装包和开发包的安装包(Winpcap_3_1.exe)程序员开发包(WpdPack_3_1.zip)运行Winpcap_3_1.exe测试安装结果Winpcap安装编程环境设定1.以Administrator身份登录Windows(2000/XP),运行一次Winpcap自带例程,此后可以一般用户身份使用2.运行VisualC++6.0,打开WpdPack_3_1\WpdPack\Examples-pcap\下的任一项目(本例用basic_dump目录下basic_dump.dsw)3.在“工程-设置-Link-对象/库模块”中加入wsock32.libws2_32.libwpcap.lib在“工具-选择-目录”的includefiles和libraryfiles设置中引入winpcap开发包中的Include和Lib目录4.编译,运行Winpcap安装Winpcap安装例程运行结果:WinPcap的典型应用获得已安装设备的高级信息过滤数据包获得设备列表打开一个适配器回调机制直接方式解析数据包获得网络流量统计数字打开离线数据包文件获得设备列表(一)一个基本的WinPcap应用程序所需的第一步就是获得合适的网络适配器。Libpcap提供pcap_findalldevs()函数完成这个功能。这个函数返回一个相连的pcap_if结构的列表,列表的每一项包含关于适配器的复杂的信息。特别的,name和description域数据包含设备的名称和可读的描述。pcap_if_t*alldevs,*d;inti=0;charerrbuf[PCAP_ERRBUF_SIZE];if(pcap_findalldevs(&alldevs,errbuf)==-1){fprintf(stderr,Errorinpcap_findalldevs:%s\n,errbuf);exit(1);}for(d=alldevs;d;d=d-next){/*Printthelist*/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;}pcap_freealldevs(alldevs);获得设备列表(二)每个pcap_findalldevs()返回的pcap_if结构也包含了一个pcap_addr结构的列表:该接口的地址列表网络掩码的列表(每个网络掩码对应地址列表中的一项)广播地址的列表(每个广播地址对应地址列表中的一项)目标地址的列表(每个目标地址对应地址列表中的一项)通过返回的结构,我们可以得到探测到的网卡设备的更详尽信息。typedefstructpcap_ifpcap_if_tstructpcap_if{structpcap_if*next;char*name;char*description;structpcap_addr*addresses;bpf_u_int32flags;/*PCAP_IF_interfaceflags*/};structpcap_addr{structpcap_addr*next;structsockaddr*addr;structsockaddr*netmask;structsockaddr*broadaddr;structsockaddr*dstaddr;};打开一个适配器开始捕获数据包pcap_t*pcap_open_live(constchar*device,intsnaplen,intpromisc,intto_ms,char*ebuf)pcap_t*adhandle=pcap_open_live(d-name,65536,1,1000,errbuf);设备标识(字符串)抓包长度混杂模式超时时间捕获数据包(回调机制)intpcap_loop(pcap_t*p,intcnt,pcap_handlercallback,u_char*user)例如:pcap_loop(adhandle,0,packet_handler,NULL);typedefvoid(*pcap_handler)(u_char*user,conststructpcap_pkthdr*pkt_header,constu_char*pkt_data)捕获数据包(直接方式)intpcap_next_ex(pcap_t*p,structpcap_pkthdr**pkt_header,constu_char**pkt_data)该函数从接口或者脱机读取一个数据包。用于接收下一个可用的数据包,而不使用libpcap提供的传统的回调机制。pcap_next_ex用下一个数据包的指向数据包头和数据的指针填充pkt_header和pkt_data参数。pcap_next_ex()目前只在Win32下可用,因为它不是属libpcap原始的API。这意味着含有这个函数的代码将不能被移植到Unix上。过滤数据包intpcap_compile(pcap_t*p,structbpf_program*fp,char*str,//过滤表达式intoptimize,bpf_u_int32netmask)//掩码intpcap_setfilter(pcap_t*p,structbpf_program*fp)pcap_compile()编译一个包过滤器。将一个高级的、布尔形式表示的字符串转换成低级的、二进制过滤语句,以便被包驱动使用。pcap_setfilter()在核心驱动中将过滤器和捕获过程结合在一起。从这一时刻起,所有网络的数据包都要经过过滤,通过过滤的数据包将被传入应用程序。过滤设置举例charpacket_filter[]=ipandudp;structbpf_programfcode;/*获取接口地址的掩码,如果没有掩码,认为该接口属于一个C类网络*/if(d-addresses!=NULL)netmask=((structsockaddr_in*)(d-addresses-netmask))-sin_addr.S_un.S_addr;elsenetmask=0xffffff;if(pcap_compile(adhandle,&fcode,packet_filter,1,netmask)0){fprintf(stderr,\nUnabletocompilethefilter.Checkthesyntax.\n);pcap_freealldevs(alldevs);return-1;}if(pcap_setfilter(adhandle,&fcode)0){fprintf(stderr,\nErrorsettingthefilter.\n);pcap_freealldevs(alldevs);return-1;}过滤表达式表达式由一个或多个原语组成。原语通常由一个id(名称或者数字)和在它前面的一个或几个修饰符组成。有3种不同的修饰符:类型指明id名称或者数字指的是哪种类型。可能是host,net和port。例如“hostfoo”、“net128.3”、“port20”。如果没有类型修饰符,缺省为host。方向指明向和/或从id传输等方向的修饰符。可能的方向有src、dst、srcordst和srcanddst。例如“srcfoo”、“dstnet128.3”、“srcordstportftp-data”。如果缺省为srcordst。协议指明符合特定协议的修饰符。目前的协议包括ether、fddi、ip、ip6、arp、rarp、tcp和udp等。例如“ethersrcfoo”、“arpnet128.3”。如果没有协议修饰符,则表示声明类型的所有协议。例如“srcfoo”表示“(iporarporrarp)srcfoo”“port53”表示“(tcporudp)port53”。解析数据包不同的网络使用不同的链路层协议,不知道网络类型就无法定位数据帧(frame),所以,首先使用如下函数对网络类型进行判断:intpcap_datalink(pcap_t*p)它返回适配器的链路层标志,例如DLT_EN10MB表示以太网(10Mb,100Mb,1000Mb及以上),DLT_IEEE802表示令牌环网。可以在设置过滤条件之前先对各个设备的网络类型进行探测,以获知物理层的数据帧格式,接下来就可以针对不同的帧格式解析出封装在其中的报文(packet),例如IP报文,继而拆封IP报文得到TCP或者UDP等报文,再深层次的拆封就是应用程序数据了。解析数据包前面我们提到,用户在程序中调用的pcap_loop()(或者pcap_dispatch())函数可以进行数据包的捕获,而每一个数据包到达时该函数会调用pcap_handler()函数进行数据包处理,返回一个指向捕获器头部和一个指向帧数据的指针(包含协议头)。下面我们针对常用的以太网给出这一过程及IP、TCP的头部格式,其他数据包的格式请参考RFC文档。注意:在数据包解析时要检查校验和以及包的顺序。Ethernet帧头structsniff_ethernet{u_charether_dhost[ETHER_ADDR_LEN];u_charether_shost[ETHER_ADDR_L