返回列表 发帖

嗅探原理

在以太网的一个冲突域中,通讯基于广播方式(应该注意的是,这里所说的广播方式,只是在共享传输介质的以
太网中,数据帧的传输是广为传播的,任何网络中的节点都可以接收的到,至于是否真正处理,还要看数据帧
的真实目的地址;而广播数据帧则是另外一个概念,其目的地址不是单个的主机MAC,而是0xFFFFFFFFFF这样的
值,表示该数据帧应该让所有的接收节点都处理),所有网络接口都可以监听到在物理介质上传输的所有的数据
帧。
正常情况下,一个网络接口(网卡)应该只响应这样的两种数据帧:
l    与自己的MAC地址相匹配的数据帧
l    发向所有机器的广播数据帧
网络中的主机系统,数据的收发是由网卡来完成的。网卡接收到传来的数据帧,其内嵌的处理程序会检查数据
帧的目的MAC地址,并根据网卡驱动程序设置的接收模式来判断该不该进一步处理。如果应该处理,就接受该数
据帧并产生中断信号通知CPU,否则就简单丢弃,因为整个过程是网卡独立完成的,与主机上的操作系统是没有
关系的。CPU得到中断信号产生中断,操作系统这才会根据网卡驱动程序设置的中断处理程序地址来调用驱动程
序接收数据,数据接收之后被放入信号堆栈,等待操作系统进一步处理。
网卡一般有四种接收模式:
l    广播模式:该模式下的网卡能够接收网络中的广播信息。
l    组播模式:该模式下的网卡能够接收组播数据。
l    直接模式:在这种模式下,只有匹配目的MAC地址的网卡才能接收该数据帧。
l    混杂模式(Promiscuous Mode):在这种模式下,网卡能够接收一切监听到的数据帧,而无论其目的MAC地
址是什么。
通常,网卡的缺省配置是同时支持前三种接收模式的。
Sniffer的基本原理在于:以太网中是基于广播方式传送数据的,所有的物理信号都会被传送到其中的每一个主
机节点;此外,网卡可以被设置成混杂接收模式(promiscuous),这种模式下,无论监听到的数据帧目的地址如
何,网卡都能够予以接收。
Sniffer的历史已经很悠久了,最初,它是做为网络管理员检测网络通信的一种工具,它即可以是软件,又可以
是一个硬件设备。作为硬件设备的Sniffer通常被称作协议分析仪,不过,其价格一般都很高昂。而作为软件的
Sniffer则方便多了,针对各种操作系统平台,都有许多种软件Sniffer,而且很多都是免费的,这对于一个想
测试网络通信流量、深度了解各种网络协议的人来说,的确是非常得力的帮手。
不过,事情往往都具有两面性,Sniffer方便之处在于它能悄无声息的监听到所有局域网内的数据通信,其潜在
的危害性也正在于此。我们知道,TCP/IP协议栈中的应用协议大多数都是基于明文在网络上传输的,这些明文
数据中,往往包含象密码、账号这样非常敏感的信息,如果使用Sniffer,非常容易就可以得到这些敏感信息。
更“可怕”的是,Sniffer完全是一个隐身的家伙,它只是“被动”的接收数据,而根本不向外发送数据,某种
程度上讲,你在传输数据的过程中,根本无法觉察到有人在监听你的数据信息。
当然,Sniffer也由一定的局限性,比如它只能在局域网的冲突域中进行监听,或者是位于两个点到点连接的中
间节点上,不过,对于一个黑客来说,渗透进目标局域网内的某台主机,在其上安装一个Sniffer,然后对整个
局域网进行监听,这往往并不是很困难的事情。
就象一只潜伏在黑暗中的猎犬一样,它用它敏锐的嗅觉捕获着空气中丝丝微微的信息,我们的Sniffer—嗅探器,就是这样一个东西。

嗅探器的软件实现方式
正常模式下,网卡驱动程序对数据帧的处理是在链路层中,如果目的MAC地址与本地地址不匹配,则将该数据帧
丢弃,否则,会上传给网络层处理。
将网卡设置成了混杂模式,所有流经网卡的数据帧都会被网卡驱动程序上传给网络层,无论其目的MAC地址是否
匹配本地地址。但是,数据分组到了网络层,网络层处理程序还要对其目的IP地址进行判断,如果是本地IP,
则上传给传输层处理(传输层再根据目的端口号来决定由哪个上层应用处理数据报文),否则丢弃。这时,问题
就出现了,如果没有特定的机制,即便网卡设置成了混杂模式,上层应用也是无法“抓”到本不属于“自己”
的数据包的。如果要让用户级的Sniffer软件可以真正“抓”到这些数据包,就需要一个与网卡驱动程序打交道
的另一个驱动模块,作为网卡驱动程序与上层应用的“中间人”,它将网卡设置成混杂模式,并从上层应用(Sn
iffer)接收下达的各种抓包请求,对来自网卡驱动程序的数据帧进行过滤,最终将符合Sniffer要求的数据返回
给Sniffer。这里我们看到,有了这个“中间人”,链路层的网卡驱动程序上传的数据帧就有了两个去处:一个
是正常的协议栈,另一个就是分组捕获及过滤模块,对于非本地的数据包,前者会丢弃(通过比较目的IP地址)
,而后者则会根据上层应用的要求来决定上传还是丢弃。
在图4.5中,我们可以看到,对于非本地的数据帧(用户级Sniffer请求捕获的),正常的TCP/IP协议栈会予以丢
弃,而分组捕获过滤机制则会将其传送给上层的应用程序。
许多操作系统都提供这样的“中间人”机制,即分组捕获机制。
在Unix类型的操作系统中,主要有三种:BSD系统中的BPF(Berkeley Packet Filter)、SVR4中的DLPI(Data
Link Provider Interface)和Linux中的SOCK_PACKET类型套接字。
基于BSD的系统在其内核中提供BPF分组过滤器,它可以作为一个可加载模块随时加载到系统内核之中,实现内
核级的分组捕获过滤机制(不是在用户进程中进行过滤,减少了从内核传送给用户进程的数据量,因此,大大提
高了效率)。
当一个数据帧到达网络接口时,通常情况下,链路层网卡驱动程序会将其提交到系统协议栈;如果BPF正在此接
口监听,驱动程序将首先调用BPF,BPF将数据包发送给过滤器,过滤器对数据包进行过滤,并将数据提交给与
过滤器关联的上层应用程序;然后链路层网卡驱动将重新取得控制权,并将数据帧提交给上层的系统协议栈处
理。
在图4.6中还可以看到,BPF不光可以对来自外部的数据分组进行捕获处理,而且可以捕获本地协议栈发送出去
的数据分组。
SVR4系统(例如Solaris 2.x)中支持数据链路提供者接口DLPI,这是OSI数据链路服务定义的一种流实现,它是
对早期SunOS 4.1.x提供的一种流式伪设备驱动器的更新。该伪设备驱动器就是网络接口开关(Network
Interface Tap,NIT),NIT与BPF类似,但不如BPF强大有效,它仅仅捕获从外部接收到的分组。
Linux提供了一个用户级的分组过滤机制:SOCK_PACKET类型套接字。在Linux中,除了完整实现了BSD Socket网
络开发接口外(TCP、UDP、IPX等),为了方便开发人员对网络的扩展和修改,增添了SOCK_PACKET类型,开发者
通过建立该类型的Socket,可以在用户级接收和发送特定类型的网络数据,包括捕获所有流经网卡的以太网数
据帧。
这里要注意的是,在基本的Socket编程中,除了有SOCK_STREAM和SOCK_DGRAM之外,还有一个SOCK_RAW类型,原
始套接字提供了一些使用TCP和UDP套接字不能实现的功能:可以访问ICMP和IGMP等协议的数据包,可以读写内
核不处理的数据包,可以创建自定义的IP数据包首部。不过,所有这些都有个前提,那就是需要内核协议栈将
所收到的某些数据包转发给原始套接字(接收时),而这些数据包,目标地址自然是本地的,这跟通过分组过滤
机制捕获所有流经网卡的数据包是有本质区别的。此外,使用原始套接字进行编程也有一定的局限性,内核会
遵循以下规则传送IP数据包给原始套接字:
(1) UDP和TCP数据包不会传送到某个原始套接字。内核自己会使用UDP和TCP协议模块来处理这两类数据包。如
果需要查看这两类数据包,只能通过直接访问数据链路层来实现(比如可以使用另一个很有用的函数库—Libnet
,它所提供的接口函数主要实现和封装了数据包的构造和发送过程,它可以支持原始套接口,也可以支持直接
对接口设备的操作)。
(2)
大多数ICMP数据包的一个拷贝将传送给匹配的原始套接字。内核为某些类型的ICMP消息返回响应,比如ICMP应
答请求(echo request)、ICMP时戳请求(timestamp request)和子网掩码请求(mask request)。
(3) 内核处理的所有其它类型数据包的拷贝将传送给匹配的原始套接字。
(4) 所有内核不能识别的协议类型的IP数据包将传送给匹配的原始套接字。
当然,上面提到的三种分组捕获机制都只适用于Unix类型的操作系统,对于Win32平台来说,因为其本身并不提
供直接的链路层访问接口,所以就需要用户自己开发基于NDIS的接口驱动程序(使用DDK),Windows9x平台上是v
xd虚拟设备驱动,而Windows
NT/2000不支持vxd类型的驱动,需要开发另一个中间驱动程序,例如Winpcap包(Libpcap和BPF机制在Windows中
的实现)所使用的packet.sys。此外,Windows 2000的WinSock 2有一个新的特性:支持直接的包捕获机制(允许
程序使用WSAIoctl( )来给一个SOCK_RAW类型的Socket设置SIO_RCVALL属性,这样该Socket就可以收到所有经过
本机的数据了),类似于Linux中的SOCK_PACKET。在图4.7中,我们可以看到Windows系统网络访问结构的简单描
述。
从图4.7中可知,Win32的应用程序和底层的网卡驱动之间是没有直接联系的,应用程序要访问网络,必须通过
几个中间组件来进行:
NDIS协议驱动器,向上和用户应用程序接口,向下则和NDIS包装器接口,这是一个依赖于系统类型的驱动模块
,不同的系统平台,需要有不同的驱动。
NDIS包装器,是一个对外提供NDIS(在Windows 2000中是NDIS5.0)接口的封装模块,由它来封装最里层的核心驱
动(也就是独立于平台的网卡驱动程序)。
在NDIS协议驱动器这部分,微软在其操作系统内部提供了一些模块,例如TCP/IP、NetBEUI、AppleTalk等等,
但它并不提供对网卡驱动进行更直接操作的特殊模块,这就和BSD系统提供BPF、Solaris提供DLPI有很大不同,
所以,传统的Windows平台Sniffer开发,都需要先开发一个满足需要的协议驱动器,这往往和系统类型是直接
相关的。当然,现在Windows 2000中Winsock2也提供了抓包的新特性,不过,就象Linux中的SOCK_PACKET那样
,在用户层进行过滤,毕竟不如内核中过滤的效率那么高。
到现在,我们知道了,要开发一个Sniffer,首先必须有一个切实可行的分组捕获机制,不过,光有了分组捕获
机制还不成,对于应用程序的开发者来说,直接在这些分组捕获机制之上进行编程,有时候困难是很大的,比
如BPF。所幸的是,针对这些更低层的分组捕获机制,在用户级有可以直接利用的库与之接口,开发者只需要调
用库中的相应函数,就不必关心其具体的分组捕获是如何实现的了。
最常用的用户级分组捕获函数库就是Libpcap了,它是一个与实现无关的、对操作系统提供的分组捕获机制进行
访问的分组捕获函数库,用于访问数据链路层。这个库为不同的平台提供了一致的编程接口,在安装了Libpcap
的平台上,以Libpcap为接口编写的程序和应用,能够自由的跨平台使用。
Libpcap与BPF结合的最为紧密,通过与底层BPF的接口,可以实现内核级的分组过滤。当然,Libpcap也可以配
置使用其它非BPF的分组过滤机制,比如DLPI和SOCK_PACKET,但除了BPF,其它的分组过滤机制即便是内核级的
,对Libpcap来说,也都需要将所有分组抓到用户级进行处理(这是Libpcap通过BPF仿真来实现的),所以相对真
正的内核级过滤,这种方式开销就很大了。
为了提供跨平台兼容性,在Libpcap源码中并没有通常的Makefile文件,而是通过运行一个configure脚本来自
动生成Makefile,configure脚本一方面检测系统特征,以确定当前系统的相关配置,另一方面读取已写好的Ma
kefile.in文件做为蓝本,生成一个适用于当前平台的Makefile。Libpcap与不同分组过滤机制的结合,就是通
过configure来实现的。
很著名的Sniffer软件—Tcpdump,以及轻量级IDS软件—Snort,都是基于Libpcap编写的。此外,大名鼎鼎的扫
描器—Nmap,也是基于Libpcap来捕获目标主机返回的数据包的。
目前,Libpcap在Windows平台中也有实现,那就是Winpcap,它提供给用户两个不同级别的编程接口:一个是基
于Libpcap的wpcap.dll,另一个是较“低层”的packet.dll,对于一般的要与Unix平台上Libpcap兼容的开发(
独立于系统平台)来说,使用wpcap.dll是当然的选择,不过,要进行更“原始”的分组过滤控制的开发,可以
选择使用packet.dll,它可以提供一个直接对设备驱动程序的编程接口。当然,Winpcap也是基于BPF模型的,
而BPF模型在Windows平台中的实现,则要依靠一个特别的设备驱动程序,那就是packet.sys和packet.inf,在W
inpcap的开发包中,有一个Winpcap.exe的执行文件,就是用来安装设备驱动程序的。
Libpcap库目前版本为0.6.2(有最新的0.7Beta版),其下载网址为:
www.tcpdump.org
Winpcap目前版本是v2.2(最新版是v2.3Beta),对应Libpcap v0.5版本,下载地址:
http://netgroup-serv.polito.it/winpcap
Windows 2000中直接利用Winsock 2进行分组捕获的例子—IPMan,可以在下面的网址中看到:
http://www.codeguru.com/network/ipmon.html
在Windows系统上开发Sniffer,传统上是先要开发一个协议驱动模块的,通常,这样的协议驱动器是通过控制
面板来指定安装的,要运行Sniffer工具,首先必须安装适合特定系统类型的驱动模块。现在,也有另一种新的
开发模式,那就是:开发一种多系统支持、动态安装及卸载的驱动模块,在Sniffer启动运行时,根据系统类型
来动态加载相应的驱动模块,退出运行时再自动卸载该模块,这样就方便了许多。这就是现在已经用的比较多
的rawether技术,在下面的网址中可以看到更详细的信息:
http://www.rawether.net/
此外,还有几个函数库对于网络安全编程来说非常有用:
l    Libnet提供的接口函数主要实现和封装了数据包的构造和发送过程。最新版是1.0.2a。
l    Libnids提供的接口函数主要实现了开发网络入侵监测系统所必须的一些结构框架。最新版是1.16。
l    Libicmp相对较为简单,它封装的是ICMP数据包的主要处理过程(构造、发送、接收等)。
利用这些C函数库的接口,网络安全工具开发人员可以很方便地编写出具有结构化强、健壮性好、可移植性高等
特点的程序,如scanner、sniffer、firewall、IDS等。
这些函数库都可以在www.packetfactory.net/projects.html中找到。

返回列表 回复 发帖