|
一 前言
SNIFF真是一个古老的话题,关于在网络上采用SNIFF来获取敏感信息已经不是什么
新鲜事,也不乏很多成功的案例,那么,SNIFF究竟是什么呢?SNIFF就是嗅探器,就是
窃听器,SNIFF静悄悄的工作在网络的底层,把你的秘密全部记录下来。看过威尔史密斯
演的《全民公敌》吗?SNIFF就象里面精巧的窃听器一样,让你防不胜防。
SNIFF可以是软件,也可以是硬件,既然是软件那就要分平台,有WINDOWS下的、UNXI
下的等,硬件的SNIFF称为网络分析仪,反正不管硬件软件,目标只有一个,就是获取在网
络上传输的各种信息。本文仅仅介绍软件的SNIFF。
当你舒适的坐在家里,惬意的享受网络给你带来的便利,收取你的EMAIL,购买你喜欢
的物品的时候,你是否会想到你的朋友给你的信件,你的信用卡帐号变成了一个又一个的
信息包在网络上不停的传送着,你是否曾经这些信息包会通过网络流入别人的机器呢?你
的担忧不是没有道理的,因为SNIFF可以让你的担忧变成实实在在的危险。就好象一个人躲
在你身后偷看一样。。。。。。
二网络基础知识
“网络基础知识”,是不是听起来有点跑题了?虽然听起来这和我们要谈的SNIFF没什么
关系,可是还是要说一说的,万丈高楼平地起,如果连地基都没打好,怎么盖楼?!如果你
对网络还不是十分清楚的话,最好能静下心来好好看看,要知道,这是基础的基础,在这里
我只是简单的说一下,免得到时候有人迷糊,详细的最好能够自己去找书看看。
(1)TCP/IP体系结构
开放系统互连(OSI)模型将网络划分为七层模型,分别用以在各层上实现不同的功能,
这七层分别为:应用层、表示层、会话层、传输层、网络层、数据链路层及物理层。而TCP/IP
体系也同样遵循这七层标准,只不过在某些OSI功能上进行了压缩,将表示层及会话层合并入
应用层中,所以实际上我们打交道的TCP/IP仅仅有5层而已,网络上的分层结构决定了在各层
上的协议分布及功能实现,从而决定了各层上网络设备的使用。实际上很多成功的系统都是基
于OSI模型的,如:如帧中继、ATM、ISDN等。
TCP/IP的网络体系结构(部分)

从上面的图中我们可以看出,第一层物理层和第二层数据链路层是TCP/IP的基础,而
TCP/IP本身并不十分关心低层,因为处在数据链路层的网络设备驱动程序将上层的协议和
实际的物理接口隔离开来。网络设备驱动程序位于介质访问子层(MAC)。
(2)网络上的设备
中继器:中继器的主要功能是终结一个网段的信号并在另一个网段再生该信号,一句话,
就是简单的放大而已,工作在物理层上。
网桥:网桥使用MAC物理地址实现中继功能,可以用来分隔网段或连接部分异种网络,工
作在数据链路层。
路由器:路由器使用网络层地址(IP,X.121,E.164等),主要负责数据包的路由寻径,也能
处理物理层和数据链路层上的工作。
网关:主要工作在网络第四层以上,主要实现收敛功能及协议转换,不过很多时候网关都
被用来描述任何网络互连设备。
(3)TCP/IP与以太网
以太网和TCP/IP可以说是相互相成的,可以说两者的关系几乎是密不可分,以太网在
一二层提供物理上的连线,而TCP/IP工作在上层,使用32位的IP地址,以太网则使用48位
的MAC地址,两者间使用ARP和RARP协议进行相互转换。从我们上面TCP/IP的模型图中可以
清楚的看到两者的关系。
载波监听/冲突检测(CSMA/CD)技术被普遍的使用在以太网中,所谓载波监听是指在以
太网中的每个站点都具有同等的权利,在传输自己的数据时,首先监听信道是否空闲,如
果空闲,就传输自己的数据,如果信道被占用,就等待信道空闲。而冲突检测则是为了防
止发生两个站点同时监测到网络没有被使用时而产生冲突。以太网采用广播机制,所有与
网络连接的工作站都可以看到网络上传递的数据。
为了加深你的理解,我们来看看下面的图,一个典型的在以太网中客户与服务器使用
TCP/IP协议的通信。

??唆唆了这么多,有人烦了吧?相信我,这是基础的基础,可以说是说得是很简单拉,
如果需要,拿出个几十万字来说上面的内容,我想也不嫌多,好了,让我们进入下一节,
sniff的原理。
三SNIFF的原理
要知道在以太网中,所有的通讯都是广播的,也就是说通常在同一个网段的所有网络接
口都可以访问在物理媒体上传输的所有数据,而每一个网络接口都有一个唯一的硬件地址,
这个硬件地址也就是网卡的MAC地址,大多数系统使用48比特的地址,这个地址用来表示网
络中的每一个设备,一般来说每一块网卡上的MFC地址都是不同的,每个网卡厂家得到一段
地址,然后用这段地址分配给其生产的每个网卡一个地址。在硬件地址和IP地址间使用ARP
和RARP协议进行相互转换。
在正常的情况下,一个网络接口应该只响应这样的两种数据帧:
1.与自己硬件地址相匹配的数据帧。2.发向所有机器的广播数据帧。
在一个实际的系统中,数据的收发是由网卡来完成的,网卡接收到传输来的数据,网卡
内的单片程序接收数据帧的目的MAC地址,根据计算机上的网卡驱动程序设置的接收模式判
断该不该接收,认为该接收就接收后产生中断信号通知CPU,认为不该接收就丢掉不管,所
以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到中断信号产生中断,操作
系统就根据网卡的驱动程序设置的网卡中断程序地址调用驱动程序接收数据,驱动程序接收
数据后放入信号堆栈让操作系统处理。而对于网卡来说一般有四种接收模式:
广播方式:该模式下的网卡能够接收网络中的广播信息。组播方式:设置在该模式下的网卡能够接收组播数据。直接方式:在这种模式下,只有目的网卡才能接收该数据。混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的。
好了,现在我们总结一下,首先,我们知道了在以太网中是基于广播方式传送数据的,也
就是说,所有的物理信号都要经过我的机器,再次,网卡可以置于一种模式叫混杂模式
(promiscuous),在这种模式下工作的网卡能够接收到一切通过它的数据,而不管实际上数
据的目的地址是不是他。这实际上就是我们SNIFF工作的基本原理:让网卡接收一切他所能接
收的数据。
我们来看一个简单的例子,机器A、B、C与集线器HUB相连接,集线器HUB通
过路由器Router访问外部网络。这是一个很简单也很常见的情况,比如说在公司大楼里,我
所在的网络部办公室里的几台机器通过集线器连接,而网络部、开发部、市场部也是同样如
此,几个部门的集线器通过路由器连接。还是回到我们的图一上来,值得注意的一点是机器
A、B、C使用一个普通的HUB连接的,不是用SWITCH,也不是用ROUTER,使用SWITCH和ROUTER
的情况要比这复杂得多。
我们假设一下机器A上的管理员为了维护机器C,使用了一个FTP命令向机器C进行远程登陆,
那么在这个用HUB连接的网络里数据走向过程是这样的。首先机器A上的管理员输入的登陆机
器C的FTP口令经过应用层FTP协议、传输层TCP协议、网络层IP协议、数据链路层上的以太网
驱动程序一层一层的包裹,最后送到了物理层,我们的网线上。接下来数据帧送到了HUB上,
现在由HUB向每一个接点广播由机器A发出的数据帧,机器B接收到由HUB广播发出的数据帧,
并检查在数据帧中的地址是否和自己的地址相匹配,发现不是发向自己的后把这数据帧丢弃,
不予理睬。而机器C也接收到了数据帧,并在比较之后发现是发现自己的,接下来他就对这数
据帧进行分析处理。
在上面这个简单的例子中,机器B上的管理员如果很好奇,他很想知道究竟登陆机器C上FTP
口令是什么?那么他要做的很简单,仅仅需要把自己机器上的网卡置于混杂模式,并对接收到
的数据帧进行分析,从而找到包含在数据帧中的口令信息。
四做一个自己的sniff
在上一节里,我们已经知道了SNIFF的基本原理是怎么一回事,这一节我们来亲自动手做一个
自己的sniff,毕竟,用程序代码来说话比什么都要来得真实,也容易加深理解。
回头想一想我们上面说的原理,我们要做的事情有几件:
1.把网卡置于混杂模式。2.捕获数据包。3.分析数据包。
注:下面的源代码取至ChadRenfro的<>
一文中
/************************Tcp_sniff_2.c********************/
1.#include
2.#include
3.#include
4.#include
5.#include
6.#include
7.#include
8.#include
9.#include"headers.h"
#defineINTERFACE"eth0"
/*Prototypearea*/
10.intOpen_Raw_Socket(void);
11.intSet_Promisc(char*interface,intsock);
12.intmain(){
13.intsock,bytes_recieved,fromlen;
14.charbuffer[65535];
15.structsockaddr_infrom;
16.structip*ip;
17.structtcp*tcp;
18.sock=Open_Raw_Socket();
19.Set_Promisc(INTERFACE,sock);
20.while(1)
22.{
23.fromlen=sizeoffrom;
24.bytes_recieved=recvfrom(sock,buffer,sizeofbuffer,0,(structsockaddr*)&from,&fromlen);
25.printf("\nBytesreceived:::%5d\n",bytes_recieved);
26.printf("Sourceaddress:::%s\n",inet_ntoa(from.sin_addr));
27.ip=(structip*)buffer;
/*SeeifthisisaTCPpacket*/
28.if(ip->ip_protocol==6){
29.printf("IPheaderlength:::%d\n",ip->ip_length);
30.printf("Protocol:::%d\n",ip->ip_protocol);
31.tcp=(structtcp*)(buffer+(4*ip->ip_length));
32.printf("Sourceport:::%d\n",ntohs(tcp->tcp_source_port));
33.printf("Destport:::%d\n",ntohs(tcp->tcp_dest_port));
34.}
35.}
36.}
37.intOpen_Raw_Socket(){
38.intsock;
39.if((sock=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0){
/*Thenthesocketwasnotcreatedproperlyandmustdie*/
40.perror("Therawsocketwasnotcreated");
41.exit(0);
42.};
43.return(sock);
44.}
45.intSet_Promisc(char*interface,intsock){
46.structifreqifr;
47.strncpy(ifr.ifr_name,interface,strnlen(interface)+1);
48.if((ioctl(sock,SIOCGIFFLAGS,&ifr)==-1)){
/*Couldnotretrieveflagsfortheinterface*/
49.perror("Couldnotretriveflagsfortheinterface");
50.exit(0);
51.}
52.printf("Theinterfaceis:::%s\n",interface);
53.perror("Retrievedflagsfrominterfacesuccessfully");
54.ifr.ifr_flags|=IFF_PROMISC;
55.if(ioctl(sock,SIOCSIFFLAGS,&ifr)==-1){
/*Couldnotsettheflagsontheinterface*/
56.perror("CouldnotsetthePROMISCflag:");
57.exit(0);
58.}
59.printf("Settinginterface:::%s:::topromisc",interface);
60.return(0);
61.}
/***********************EOF**********************************/
上面这段程序中有很详细的注解,不过我想还是有必要说一说,首先
第10行--intOpen_Raw_Socket(void);是我们的自定义函数,具体内容如下:
37.intOpen_Raw_Socket(){
38.intsock;
39.if((sock=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0){
/*Thenthesocketwasnotcreatedproperlyandmustdie*/
40.perror("Therawsocketwasnotcreated");
41.exit(0);
42.};
43.return(sock);
44.}
第39行if((sock=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))<0){
这里我们调用了socket函数,使创建了了一个原始套接口,使之收到TCP/IP信息包。
接下来第11行-intSet_Promisc(char*interface,intsock),这也是我们的自定义函数,
目的是把网卡置于混杂模式,具体内容如下:
45.intSet_Promisc(char*interface,intsock){
46.structifreqifr;
47.strncpy(ifr.ifr_name,interface,strnlen(interface)+1);
48.if((ioctl(sock,SIOCGIFFLAGS,&ifr)==-1)){
/*Couldnotretrieveflagsfortheinterface*/
49.perror("Couldnotretriveflagsfortheinterface");
50.exit(0);
51.}
52.printf("Theinterfaceis:::%s\n",interface);
53.perror("Retrievedflagsfrominterfacesuccessfully");
54.ifr.ifr_flags|=IFF_PROMISC;
55.if(ioctl(sock,SIOCSIFFLAGS,&ifr)==-1){
/*Couldnotsettheflagsontheinterface*/
56.perror("CouldnotsetthePROMISCflag:");
57.exit(0);
58.}
59.printf("Settinginterface:::%s:::topromisc",interface);
60.return(0);
61.}
首先structifreqifr;定一了一个ifrreg的结构ifr,接下来
strncpy(ifr.ifr_name,interface,strnlen(interface)+1);,就是把我们网络设备的名字填
充到ifr结构中,在这里#defineINTERFACE"eth0",让我们再往下看,
ioctl(sock,SIOCGIFFLAGS,&ifr),SIOCGIFFLAGS请求表示需要获取接口标志,现在到了
第54行,在我们成功的获取接口标志后把他设置成混杂模式,
ifr.ifr_flags|=IFF_PROMISC;ioctl(sock,SIOCSIFFLAGS,&ifr)。OK,现在我们所说的
第一步已经完成--------把网卡置于混杂模式。
现在进入第二步,捕获数据包。从第20行开始,我们进入了一个死循环,while(1),在
第24行,recvfrom(sock,buffer,sizeofbuffer,0,(structsockaddr*)&from,&fromlen),
这个函数要做的就是接收数据,冰把接收到的数据放入buffer中。就是这么简单,已经完成了我
们要捕获数据包的任务。
到了第三步,分析数据包。27行,ip=(structip*)buffer,使我们在头文件中的IP结
构对应于所接收到的数据,接下来判断在网络层中是否使用的是TCP协议,
if(ip->ip_protocol==6),如果答案是,tcp信息包从整个IP/TCP包buffer+(4*ip->ip_length)
地址处开始,所以31行tcp=(structtcp*)(buffer+(4*ip->ip_length)),然后对应
结构把你所需要的信息输出。
/*************************headers.h**************************/
/*structureofanipheader*/
structip{
unsignedintip_length:4;/*little-endian*/
unsignedintip_version:4;
unsignedcharip_tos;
unsignedshortip_total_length;
unsignedshortip_id;
unsignedshortip_flags;
unsignedcharip_ttl;
unsignedcharip_protocol;
unsignedshortip_cksum;
unsignedintip_source;unsignedintip_dest;
};
/*StructureofaTCPheader*/
structtcp{
unsignedshorttcp_source_port;
unsignedshort [1] [2] [3] 下一页
 |
频道声明:本频道的文章除部分特别声明禁止转载的专稿外,可以自由转载.但请务必注明出出处和原始作者 文章版权归本频道与文章作者所有.对于被频道转载文章的个人和网站,我们表示深深的谢意。
| 原始作者:佚名 |
录入时间:2006-11-22 |
| 信息来源:不详 |
投稿信箱:itqoo@126.com |
|
|
 |
|