青 岛 农 业 大 学 计算机网络综合实习
论 文 题 目 : UDP包解析软件的设计与实现 专 业 班 级 : 计本0803
姓名(学号): 周方盼(20082845)
2011年 11 月 16 日 UDP包解析软件的设计与实现 1数据包捕获原理
由于目前用的最多的网络形式是以太网,在以太网上,数据是以被称为帧的数据结构为单位进行交换的,而帧是用被称为带碰撞检测的载波侦听多址访问即CSMA/CD 的方式发送的,在这种方法中,发送到指定地址的帧实际上是发送到所有计算机的,只是如果网卡检测到经过的数据不是发往自身的,简单忽略过去而已[3]。正是这种基于CSMA/CD 的广播机制,这就给连接在网络上的计算机捕获来自于其他主机的数据带来了可能,即通过对网络接口的设置可以使网卡能够接收到所有经过该机器的数据,然后将这些数据做相应处理并实时分析这些数据的内容,进而分析网络当前状态和整体布局。从广义的角度上看,一个数据包捕获机制包含三个主要部分。首先是最底层针对特定操作系统的包捕获机制,然后是最高层针对用户程序的接口,第三部分是数据包过滤机制。不同的操作系统实现的底层包捕获机制可能是不一样的,但从形式上看大同小异。数据包常规的传输路径依次为网卡、设备驱动层、数据链路层、IP 层、传输层、最后到达应用程序。而数据包捕获机制是在数据链路层增加一个旁路处理,对发送和接收到的数据包做过滤缓冲等相关处理,最后直接传递到应用程序[4]。值得注意的是,数据包捕获机制并不影响操作系统对数据包的网络栈处理。对用户程序而言,数据包捕获机制提供了一个统一的接口,使用户程序只需要简单的调用若干函数就能获得所期望的数据包。这样一来,针对特定操作系统的捕获机制对用户透明,使用户程序有比较好的可移植性。数据包过滤机制是对所捕获到的数据包根据用户的要求进行筛选,最终只把满足过滤条件的数据包传递给用户程序。
2 数据包捕获方法 纵观国内外使用的包捕获机制的方法,大致可归纳为两类:一类是由操作系统内核提供的捕获机制;另一类是由应用软件或系统开发包通过安装包捕获驱动程序提供的捕获机制,该机制主要用于Win32 平台下的开发。操作系统提供的捕获机制主要有四种[5]:BPF、DLPI、NIT 和Sock Packet 类型套接口。BPF 由基于BSD 的Unix 系统内核所实现。DLPI 是Silaris 系统的内嵌子系统。NIT 是SunOS4系统的一部分,但在Solaris 系统中被DLPI 及NIT 所取代。Linux 核心则实现了Sock Packet的包捕获机制。从性能上看,BPF 比DLPI 及NIT 好得多,而Sock Packet 最弱。Windows操作系统没有提供内置的包捕获机制。它只提供了数量很少并且功能有限的API 调用。PCAUSA 公司提供了一个商业的产品,该产品提供了包捕获接口并且包括了一个BPF 兼容的过滤器。然而,用户接口过于低级并且该产品不提供象过滤器生成函数那样的抽象函数。WinPcap 是Win32 上的第一个用来捕获数据包的开放系统软件包,它是一种新提出的强有力并且可扩展的框架结构。WinPcap 包含了一系列以前系统所没有的创新特性。本文中将主要使用目前比较流行的WinPcap 软件包来提供捕获机制,因为它始终把性能放在首位,能支持十分苛求的应用要求[6]。由于网络适配器一般工作在数据链路层,因此所得到的报文是链路层的报文,它除了应用层的数据外,还包括数据链路层帧头、IP 报文头、以及IP 层之上的TCP 或UDP 报文头,并且应用层还有可能也定义了自己的帧结构。本文设计的捕获程序是由运行在Windows 核心层的包捕获驱动程序实现的。不使用专用的数据采集系统,而是通过网卡来捕获数据,所以必须要有一个高效的丢包率低的包捕获驱动程序。该驱动程序采用WinPcap 包捕获结构。该包捕获结构具有与网络适配卡无关的特性,并且独立于网络的具体形式,如令牌环、以太网、点到点协议PPP 等,因此适应面广。利用WinPcap 的捕获数据技术对底层数据进行捕获。首先用户应用程序向网卡设备驱动程序发出请求,由网卡设备驱动程序激活从网络拷贝每个分组的数据包,并且将它们分发到对应的应用程序[7]。捕获流程如图1 所示。图1 捕获数据流程图具体步骤如下:1、打开网卡,并设为混杂模式。2、回调函数Network Tap[10]在得到监听命令后,从网络设备驱动程序处收集数据包,把监听到的数据包负责传送给过滤程序。3、当Packet filter 监听到有数据包到达时,NDIS 中间驱动程序首先调用分组驱动程序,该程序将数据传递给每一个参与进程的分组过滤程序。4、然后由Packet filter 过滤程序决定哪些数据包应该丢弃,哪些数据包应该接收,是否需要将接收到的数据拷贝到相应的应用程序。5、通过分组过滤器后,将数据未过滤掉的数据包提交给核心缓冲区。然后等待系统缓冲区满后,再将数据包拷贝到用户缓冲区。监听程序可以直接从用户缓冲区中读取捕获的数据包。6、关闭网卡。
3 数据包捕获实现 关于数据包捕获的实现算法是十分的简单的,当网卡设置为混杂模式时,在网内的数据包都会被发送一份到网卡上。以电子邮件为例,在发送邮件连接信息和邮件头的端口号是25 端口。据此,只需要监听网络是否25 端口的数据包,如果有,则进一步判断其数据报的IP 是否是我们需要监听的计算机的IP,如果是,则检查是否有以指定IP 命名的文件夹,动态命文件名存储数据包;如果不是,一方面我们可以存储数据包,也可以丢弃[8]。程序主要函数如下:void CMainFrame::OnOptAdpater()//提取网卡信息void CMainFrame::OnUpdateOptAdpater(CCmdUI* pCmdUI)//数据包捕获开始后,禁止选择网卡void CMainFrame::OnFileStart()//开始捕获void CMainFrame::OnFileStop()//暂停捕获UINT ReceivePacket (LPVOID param)//接收数据包CListCtrl &ctrl=this->GetListCtrl();//将所捕获数据包的内容复制下来,并保存到CArray 数组中,以备将来使用在实际应用中,用户系统只关心一些特定的数据。因此高效的信息过滤机制是系统的重要组成部分,它使得用户系统可以指定特定的子网主机以及指定的协议类型,只将用户关心的数据向上层提交,从而提高系统的工作效率。WinPcap 使用了一个高效的数据包过滤机制,BPF(伯克利数据包过滤)。与应用层过滤机制相比,BPF 机制是一种内核层的过滤机制,通过它可以大幅度地提高包过滤时的性能。在过滤机制中,对于开发人员来说,最重要是要掌握它的过滤规则,因为WinPcap 就是通过调用相应的函数,来使用这些过滤规则实现对数据包实现过滤的[9]。考虑到性能的因素,在使用过滤器前,NPF 提供一个JIT 编译器将它转化成本地的80x86函数。当一个数据包被捕获,NPF 调用这个本地函数而不是调用过滤器的解释器,这使得处理过程相当快。程序主要函数如下:void CMainFrame::OnOptFilter()//设置过滤器BOOL CMainFrame::Filter(const unsigned char *pkt_data)//过滤数据包void CMainFrame::OnUpdateOptFilter(CCmdUI* pCmdUI)//数据包捕获开始后禁止设置过滤器捕获结果如图2 所示。图2 数据包捕获结果 具体代码如下:
#include "stdio.h" #include "stdlib.h" #include "iostream.h" #include "pcap.h" #include "winsock2.h" #pragma comment(lib,"ws2_32") #pragma comment(lib,"wpcap")
//以太帧头结构体 typedef struct ether_header { unsigned char ether_dhost[6]; unsigned char ether_shost[6]; unsigned short ether_type; }ETHHEADER,*PETHHEADER; //IPv4包头结构体 typedef struct ip_header { unsigned char ver_ihl; //Version (4 bits) + Internet header length (4 bits) unsigned char tos; //Type of service unsigned short tlen; //Total length unsigned short identification; //Identification unsigned short flags_fo; //Flags (3 bits) + Fragment offset (13 bits) unsigned char ttl; //Time to live unsigned char proto; //Protocol unsigned short crc; //Header checksum u_char ip_src[4]; //Source address u_char ip_dst[4]; //Destination address }IPHEADER,*PIPHEADER; //UDP包头结构体 typedef struct udp_header { u_short sport; //源端口号 u_short dport; //目的端口号 u_short len; //数据报长度 u_short crc; //校验和 }UDPHEADER,*PUDPHEADER;
void InitAdapter(); //初始化网络适配器 void dispatcher_handler(u_char*,const pcap_pkthdr*,const u_char *);//解析UDP数据包格式 void CloseAdapter(); //关闭网络适配器
FILE *fp; pcap_if_t *alldevs,*d; char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *adhandle;
void main() { int count=0,number; printf("Please enter the number of packet sended:"); scanf("%d",&number); InitAdapter(); fp=fopen("d://buhuo//udp.txt","w");