1、使用WinPcap编写Sniffer程序,内容介绍,嗅探器原理 Winpcap介绍 Winpcap安装 Winpcap应用程序结构,Sniffer(嗅探器)设计原理,通常的套接字程序只能响应与自己硬件地址相匹配的或是以广播形 式发出的数据帧,对于其他形式的数据帧,比如已到达网络接口但 却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地 址之后将不引起响应,也就是说应用程序无法收取到达的数据包。网络嗅探器的目的恰恰在于从网卡接收所有经过它的数据包,这些 数据包既可以是发给本机的也可以是发往别处的。通过将网络接口 设置为混杂模式可以使它接收所有经过它的数据包(例如以太网帧 将会到达同一局域
2、网的所有网络接口)。 但是,此时操作系统不再 行底层的细节操作(协议处理,流量均衡等) ,而是将拆封解释处 理接收到的数据帧(frame)的任务交给应用程序完成,这样应用程 序就可以灵活的获取各类信息。,什么是 Winpcap,网络数据包捕获库函数 直接访问网络,免费、公用 工作于驱动层,网络操作高效 为应用程序提供了一组API接口 编程容易,源码级移植方便,Libpcap(UNIX)库,Winpcap(Windows)库,Winpcap介绍,WinPcap主要功能,捕获原始数据包 将数据包发送给应用程序之前,按照用户规定的规范过滤数据包 将捕获到的数据包输出到文件中,并可以对这些文件进行再分
3、析 向网络发送原始数据包 搜集网络传输统计数据,Winpcap介绍,哪些应用适合使用 WinPcap,网络和协议分析network and protocol analyzers 网络监控network monitors 流量记录traffic loggers 流量产生traffic generators 用户级网桥和路由器user-level bridges and routers 网络入侵检测network intrusion detection systems (NIDS) 网络扫描network scanners 安全工具security tools,Winpcap介绍,WinPcap不
4、能胜任的事情,WinPcap从主机的协议(如TCP/IP)独 立收发数据包。这意味着它不能阻塞、过 滤或者处理同一主机上其他程序产生的数 据包:它仅仅嗅探网线上传输的数据包。 所以它不适合应用于流量均衡、QoS调度 和个人防火墙。,Winpcap介绍,Winpcap的安装,下载安装包和开发包http:/winpcap.polito.itWinpcap的安装包(Winpcap_3_1.exe)程序员开发包(WpdPack_3_1.zip)运行Winpcap_3_1.exe测试安装结果,Winpcap安装,编程环境设定,1. 以Administrator身份登录Windows(2000/XP),运
5、行一次Winpcap自带例程,此后可以一般用户身份使用2. 运行Visual C+ 6.0,打开WpdPack_3_1WpdPackExamples-pcap下的任一项目(本例用basic_dump目录下 basic_dump.dsw)3. 在“工程-设置 Link对象/库模块” 中加入wsock32.lib ws2_32.lib wpcap.lib 在“工具-选择-目录”的include files和library files设置中引入winpcap开发包中的Include和Lib目录4. 编译,运行,Winpcap安装,Winpcap安装,例程运行结果:,WinPcap的典型应用,回调机制
6、,直接方式,获得设备列表 (一),一个基本的WinPcap应用程序所需的第一步 就是获得合适的网络适配器。Libpcap提供 pcap_findalldevs() 函数完成 这个功能。这个函数返回一个相连的pcap_if结 构的列表,列表的每一项包含关于适配器的复杂 的信息。特别的,name和description域数据包 含设备的名称和可读的描述。,pcap_if_t *alldevs,*d;int i=0; char errbufPCAP_ERRBUF_SIZE; if (pcap_findalldevs(,获得设备列表 (二),每个pcap_findalldevs() 返回的 pcap_
7、if 结构也 包含了一个pcap_addr 结构的列表:该接口的地址列表 网络掩码的列表(每个网络掩码对应地址列表中的一项) 广播地址的列表(每个广播地址对应地址列表中的一项) 目标地址的列表(每个目标地址对应地址列表中的一项)通过返回的结构,我们可以得到探测到的网卡 设备的更详尽信息。,typedef struct pcap_if pcap_if_t struct pcap_if struct pcap_if *next;char *name;char *description; struct pcap_addr *addresses;bpf_u_int32 flags; /* PCAP_I
8、F_ interface flags */ ; struct pcap_addr struct pcap_addr *next;struct sockaddr *addr;struct sockaddr *netmask;struct sockaddr *broadaddr; struct sockaddr *dstaddr; ;,打开一个适配器开始捕获数据包,pcap_t * pcap_open_live ( const char * device, int snaplen, int promisc, int to_ms, char * ebuf )pcap_t *adhandle= pca
9、p_open_live(d-name, 65536,1, 1000, errbuf );,设备标识 (字符串),抓包长度,混杂模式,超时时间,捕获数据包(回调机制),int pcap_loop ( pcap_t * p, int cnt, pcap_handler callback, u_char * user ) 例如: pcap_loop(adhandle, 0, packet_handler, NULL); typedef void(* pcap_handler) ( u_char *user, const struct pcap_pkthdr *pkt_header,const u_c
10、har *pkt_data),捕获数据包(直接方式),int pcap_next_ex ( pcap_t * p, struct pcap_pkthdr * pkt_header, const u_char * pkt_data )该函数从接口或者脱机读取一个数据包。用于接收下一个可用的数 据包,而不使用libpcap提供的传统的回调机制。 pcap_next_ex用下 一个数据包的指向数据包头和数据的指针填充pkt_header和pkt_data 参数。pcap_next_ex() 目前只在Win32下可用,因为它不是属libpcap 原始的API。这意味着含有这个函数的代码将不能被移植到U
11、nix上。,过滤数据包,int pcap_compile ( pcap_t * p, struct bpf_program * fp, char * str, /过滤表达式int optimize, bpf_u_int32 netmask ) /掩码 int pcap_setfilter ( pcap_t * p, struct bpf_program * fp ) pcap_compile() 编译一个包过滤器。将一个高级的、布尔形式表 示的字符串转换成低级的、二进制过滤语句,以便被包驱动使用。 pcap_setfilter() 在核心驱动中将过滤器和捕获过程结合在一起。从 这一时刻起,所有
12、网络的数据包都要经过过滤,通过过滤的数据 包将被传入应用程序。,过滤设置举例,char packet_filter = “ip and udp“;struct bpf_program fcode;/* 获取接口地址的掩码,如果没有掩码,认为该接口属于一个C类网络 */ if(d-addresses != NULL) netmask=(struct sockaddr_in *)(d-addresses-netmask)-sin_addr.S_un.S_addr;else netmask=0xffffff; if(pcap_compile(adhandle, ,过滤表达式,表达式由一个或多个原语组
13、成。原语通常由一个id(名称或者数字) 和在它前面的一个或几个修饰符组成。有3种不同的修饰符:类型 指明id名称或者数字指的是哪种类型。可能是host,net和port。例如 “host foo”、“net 128.3”、“port 20”。如果没有类型修饰符, 缺省为host。方向 指明向 和/或 从id传输等方向的修饰符。可能的方向有src、dst、src or dst 和 src and dst。例如“src foo”、“dst net 128.3”、“src or dst port ftp-data”。如果缺省为src or dst。协议 指明符合特定协议的修饰符。目前的协议包括eth
14、er、fddi、ip、ip6、arp、rarp、tcp和udp等。 例如“ether src foo”、 “arp net 128.3”。如果没有协议修饰符,则表示声明类型的所有协议。例如“src foo”表示“(ip or arp or rarp) src foo” “port 53”表示“(tcp or udp) port 53”。,解析数据包,不同的网络使用不同的链路层协议,不知道网络类型就无法定位数 据帧(frame) ,所以,首先使用如下函数对网络类型进行判断:int pcap_datalink(pcap_t *p) 它返回适配器的链路层标志,例如DLT_EN10MB表示以太网(10
15、Mb, 100Mb,1000Mb及以上),DLT_IEEE802表示令牌环网。可以在设 置过滤条件之前先对各个设备的网络类型进行探测,以获知物理层 的数据帧格式,接下来就可以针对不同的帧格式解析出封装在其中 的报文(packet),例如IP报文,继而拆封IP报文得到TCP或者UDP 等报文,再深层次的拆封就是应用程序数据了。,解析数据包,前面我们提到,用户在程序中调用的pcap_loop()(或者 pcap_dispatch())函数可以进行数据包的捕获,而每一 个数据包到达时该函数会调用pcap_handler()函数进行数 据包处理,返回一个指向捕获器头部和一个指向帧数据 的指针(包含协议
16、头)。下面我们针对常用的以太网给 出这一过程及IP、TCP的头部格式,其他数据包的格式 请参考RFC文档。注意:在数据包解析时要检查校验和 以及包的顺序。,Ethernet帧头,struct sniff_ethernet u_char ether_dhostETHER_ADDR_LEN; u_char ether_shostETHER_ADDR_LEN; u_short ether_type; /* IP? ARP? RARP? 等*/ ;,IP报文头,struct sniff_ip #if BYTE_ORDER = LITTLE_ENDIANu_int ip_hl:4, /* header
17、length */ip_v:4; /* version */#endif#if BYTE_ORDER = BIG_ENDIANu_int ip_v:4, /* version */ip_hl:4; /* header length */#endif /* not _IP_VHL */u_char ip_tos; /* type of service */u_short ip_len; /* total length */u_short ip_id; /* identification */u_short ip_off; /* fragment offset field */#define IP
18、_RF 0x8000 /* reserved fragment flag */#define IP_DF 0x4000 /* dont fragment flag */#define IP_MF 0x2000 /* more fragments flag */#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */u_char ip_ttl; /* time to live */u_char ip_p; /* protocol */u_short ip_sum; /* checksum */struct in_addr ip_src,i
19、p_dst; /* source and dest address */ ;,TCP报文头,struct sniff_tcp u_short th_sport; /* source port */u_short th_dport; /* destination port */tcp_seq th_seq; /* sequence number */tcp_seq th_ack; /* acknowledgement number */#if BYTE_ORDER = LITTLE_ENDIANu_int th_x2:4, /* (unused) */th_off:4; /* data offs
20、et */#endif#if BYTE_ORDER = BIG_ENDIANu_int th_off:4, /* data offset */th_x2:4; /* (unused) */#endifu_char th_flags;#define TH_FIN 0x01#define TH_SYN 0x02#define TH_RST 0x04#define TH_PUSH 0x08#define TH_ACK 0x10#define TH_URG 0x20#define TH_ECE 0x40#define TH_CWR 0x80#define TH_FLAGS (TH_FIN|TH_SYN
21、|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)u_short th_win; /* window */u_short th_sum; /* checksum */u_short th_urp; /* urgent pointer */ ;,获取包数据,struct sniff_ethernet* ethernet; struct sniff_ip* ip; struct sniff_tcp* tcp;ethernet = (struct sniff_ethernet*)(packet); ip = (struct sniff_ip*)(packet + size_et
22、hernet); tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip); payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);,pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) handler_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);,处理离线数据包文件,
23、WinPcap提供了大批函数,用于将网络数据保存到文 件和将储存文件读出。文件格式和libpcap保存的是一样的。这种格式比较简 单,用二进制格式保存捕获的数据包的数据,它也是很 多网络工具例如WinDump,Ethereal和Snort等使用的标 准。这里的函数包括pcap_dump_open()、pcap_dump()、 pcap_open_offline()等。,将数据包存入文件,/主程序 pcap_dumper_t * dumpfile; dumpfile = pcap_dump_open(adhandle, argv1); /打开一个存储文件并将它和接口联系起来if(dumpfile
24、=NULL) fprintf(stderr,“nError opening output filen“);return -1; / pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile); /packet_handler void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)/* save the packet on the dump file */ pcap_dump(dumpfil
25、e, header, pkt_data); ,从保存文件中读取数据包,while(res = pcap_next_ex( fp, &header, &pkt_data) = 0) /处理代码 /主程序中,pcap_loop(fp, 0, dispatcher_handler, NULL);/主程序中 void dispatcher_handler(u_char *temp1, const struct pcap_pkthdr *header, const u_char *pkt_data) /处理代码 ,第一步:打开离线数据文件,第二步:读取离线数据(一)使用回调函数(二)不使用回调函数,if
26、 ( (fp = pcap_open_offline(argv1, errbuf) ) = NULL) fprintf(stderr,“nError opening dump filen“);return -1; ,发送数据包,pcap_sendpacket发送单个数据包 发送队列(查看winpcap手册),pcap_sendpacket发送单个数据包,打开适配器后,调用pcap_sendpacket()函数 来发送一个手写的数据包。pcap_sendpacket()用一个包含要发送的数据 的缓冲区、该缓冲区的长度和发送它的适配器作 为参数。注意该缓冲区是不经任何处理向外发出 的,这意味着,如
27、果想发些有用的东西的话,应 用程序必须产生正确的协议头。,u_char packet100; if(fp = pcap_open_live(argv1, 100, 1, 1000, error) ) = NULL) fprintf(stderr,“nError opening adapter: %sn“, error);return; /* Supposing to be on ethernet, set mac destination to 1:1:1:1:1:1 */packet05=1; /* set mac source to 2:2:2:2:2:2 */packet611=2;/*
28、Fill the rest of the packet */for(i=12;i100;i+)packeti=i%256; /* Send down the packet */pcap_sendpacket ( fp, packet, 100);,获得网络流量统计数字,统计的内容:最后一个时间间隔内的数据包的数目和接收 的比特数为了用这个功能来监视网络,程序员必须打开适配器并 将它设置为统计模式。这可以通过调用pcap_setmode() 完成,此时这个函数的mode参数应该为MODE_STAT。,获得网络流量统计数字,统计模式需要的数据拷贝和内容交换最少,所以CPU的 利用率比较高。并且所需
29、要的内存很少。注意:开始激活统计模式之前,用户可以设置过滤器,定义监视网络流量的一部分。如果没有设置过虑器,整个流量都要被监视。,struct timeval st_ts; fp = pcap_open_live(argv1, 100, 1, 1000, error); pcap_setmode(fp, MODE_STAT); pcap_loop(fp, 0, dispatcher_handler, (PUCHAR),注意:此处dispatch_handler每次回调时填入的第二个和第三个参数,前者还是和驱动有关的抓包头部,而后者指向封装了接收包数目和接收字节数目的数据区,/计算每秒获取的二进制位数 Bps.QuadPart=(*(LONGLONG*)(pkt_data + 8) * 8 * 1000000) / (delay);/* 计算每秒获取的包数 */Pps.QuadPart=(*(LONGLONG*)(pkt_data) * 1000000) / (delay); /打印时间戳ltime=localtime( ,例:应用程序模块及工作流程,