基于WinPcap的网络数据包捕获与分析报告.docx
《基于WinPcap的网络数据包捕获与分析报告.docx》由会员分享,可在线阅读,更多相关《基于WinPcap的网络数据包捕获与分析报告.docx(21页珍藏版)》请在冰豆网上搜索。
基于WinPcap的网络数据包捕获与分析报告
基于WinPcap的网络数据包捕获与分析
一、WinPcap介绍
1.WinPcap简介
WinPcap是一个在Windows操作系统下的免费、公开的用于直接访问网络的开发工具包(编程API)。
大多数Windows网络应用程序都是通过WinsockAPI(Windows套接口)这类高级编程接口访问网络的。
这种方法允许在网络上进行简单的数据传送,因为操作系统的TCP/IP协议栈实现软件会处理底层细节(协议操作、流程重组等等),并提供一个类似于读写文件的函数接口。
然而,有时候“简便方法”并不能满足实际需要。
有些程序希望绕过TCP/IP协议栈,直接处理底层网络中的通信数据,它们需要对网络进行底层进行直接访问,即在没有类似协议栈(TCP/IP协议栈)的实体介入条件下对网络进行原始访问。
基于WinsockAPI编程,应用程序是通过调用操作系统提供的编程接口访问TCP/IP协议栈实现网络通信的。
基于WinPcap编程,网络程序实际上是绕开操作系统的TCP/IP协议栈直接通过底层网络发送数据,因此,网络程序可以实现一些更低级、更灵活的功能。
2.WinPcap的组成与结构
如图1.1,WinPcap由一个数据包监听设备驱动程序(NPF)、一个底层的动态连接库(packet.dll)和一个高层的不依赖于操作系统的静态库(wpcap.dll)共三个部分构成。
这里,NPF在操作系统的核级,packet.dll、wpcap.dll在用户级。
图1.1WinPcap的组成和结构
应用程序
wpcap.dll
packet.dll
NPF
DeviceDriver
用户层
核心层
网络层
数据包
1)数据包监听设备驱动程序
技术实现上,为了实现抓包,系统必须绕过操作系统的协议栈来访问在网络上传输的原始数据包(rawpacket)。
这就要求WinPcap的一部分运行在操作系统核心部,直接与网络接口驱动交互。
由于这个部分是系统依赖(systemdependent)的,在Winpcap的解决方案中它被视为是一个设备驱动,称作NPF(NetgroupPacketFilter)。
2)底层的动态连接库(packet.dll)和高层静态库(wpcap.dll)
为了方便编程,WinPcap必须提供一个编程接口(API),这就是WinPcap的底层的动态连接库(packet.dll)和高层静态库(wpcap.dll)。
这里,packet.dll提供了一个底层API,伴随着一个独立于Microsoft操作系统的编程接口,这些API可以直接用来访问驱动的函数;wpcap.dll导出了一组更强大的与libpcap一致的高层抓包函数库(captureprimitives),这些函数使得数据包的捕获以一种与网络硬件和操作系统无关的方式进行。
底层动态库运行在用户层,它将应用程序和数据包监听设备驱动程序隔离开来,使得应用程序可以不加修改地在不同的WINDOWS系统上运行。
高级的静态库和应用程序编译在一起,它使用低级动态库提供的服务,向应用程序提供完善的监听接口。
3.WinPcap的基本原理
抓包是WinPcap的基本功能,也是NPF最重要的操作。
在抓包的时候,驱动(例如NICDriver)使用一个网络接口监视着数据包,并将这些数据包完整无缺地投递给用户级应用程序。
如图1.4,WinPcap的NPF抓包主要依靠两个组件。
1)数据包过滤器(filter)。
数据包过滤器决定是否接收进来的数据包并把数据包拷贝给监听程序。
数据包过滤器是一个有布尔输出的函数。
如果函数值是true,抓包驱动拷贝数据包给应用程序;如果是false,数据包将被丢弃。
NPF数据包过滤器更复杂一些,因为它不仅决定数据包是否应该被保存,而且还决定要保存的字节数。
被NPF驱动采用的过滤系统来源于BSDPacketFilter(BPF),一个虚拟处理器可以执行伪汇编书写的用户级过滤程序。
应用程序采用用户自定义的过滤器并使用wpcap.dll将它们编译进BPF程序。
然后,应用程序使用BIOCSETFIOCTL写入核心态的过滤器。
这样,对于每一个到来的数据包该程序都将被执行,而满足条件的数据包将被接收。
与传统解决方案不同,NPF不解释(interpret)过滤器,而是执行(execute)它。
由于性能的原因,在使用过滤器前,NPF提供一个JIT编译器将它转化成本地的80x86函数。
当一个数据包被捕获,NPF调用这个本地函数而不是调用过滤器解释器,这使得处理过程相当快。
2)循环缓冲区(Buffer)。
NPF的循环缓冲区用来保存数据包以免丢失(如果一个包符合过滤器的要求,就被复制到循环缓冲区)。
一个保存在缓冲区中的数据包有一个头,它包含了一些主要的信息,例如时间戳和数据包的大小,注意:
它不是协议头。
另外,循环缓冲区以队列插入的方式来保存数据包,提高数据的存储效率。
程序员可以以组的方式将数据包从NPF缓冲区拷贝到应用程序,这样就提高了性能,因为它降低了读的次数。
如果一个数据包到来的时候缓冲区已经满了,那么该数据包将被丢弃,这时就发生了丢包现象。
3)NetworkTap是一个用于探听网络中所有数据流的函数。
4)数据统计
如图1.4,为了提高数据处理的速度,WinPcap将统计和监听功能移到核中,这样避免了将任何数据都传递给用户。
WinPcap通过使用从NPF中得到的过滤器来执行一个核级的可编统计模块,这使其变成一个强大的分级引擎,而不只是个简单的包过滤器。
应用程序可以构造这个模块来监听网络活动的任意方面(例如:
网络负荷、两台主机间的流量、每秒web请求的次数等等),并在预定的时间间隔接收核传来的数据。
图1.4Wincap的内部结构和原理
基于Winpcap
的监控程序
packet.dll
wpcap.dll
NICDriver
(NDIS3.0或更高)
核心层
网络层
数据包
基于Winpcap
的应用程序1
Filter1
Filter2
Filter3
…
Buffer1
Buffer2
统计引擎
NetworkTap
TCP/IP协议栈
其他协议栈实现
User-Buffer1
User-Buffer2
基于Winpcap
的应用程序2
NPF
调用packet.dll
API的程序
直接访问
NPF的程序
用户层
统计模式避免了复制数据包并且执行0-copy机制(当包仍存放在NIC(网络接口卡)驱动的存中时开始进行统计,随后丢弃这个包)。
而且,环境转换的次数可以保持最低,这是因为结果通过一次系统调用就可以返回给用户。
它不需要缓冲区(核或用户),因此当监听开始时不用为它分配存。
可见,统计模式是一种很有效的网络监听方式,在高速网络中利用libpcap来工作也没任何问题。
WinPcap为程序员提供了一套系统调用和高层函数来进行网络监听,这使得已经知道libpcapAPI的程序员能很容易使用。
5)构造数据包
BPF和NPF都提供了构造包的函数,使用户可以将原始数据包发送到网络中。
然而,Unix程序员一般不用libpcap提供的这些函数,因为在Unix平台上,应用程序可以使用原始套接字来发送伪造的数据包。
在Windows环境下,只有Windows2000提供了原始套接字,而且非常有限。
因此在Windows环境下,WinPcap就成为首选的构造数据包的函数库,它提供了一套标准稳定的函数。
另外,NPF增加了一些新的函数,这些函数可以使数据包通过一次用户和核模式之间的转换就发送几次。
数据复制到核中,然后通过调用一次NDIS将包发送到网络中。
尽管WinPcap提供了一套新的函数来开发这些特性,但它没有提供那些强大的创建数据包的抽象函数,这需要通过其它现有的工具来实现。
程序员可以利用著名的LibnetPacketAssemblyLibrary的Windows版本实现,这个函数库增加了数据包结构层并在WinPcap上构造数据包。
二、基于Winpcap的数据包捕获与分析程序开发流程
1.程序分析
本课程设计采用VC++,基于应用程序Winpcap来实现数据包的捕获与分析。
界面采用MFC实现一个单文档的程序,用户区分为左右两个视图,左边视图是一个列表,显示捕获数据包的简要信息,右边视图是一个树形图,显示选中数据包的详细信息。
由菜单项中的按钮触发操作,同时改进了程序自带的保存、另存为等图标,成功加上了自己的图标,并与按钮ID相匹配。
这个程序基本实现了预期功能,下面是程序开发的过程。
2.建立工程
在VC++6.0下创建一个单文档的MFC应用程序,工程名:
Sniffer,如图2.1->图2.2->图2.3。
图2.1建立工程
图2.2选中单文档
图2.3自动生成的类列表
到.winpcap.org/devel.htm下载WinPcap4.0.2.zip,然后解压,解压缩就可以看见Include和lib;在"Project->Settings"标签栏中选择"C/C++",在"Preprocessordefinitions"的输入框里添加"WPCAP";再选择"Link",在"Object/librarymodules"的输入框里添加"wpcap.lib"。
然后再设置VC++环境变量:
选择Tools->options->Directories的include里面加入下载的winpcap开发包解压以后的include文件夹。
选择Tools->options->Directories的lib里面加入下载的winpcap开发包解压以后的lib文件夹。
3.界面设计
工程建好了,下面进行界面的设计:
首先,对菜单栏进行修改,去掉原来单文档所自带的“文件”、“编辑”菜单选项,保留“查看”和“帮助”,新建“文件”,下面有“开始抓包(ID_FILE_START)”、“停止抓包(ID_FILE_STOP)”、“退出(ID_APP_EXIT)”三个菜单选项。
再建“适配器”菜单,下面有“选择适配器(ID_ADP_CHOOSE)”选项,并对每个新建的选项进行注释说明。
如图2.4:
图2.4菜单栏
然后,打开Sniffer.rc文件,对工具栏进行修改。
自做了一个工具栏图片Toolbar1.bmp来代替原来工程的Toolbar.bmp,然后将多余的复制粘贴等工具按钮信息删除掉,回到界面处,再对每个按钮图标进行ID设置。
如图:
,从左向右依次是:
开始抓包、停止抓包、选择适配器、帮助图标按钮。
最后,新建一个选择适配器的基本对话框,ID标识为IDD_ADP_DIALOG,双击此对话框新建一个类:
CAdpterDlg。
在此基本对话框上拖入一个TreeContrl控件和ListContrl控件,对控件属性界面进行调整,打开类向导,将TreeContrl成员变量设置为:
m_treeCtrl,ListContrl成员变量设置为m_listCtrl。
如图2.5:
图2.5选择适配器对话框
图2.6类向导设置变量
4.代码编写
1)对菜单中的按钮项分别建立类向导、增加函数,如“选择适配器”的消息映射关系为ON_COMMAND(ID_ADP_CHOOSE,OnAdpChoose),“开始抓包”的消息映射关系为ON_COMMAND(ID_FILE_START,OnFileStart);“停止抓包”的消息映射关系为ON_COMMAND(ID_FILE_STOP,OnFileStop)以及菜单项是否禁用的菜单项的状态的响应消息映射关系。
这些都是在CMainFrame类里面定义或声明的,CMainFrame作为一个主控类,当操作时,其调用其他的对话框类、视图类,然后再调用报文类。
2)网卡的绑定
1为ID_ADP_CHOOSE选择适配器建立类向导,新建函数:
OnAdpChoose(),打开适配器对话框,并对网卡链表进行传递。
具体代码如下:
voidCMainFrame:
:
OnAdpChoose()
{CAdpterDlgadp;//为适配器对话框初始化值
adp.mainFrm=this;
adp.alldevs=this->alldevs;
adp.dev=this->dev;
if(adp.DoModal()==IDOK){//将适配器信息返回
this->alldevs=adp.alldevs;
this->dev=adp.dev;
}}
2适配器对话框的数据初始化:
在类向导中为类CAdpterDlg添加WM_INITDIALOG消息,并添加默认函数:
OnInitDialog,点击“EditCode”对函数进行编辑。
Pcap提供cap_findAlldevs()这个函数来实现些功能,返回一个pcap_if结构的链表,链表的每项容都含有全面的网卡信息,网卡名称和网卡描述,特别是pcap_findalldevs()这个函数返回的每个pcap_if结构体都同包含一个pcap_addr结构的列表,它包含:
一个地址列表,一个掩码列表,一个广播地址列表和一个目的地址列表。
3为TreeContrl建立类向导,增加OnClickAdpTree(),实现双击网卡,在ListContrl中显示网卡具体信息,要注意的是显示网卡详细信息时,调用DeleteAllItems()删除原来容。
3)捕获包并显示数据包的简要信息
规划捕获包和分析数据包的界面,我准备将视图分为左右两部分,左边为一个ListView,右边为一个TreeView。
所以新建两个类CapPackView(继承CListView)、ProTreeView(继承CTreeView)。
CapPackView负责显示所有抓到的包的简要信息,ProTreeView负责显示选中的包的具体信息。
在主窗口按下开始抓包按钮时,启动捕获包线程ReceivePacket,调用pcap_open_live(dev->name,65536,1,1000,errbuf))打开网卡,在这里第三个参数设置为1代表将适配器设置为混杂模式,捕获所有流经此适配器的数据包。
网卡一旦打开,就可以调用pcap_loop()进行数据的捕获。
每次捕获到数据包时,libpcap都会自动调用回调函数pcap_handle(),在回调函数里,PostMessage()向CapPackView发送消息,通知CapPackView处理收到的包。
在这里,使用的是自定义消息来实现的,即首先自定义消息#defineWM_MESSAGE_PACKET_RECEIVEWM_USER+1,类CCapPackView头文件中声明消息的数据包处理函数:
afx_msgvoidOnPacketReceive(conststructpcap_pkthdr*pkt_header,constu_char*pkt_data)。
CapPackView收到消息,调用OnPacketReceive()将将所捕获包的容复制下来,并保存到CArray数组中,然后对包的类型进行判断,当为IP数据包时,调用类IPGram中的函数GetSrcAddr()(获取IP数据包的源IP地址),GetDestAddr()(获取IP数据包的目的IP地址),ip.GetService()(得到IP数据包的协议类型),将这些信息及数据包的长度等显示到界面。
类似的,如果数据包为ARP或RARP类型,则相应的调用类ARPGram、RARPGram中的GetSrcPrtAddr()、GetDestPrtAddr(),GetSrcPrtAddr()、GetDestPrtAddr()等函数,并将信息逐行显示于界面。
4)选中CapPackView中的某条信息,将数据包的具体信息显示于ProTreeView中。
定义响应操作的消息#defineWM_MESSAGE_PACKET_SELECTWM_USER+2,在类CProTreeView头文件中声明消息的处理函数:
afx_msgvoidOnPacketSelect(conststructpcap_pkthdr*pkt_header,constu_char*pkt_data);在类CCpPackView中,调用PostMessage()通知ProTreeView刷新为新选中的报文容。
类CProTreeView接收到消息后,调用OnPacketSelect(),对数据包进行分析,首先解析以太帧,调用类EtherHead中的函数GetMacDestAddr()(获取目的MAC地址)、GetMacSrcAddr()(获取源MAC地址)。
然后根据数据包类型的不同,调用IP数据包、ARP数据包、RARP数据包处理类IPGram、ARPGram、RARPGram中的函数,逐行插入数据包的具体信息。
5)需要停止抓包时,点击菜单项或图标按钮停止抓包。
只需要添加消息处理函数OnFileStop(),设置一下开始抓包或停止抓包状态。
6)完善程序。
对一些可能出现的情况进行错误处理,如选择网卡时,抓包是时按钮状态是否禁用等等。
到此,基本上完成了程序所需要的所有功能。
三、主要建立的类及调用流程
本程序中,主要有四种类型的类,主控类:
CMainFrame,视图类:
CCapPackView、CProTreeView,对话框类:
CAdpterDlg以及报文类。
图3.1为整体的类之间的调用流程图。
图3.1总体类调用流程图
1.CMainFrame类
CMainFrame,是用于主控的类,它起到分区、处理消息、传递消息的功能。
将用户区分为两个视图,点击菜单里的按钮,触发打开适配器对话框或者开始抓包、停止抓包的动作。
表3.1CMainFrame类说明
CMainFrame类
父类
CFrameWnd类
成员函数
参数
功能
OnCreateClient()
LPCREATESTRUCTlpcs,CCreateContext*pContext
将用户区分为左右两个视图ProTreeView、CapPackView
OnAdpChoose()
消息处理函数
无
为适配器对话框初始化值,即点击选中适配器按钮弹出适配器对话框
OnFileStart()
消息处理函数
无
设置抓包状态,释放所有包,清空容,启动收包线程,开始捕获包。
OnFileStop()
消息处理函数
无
停止抓包
OnUpdateFileStart()
消息处理函数
CCmdUI*pCmdUI
当在捕获包时,禁用“抓包”按钮
OnUpdateFileStop()
消息处理函数
CCmdUI*pCmdUI
当停止捕获包时,禁用“停止”按钮
成员变量
类型
含义
*PackView
CCapPackView
显示所有抓到的包的简要信息
*ProTreeView
CProTreeView
显示选中的包的具体信息,用解析树表达
*alldevs
pcap_if_t
适配器链表
*dev
pcap_if_t
适配器链表
isStart
bool
是否开始抓包
isStop
bool
是否停止抓包
图3.2为CMainFrame类中方法调用流程图。
图3.2CMainFrame类中方法调用流程图
2.CAdpterDlg类
CAdpterDlg:
对话框类,此对话框分为两部分:
树形控件和list控件。
当对话框被触发后,树形控件显示了本机所有的适配器列表,双击选中网卡,list控件显示其名称、描述、子网掩码、IP地址等信息。
点击确定按钮即绑定了此网卡,注意虚拟网卡是不能被绑定的。
表3.2为类CAdpterDlg的成员函数及成员变量的说明。
表3.2CAdpterDlg类说明
CAdpterDlg类
父类
CDialog类
成员函数
参数
功能
OnInitDialog()
公有
无
初始化树形控件,设置list控件,读取适配器并将适配器列表显示在树形控件中。
当网卡绑定以后再次打开此对话框,直接显示其信息。
OnClickAdpTree()
消息处理函数
NMHDR*pNMHDR,LRESULT*pResult
当双击选中网卡时,找到选中的网卡,设置list控件,并显示网卡名称、描述等信息。
OnOK()
消息处理函数
无
对选中的网卡进行判断,不可以选虚拟网卡也不可不选。
OnCancel()
消息处理函数
无
关闭对话框
成员变量
类型
含义
*dev
pcap_if_t
适配器链表
*alldevs
pcap_if_t
适配器链表
errbuf
[PCAP_ERRBUF_SIZE]
char
存储错误信息的字符串
CAdpterDlg类中函数调用流程图如图3.3所示。
图3.3CAdpterDlg类中函数调用流程图
3.CCapPackView类
CCapPackView,视图类,主要功能是实现接收从类MainFrm传递过来的消息和报文,将数据包的头部及数据包的数据容分别保存在CArray数组中,然后调用报文类IPGram、ARPGram、RARPGram中的函数,将数据包简要信息显示于界面。
表3.3为CCapPackView的成员函数及成员变量的说明。
图3.4为其类中方法调用流程图。
表3.3CCapPackView类说明
CCapPackView类
父类
CListView类
成员函数
参数
功能
OnInitialUpdate()
公有
无
设置CapPackView视图的视图风格,设置其列信息。
OnPacketReceive()
消息处理函数
pcap_pkthdr*pkt_header,
constu_char*pkt_data
显示所有抓到的包的简要信息
OnItemchanged()
消息处理函数
NMHDR*pNMHDR,LRESULT*pResult
当点击列表中某行信息,将向ProTreeView发送消息,并传递报文信息。
成员变量
类型
含义
index
int
序号
totalmem
int
收到的报文占用总字节数
time
int
流逝的秒数
delta
int
一秒收到的字节数
图3.4CCapPackView类中函数调用流程图
4.CProTreeView类
CProTreeView,视图类,主要功能是接收来自于CapPackView的消息,调用消息处理函数OnPacketSelect(),在显示数据前清空列表及捕获的数据包。
对传递过来的报文头部和容进行分析,调用报文类IPGram、ARPGram、RARPGram中的函数将具体信息显示于树形列表中。
表3.4为CProTreeView的成员函数及成员变量的说明。
表3.4CProTreeView类说明
CProTreeView类
父类
CTreeView类
成员函数
参数
功能
OnInitialUpdate()
无
设置ProTreeView视图的视图风格,解析树。
OnPacketSelect()
消息处理函数
pcap_pkthdr*pkt_header,
constu_char*pkt_data
(报文头部和容指针)
以树形结构显示选中的包的具体信息。
OnClick()
NMHDR*pNMHDR,LRESULT*pResult
无
成员变量
类型