基于WinPcap的程序开发.docx
《基于WinPcap的程序开发.docx》由会员分享,可在线阅读,更多相关《基于WinPcap的程序开发.docx(16页珍藏版)》请在冰豆网上搜索。
![基于WinPcap的程序开发.docx](https://file1.bdocx.com/fileroot1/2022-12/16/d155daf8-8b8b-409c-923c-698f2e58b095/d155daf8-8b8b-409c-923c-698f2e58b0951.gif)
基于WinPcap的程序开发
基于WinPcap的程序开发
一、WinPcap介绍
1、WinPcap简介
大多数Unix操作系统提供了一套允许应用程序直接与网络相互联系的系统调用。
这些指令对于那些需要通过网络捕获连续的包数据而不用内核进行过多的干预的包捕获应用程序非常有用。
在Windows环境下,WinPcap就是这样一类工具。
WinPcap是一个在Windows操作系统下的免费、公开的用于直接访问网络的开发工具包(编程API)。
大多数Windows网络应用程序都是通过WinsockAPI(Windows套接口)这类高级编程接口访问网络的。
这种方法允许在网络上进行简单的数据传送,因为操作系统的TCP/IP协议栈实现软件会处理底层细节(协议操作、流程重组等等),并提供一个类似于读写文件的函数接口。
然而,有时候“简便方法”并不能满足实际需要。
有些程序希望绕过TCP/IP协议栈,直接处理底层网络中的通信数据,它们需要对网络进行底层进行直接访问,即在没有类似协议栈(TCP/IP协议栈)的实体介入条件下对网络进行原始访问。
基于WinsockAPI编程,应用程序是通过调用操作系统提供的编程接口访问TCP/IP协议栈实现网络通信的。
基于WinPcap编程,网络程序实际上是绕开操作系统的TCP/IP协议栈直接通过底层网络发送数据,因此,网络程序可以实现一些更低级、更灵活的功能。
2、WinPcap的目的和用途
开发WinPcap的目的是为Win32应用程序提供一种直接访问底层网络的能力。
通过WinPcap,网络应用程序可以实现如下功能:
1)捕获原始数据包,包括发送到本主机以及在共享网络上的数据包。
2)数据过滤。
在将数据包发送给应用程序之前按照用户的规定对捕获的数据包进行过滤。
3)发送原始数据包。
向网络发送原始数据包。
4)数据包统计。
对网络通信进行统计。
上述功能都通过一个设备驱动(这个驱动程序安装在Win32内核的网络部分)和一组动态连接库(DLL)获得。
所有这些功能部件都通过一个强大的编程接口来实现,应用程序能易于开发并且能移植到各种操作系统中。
3、基于WinPcap可以开发的网络应用程序
基于WinPcap可以开发很多网络应用程序,典型的包括:
5)网络和协议分析软件
6)网络监听软件
7)网络通信量记录软件
8)网络数据生成软件
9)用户机网桥和路由器
10)网络入侵探测系统
11)网络扫描软件
12)网络安全工具
4、WinPcap的局限性
WinPcap可以独立于主机的协议(如TCP/IP协议)进行接收和发送数据包。
这意味着WinPcap不能阻塞、过滤或处理本机上其它程序产生的数据。
它仅仅能嗅探在网线上传输的数据包。
因此,WinPcap不能在trafficshapers、QoSschedulers和个人防火墙这类应用程序中使用。
5、WinPcap的开发和运行环境
目前,winpcap主要的开发和运行环境是windowsNT/2000/XP。
由于winpcap的用户中只有很小一部分使用windows95/98/Me,并且微软也已经放弃了对win9x的开发和支持。
实际上,winpcap中的面向9x系统的概念和NT系统的非常相似,只是在某些实现上有点差异,比如说9x只支持ANSI编码,而NT系统则提倡使用Unicode编码。
简单来讲,WinPcap典型的开发和运行环境是windowsNT/2000/XP。
当然,早期的WinPcap也支持windows95/98/Me,但是,我们不推荐在windows95/98/Me下开发和运行基于WinPcap的网络应用。
二、WinPcap的体系结构分析
1、WinPcap的组成与结构
如图2.1,WinPcap由一个数据包监听设备驱动程序(NPF)、一个底层的动态连接库(packet.dll)和一个高层的不依赖于操作系统的静态库(wpcap.dll)共三个部分构成。
这里,NPF在操作系统的内核级,packet.dll、wpcap.dll在用户级。
1)数据包监听设备驱动程序
技术实现上,为了实现抓包,系统必须绕过操作系统的协议栈来访问在网络上传输的原始数据包(rawpacket)。
这就要求WinPcap的一部分运行在操作系统核心内部,直接与网络接口驱动交互。
由于这个部分是系统依赖(systemdependent)的,在Winpcap的解决方案中它被视为是一个设备驱动,称作NPF(NetgroupPacketFilter)。
Winpcap开发小组针对Windows95,Windows98,WindowsME,WindowsNT4,Windows2000和WindowsXP提供了不同版本的驱动(在Windows95/98/ME中是VXD文件,在WindowsNT/2000中是SYS文件)。
这些驱动不仅提供了基本的特性(例如抓包、发送原始数据包——注入数据包),还有更高级的特性(例如可编程的过滤器系统和监视引擎)。
前者可以被用来约束一个抓包会话只针对网络通信中的一个子集(例如,只捕获特殊主机产生的ftp通信数据包),后者提供了一个强大而简单的统计网络通信量的机制(例如,获得网络负载或两个主机间的数据交换量)。
概括地讲,数据包监听设备驱动程序直接从数据链路层抓取网络数据包并过滤,将数据包不加修改地传递给运行在用户层的应用程序。
它在不同的WINDOWS系统下是不同。
数据包监听设备驱动程序支持BPF过滤机制,可以灵活地设置过滤规则。
2)底层的动态连接库(packet.dll)和高层静态库(wpcap.dll)
为了方便编程,WinPcap必须提供一个编程接口(API),这就是WinPcap的底层的动态连接库(packet.dll)和高层静态库(wpcap.dll)。
这里,packet.dll提供了一个底层API,伴随着一个独立于Microsoft操作系统的编程接口,这些API可以直接用来访问驱动的函数;wpcap.dll导出了一组更强大的与libpcap一致的高层抓包函数库(captureprimitives),这些函数使得数据包的捕获以一种与网络硬件和操作系统无关的方式进行。
底层动态链接库运行在用户层,它将应用程序和数据包监听设备驱动程序隔离开来,使得应用程序可以不加修改地在不同的WINDOWS系统上运行。
高级的静态链接库和应用程序编译在一起,它使用低级动态链接库提供的服务,向应用程序提供完善的监听接口。
概括来讲,WinPcap包含了一个最优化的内核模式驱动——称作NetgroupPacketFilter(NPF),和一套与libpcap兼容的用户级函数库。
WinPcap使Unix平台下的应用程序能方便的移植到Win32平台下,并且它能使一套很大的Unix函数库只需通过简单的重新编译就立刻在Win32平台下使用。
而且,由于网络监听的重要性,WinPcap还为此提供了特殊的系统调用函数。
实际上,Windows环境下的WinPcap与Unix环境下的BerkeleyPacketFilter(BPF)在体系架构上是基本一致的。
在Unix环境下,程序员只需要了解Libpcap提供的编程接口即可,Libpcap将应用程序与操作系统内核之间的相互作用隐藏起来,提供了一套与用户程序联系的函数和强大的捕获数据包的抽象接口。
在Windows环境下,程序员主要基于wpcap.dll提供的编程接口开发应用程序。
当然,程序要也可以使用packet.dll、NPF提供的编程接口,只是后者提供的API更加底层。
2、WinPcap的技术细节
1)NPF驱动
网络数据包过滤器(NetgroupPacketFilter,简称NPF)是Winpcap的核心部分,它是Winpcap完成困难工作的核心组件。
NPF负责处理网络上传输的数据包,并向上层提供各种服务,包括数据包的捕获(capture)、发送(injection)和分析性能(analysiscapabilities)。
2)NPF和NDIS
NDIS(NetworkDriverInterfaceSpecification)是Windows环境下的一个定义网络适配器(或者说成是管理网络适配器的驱动程序)与协议驱动(例如TCP/IP的实现)之间通信的规范,如图2.2。
基于NDIS,顶层应用(例如TCP/IP协议)可以发送和接收网络(LAN或WAN)上的数据包而不必关心特定的适配器硬件或特定的Win32操作系统版本。
NDIS支持三种类型的网络驱动:
①网络接口卡或NIC驱动(NetworkinterfacecardorNICdrivers)。
NIC驱动直接管理着网络接口卡(NIC)。
NIC驱动接下边与硬件连接,从上边表现为一个接口,该接口允许高层发送数据包到网络上,处理中断,重置NIC,停止NIC,查询和设置驱动的运行特征。
NIC驱动可以是小端口(miniport)或完全的NIC驱动(fullNICdriver)。
Miniport驱动仅仅实现了管理NIC的必要操作,包括在NIC上发送和接收数据。
对于所有最底层的NIC驱动的操作由NDIS提供,例如同步(synchronization)。
小端口(miniport)不直接调用操作系统函数,它们对于操作系统的接口是NDIS。
小端口仅仅是向上传递数据包给NDIS并且NDIS确保这些数据包被传递给正确的协议。
完全NIC驱动(FullNICdriver)完成硬件细节的操作和所有由NDIS完成的同步和查询操作。
例如,完全NIC驱动维持接收到的数据的绑定信息。
②中间层驱动(Intermediatedrivers)中间层驱动位于高层驱动(例如协议驱动)和小端口之间。
对于高层驱动,中间层驱动看起来像是小端口;对于小端口,中间层驱动看起来像协议驱动。
一个中间层协议驱动可以位于另一个中间层驱动之上,尽管这种分层可能对系统性能带来负面影响。
开发中间层驱动的一个关键原因是在现存的遗留协议驱动(legacyprotocoldriver)和小端口之间形成媒体的转化。
例如,中间层驱动可以将LAN协议转换成ATM协议。
中间层驱动不能与用户模式的应用程序通信,但可以与其他的NDIS驱动通信。
③传输驱动或协议驱动(Transportdriversorprotocoldrivers)协议驱动实现了网络协议栈,例如IPX/SPX或TCP/IP,在一个或多个网络接口卡上提供它的服务。
在协议驱动的上面,它为应用层客户程序服务;在它的下面,它与一个或多个NIC驱动或中间层NDIS驱动连接。
3)NPF在Windows系统中的位置
如图2.3,NPF是一个协议驱动。
从性能方面来看,这不是最好的选择,但是它合理地独立于MAC层并且有权使用原始通信(rawtraffic)。
可以看出,NPF在NDIS之上,它与TCP/IP协议栈的实现在同一层次。
因此,应用程序不通过Winsock也可以实现数据的发送和接受。
3、WinPcap的基本原理
抓包是WinPcap的基本功能,也是NPF最重要的操作。
在抓包的时候,驱动(例如NICDriver)使用一个网络接口监视着数据包,并将这些数据包完整无缺地投递给用户级应用程序。
如图2.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)数据统计
如图2.4,为了提高数据处理的速度,WinPcap将统计和监听功能移到内核中,这样避免了将任何数据都传递给用户。
WinPcap通过使用从NPF中得到的过滤器来执行一个内核级的可编统计模块,这使其变成一个强大的分级引擎,而不只是个简单的包过滤器。
应用程序可以构造这个模块来监听网络活动的任意方面(例如:
网络负荷、两台主机间的流量、每秒web请求的次数等等),并在预定的时间间隔内接收内核传来的数据。
统计模式避免了复制数据包并且执行0-copy机制(当包仍存放在NIC(网络接口卡)驱动的内存中时开始进行统计,随后丢弃这个包)。
而且,环境转换的次数可以保持最低,这是因为结果通过一次系统调用就可以返回给用户。
它不需要缓冲区(内核或用户),因此当监听开始时不用为它分配内存。
可见,统计模式是一种很有效的网络监听方式,在高速网络中利用libpcap来工作也没任何问题。
WinPcap为程序员提供了一套系统调用和高层函数来进行网络监听,这使得已经知道libpcapAPI的程序员能很容易使用。
5)构造数据包
BPF和NPF都提供了构造包的函数,使用户可以将原始数据包发送到网络中。
然而,Unix程序员一般不用libpcap提供的这些函数,因为在Unix平台上,应用程序可以使用原始套接字来发送伪造的数据包。
在Windows环境下,只有Windows2000提供了原始套接字,而且非常有限。
因此在Windows环境下,WinPcap就成为首选的构造数据包的函数库,它提供了一套标准稳定的函数。
另外,NPF增加了一些新的函数,这些函数可以使数据包通过一次用户和内核模式之间的转换就发送几次。
数据复制到内核中,然后通过调用一次NDIS将包发送到网络中。
尽管WinPcap提供了一套新的函数来开发这些特性,但它没有提供那些强大的创建数据包的抽象函数,这需要通过其它现有的工具来实现。
程序员可以利用著名的LibnetPacketAssemblyLibrary的Windows版本实现,这个函数库增加了数据包结构层并在WinPcap上构造数据包。
三、基于WinPcap的网络编程
1、编程API接口的选择
如前所述,WinPcap由三部分模块组成——NPF、packet.dll和wpcap.dll。
编程方面,我们主要使用packet.dll和wpcap.dll提供的编程接口。
packet.dll提供了Win32平台下的捕获包的驱动普通接口。
实际上,不同版本的Windows都提供了不同的内核模块和应用程序编程接口。
packet.dll提供一套独立于操作系统的API来处理这些不同。
基于packet.dll编写的程序可以不经过重新编译就在各种Win32平台下实现捕获数据包。
另外,packet.dll还包含了其他一些函数,它可以进行一些底层的操作,如获取网络适配器名或动态的加载驱动程序。
通过packet.dll还可以取得一些系统信息,如主机的MAC地址、一些硬件计数,例如以太网上冲突次数等。
packet.dll和NPF都严重依赖于操作系统,并且由于Windows95/98和WindowsNT/2000之间OS结构的不同而在不同版本的操作系统上有所不同。
为了提高应用程序的可移植性,WinPcap提供了基于wpcap.dll的编程方式。
这里,wpcap.dll不依赖于操作系统,它包含了一些其它高层的函数,比如:
过滤器生成器、用户定义的缓冲区和高层特性(数据统计和构造数据包)。
因此程序员可以使用两类API:
1)直接映射到内核调用的原始函数,包含在packet.dll中;2)wpcap.dll提供的高层函数,这些函数更强大,用户更容易掌握。
wpcap.dll能自动调用packet.dll。
一个“高层”调用会被译成几个NPF系统调用。
程序员一般使用wpcap.dll,只有在为数不多的程序中才直接使用packet.dll。
2、创建第一个基于wpcap.dll的命令行应用程序
作为WinPcap编程的开始,首先我们编写一个Windows下的DOS命令行应用程序——一个简单的数据包捕获程序。
本程序可以在WinPcap开发包WpdPack_4_0_2.zip的Examples-pcap目录下找到。
源程序文件名为:
pktdump_ex。
更详细的文献可以参考:
1)WinPcap中文帮助手册.chm—>“获取设备列表”—>“使用WinPcap编程”
2)如何使用WinPcap进行编程-中国协议分析网.htm
以下是利用MicrosoftVisualC++创建应用程序的基本步骤。
1)在VC++6.0下创建一个DOS命令行程序,工程名:
MyPacketDump
2)打开main()函数,在主程序中增加如下头文件和常量定义:
#include
#defineLINE_LEN16
在main()函数中添加如下代码:
pcap_if_t*alldevs,*d;
pcap_t*fp;
u_intinum,i=0;
charerrbuf[PCAP_ERRBUF_SIZE];
intres;
structpcap_pkthdr*header;
constu_char*pkt_data;
printf("pktdump_ex:
printsthepacketsofthenetworkusingWinPcap.\n");
printf("Usage:
pktdump_ex[-ssource]\n\n"
"Examples:
\n"
"pktdump_ex-sfile.acp\n"
"pktdump_ex-s\\Device\\NPF_{C8736017-F3C3-4373-94AC-9A34B7DAD998}\n\n");
if(argc<3)
{
printf("\nNoadapterselected:
printingthedevicelist:
\n");
/*Theuserdidn'tprovideapacketsource:
Retrievethelocaldevicelist*/
if(pcap_findalldevs(&alldevs,errbuf)==-1)
{
fprintf(stderr,"Errorinpcap_findalldevs_ex:
%s\n",errbuf);
exit
(1);
}
/*Printthelist*/
for(d=alldevs;d;d=d->next)
{
printf("%d.%s\n",++i,d->name);
if(d->description)
printf("(%s)\n",d->description);
else
printf("(Nodescriptionavailable)\n");
}
if(i==0)
{
printf("\nNointerfacesfound!
MakesureWinPcapisinstalled.\n");
return-1;
}
printf("Entertheinterfacenumber(1-%d):
",i);
scanf("%d",&inum);
if(inum<1||inum>i)
{
printf("\nInterfacenumberoutofrange.\n");
/*Freethedevicelist*/
pcap_freealldevs(alldevs);
return-1;
}
/*Jumptotheselectedadapter*/
for(d=alldevs,i=0;inext,i++);
/*Opentheadapter*/
if((fp=pcap_open_live(d->name,//nameofthedevice
65536,//portionofthepackettocapture.
//65536grantsthatthewholepacketwillbecapturedonalltheMACs.
1,//promiscuousmode(nonzeromeanspromiscuous)
1000,//readtimeout
errbuf//errorbuffer
))==NULL)
{
fprintf(stderr,"\nErroropeningadapter\n");
return-1;
}
}
else
{
/*Donotcheckfortheswitchtype('-s')*/
if((fp=pcap_open_live(argv[2],//nameofthedevice
65536,//portionofthepackettocapture.
//65536grantsthatthewholepacketwillbecapturedonalltheMACs.
1,//promiscuousmode(nonzeromeanspromiscuous)
1000,//readtimeout
errbuf//errorbuffer
))==NULL)
{
fprintf(stderr,"\nErroropeningadapter\n");
return-1;
}
}
/*Readthepackets*/
while((res=pcap_next_ex(fp,&header,&p