Sniffer原理与C实现

by 苗壮 1. May 2008 20:46

sniffer

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的网络体系结构(部分)
-----------------------------------
|SMTP|DNS|HTTP|FTP|TELNET|应用层
-----------------------------------
|TCP|UDP|传输层
-----------------------------------
|IP|ICMP|ARPRARP|网络层
------------------------
|IEEE802以太网SLIP/PPPPDNetc|数据链路层
-----------------------------------
|网卡电缆双绞线etc|物理层
-----------------------------------
从上面的图中我们可以看出,第一层物理层和第二层数据链路层是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协议的通信。
用户进程FTP客户<------------------------->FTP服务器应用层
||
内核中的协议栈TCP<------------------------->TCP传输层
||
内核中的协议栈IP<------------------------->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,毕竟,用程序代码来说话比什么都要来得真实,也容易加深理解。
回头想一想我们上面说的原理,我们要做的事情有几件:

一. 摘要
  Raw Socket: 原始套接字
  可以用它来发送和接收 IP 层以上的原始数据包, 如 ICMP, TCP, UDP...

    int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

  这样我们就创建了一个 Raw Socket

  Sniffer: 嗅探器
  关于嗅探器的原理我想大多数人可能都知道
  1. 把网卡置于混杂模式;
  2. 捕获数据包;
  3. 分析数据包.

  但具体的实现知道的人恐怕就不是那么多了. 好, 现在让我们用 Raw Socket 的做一个自已的 Sniffer.

二. 把网卡置于混杂模式
  在正常的情况下,一个网络接口应该只响应两种数据帧:
  一种是与自己硬件地址相匹配的数据帧
  一种是发向所有机器的广播数据帧
  如果要网卡接收所有通过它的数据, 而不管是不是发给它的, 那么必须把网卡置于混杂模式. 也就是说让它的思维混乱, 不按正常的方式工作. 用 Raw Socket 实现代码如下:

    setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag); //设置 IP 头操作选项
    bind(sockRaw, (PSOCKADDR)&addrLocal, sizeof(addrLocal); //把 sockRaw 绑定到本地网卡上
    ioctlsocket(sockRaw, SIO_RCVALL, &dwValue);       //让 sockRaw 接受所有的数据

  flag 标志是用来设置 IP 头操作的, 也就是说要亲自处理 IP 头: bool flag = ture;
  addrLocal 为本地地址: SOCKADDR_IN addrLocal;
  dwValue 为输入输出参数, 为 1 时执行, 0 时取消: DWORD dwValue = 1;
  没想到这么简单吧?

三. 捕获数据包
  你的 sockRaw 现在已经在工作了, 可以在局域网内其它的电脑上用 Sniffer 检测工具检测一下, 看你的网卡是否处于混杂模式(比如 DigitalBrain 的 ARPKiller).
  不能让他白白的浪费资源啊, 抓包!

    recv(sockRaw, RecvBuf, BUFFER_SIZE, 0); //接受任意数据包

  #define BUFFER_SIZE 65535
  char RecvBuf[BUFFER_SIZE];
  越来越发现 Sniffer 原来如此的简单了, 这么一个函数就已经完成抓取数据包的任务了.

四. 分析数据包
  这回抓来的包和平常用 Socket 接受的包可就不是一回事儿了, 里面包含 IP, TCP 等原始信息. 要分析它首先得知道这些结构.
  数据包的总体结构:
  ----------------------------------------------
  | ip header | tcp header(or x header) | data |
  ----------------------------------------------

  IP header structure:
       4    8    16                    32 bit
  |--------|--------|----------------|--------------------------------|
  | Ver  | IHL  |Type of service |     Total length     |
  |--------|--------|----------------|--------------------------------|
  | Identification |   Flags   |     Fragment offset    |
  |--------|--------|----------------|--------------------------------|
  | Time to live  |  Protocol  |     Header checksum    |
  |--------|--------|----------------|--------------------------------|
  |             Source address              |
  |--------|--------|----------------|--------------------------------|
  |            Destination address             |
  |--------|--------|----------------|--------------------------------|
  |            Option + Padding              |
  |--------|--------|----------------|--------------------------------|
  |                Data                |
  |--------|--------|----------------|--------------------------------|

  TCP header structure:
                  16                32 bit
  |--------------------------------|--------------------------------|
  |     Source port      |    Destination port    |
  |--------------------------------|--------------------------------|
  |             Sequence number             |
  |--------------------------------|--------------------------------|
  |           Acknowledgement number           |
  |--------------------------------|--------------------------------|
  | Offset | Resrvd |U|A|P|R|S|F|      Window       |
  |--------------------------------|--------------------------------|
  |      Checksum       |    Urgent pointer     |
  |--------------------------------|--------------------------------|
  |             Option + Padding            |
  |--------------------------------|--------------------------------|
  |               Data                |
  |--------------------------------|--------------------------------|

五. 实现 Sniffer
  OK!
  现在都清楚了, 还等什么.
  下面是我用 BCB6 写的一个 Simple Sniffer 的代码, 仅供参考.
  (需要在工程文件里加入WS2_32.LIB这个文件)
//*************************************************************************//
//* CPP File: WMain.cpp
//* Simple Sniffer by shadowstar
//* http://shadowstar.126.com/
//*************************************************************************//
#include <vcl.h>
#pragma hdrstop

#include <winsock2.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
#include <netmon.h>
#include "WMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
  : TForm(Owner)
{
WSADATA WSAData;
  BOOL  flag  = true;
  int   nTimeout = 1000;
  char  LocalName[16];
  struct hostent *pHost;

  //检查 Winsock 版本号
  if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
    throw Exception("WSAStartup error!");

  //初始化 Raw Socket
  if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == INVALID_SOCKET)
    throw Exception("socket setup error!");

  //设置IP头操作选项
  if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)) == SOCKET_ERROR)
    throw Exception("setsockopt IP_HDRINCL error!");

  //获取本机名
  if (gethostname((char*)LocalName, sizeof(LocalName)-1) == SOCKET_ERROR)
    throw Exception("gethostname error!");

  //获取本地 IP 地址
  if ((pHost = gethostbyname((char*)LocalName)) == NULL)
    throw Exception("gethostbyname error!");

  addr_in.sin_addr  = *(in_addr *)pHost->h_addr_list[0]; //IP
  addr_in.sin_family = AF_INET;
  addr_in.sin_port  = htons(57274);

  //把 sock 绑定到本地地址上
  if (bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
    throw Exception("bind error!");

  iSortDirection = 1;
}
//---------------------------------------------------------------------------
__fastcall TMainForm::~TMainForm()
{
  WSACleanup();
}
//---------------------------------------------------------------------------

void __fastcall TMainForm::btnCtrlClick(TObject *Sender)
{
  TListItem *Item;
  DWORD dwValue;
  int nIndex = 0;

  if (btnCtrl->Caption == "&Start")
  {
    dwValue = 1;
    //设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包
    if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0)
      throw Exception("ioctlsocket SIO_RCVALL error!");
    bStop = false;
    btnCtrl->Caption = "&Stop";
    lsvPacket->Items->Clear();
  }
  else
  {
    dwValue = 0;
    bStop = true;
    btnCtrl->Caption = "&Start";
    //设置SOCK_RAW为SIO_RCVALL,停止接收
    if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0)
      throw Exception("WSAIoctl SIO_RCVALL error!");
  }

  while (!bStop)
  {
    if (recv(sock, RecvBuf, BUFFER_SIZE, 0) > 0)
    {
      nIndex++;
      
      ip = *(IP*)RecvBuf;
      tcp = *(TCP*)(RecvBuf + (ip.HdrLen & IP_HDRLEN_MASK));

      Item = lsvPacket->Items->Add();
      Item->Caption = nIndex;
      Item->SubItems->Add(GetProtocolTxt(ip.Protocol));
      Item->SubItems->Add(inet_ntoa(*(in_addr*)&ip.SrcAddr));
      Item->SubItems->Add(inet_ntoa(*(in_addr*)&ip.DstAddr));
      Item->SubItems->Add(tcp.SrcPort);
      Item->SubItems->Add(tcp.DstPort);
      Item->SubItems->Add(ntohs(ip.TotalLen));
    }
    Application->ProcessMessages();
  }  
}
//---------------------------------------------------------------------------

AnsiString __fastcall TMainForm::GetProtocolTxt(int Protocol)
{
  switch (Protocol)
  {
    case IPPROTO_ICMP :      //1        /* control message protocol */
      return PROTOCOL_STRING_ICMP_TXT;
    case IPPROTO_TCP :      //6        /* tcp */
      return PROTOCOL_STRING_TCP_TXT;
    case IPPROTO_UDP :      //17       /* user datagram protocol */
      return PROTOCOL_STRING_UDP_TXT;
    default :
      return PROTOCOL_STRING_UNKNOWN_TXT;
  }
}
//---------------------------------------------------------------------------


//*************************************************************************//
//* Header File: WMain.h for WMain.cpp class TMainForm
//*************************************************************************//
//---------------------------------------------------------------------------

#ifndef WMainH
#define WMainH
//---------------------------------------------------------------------------
#define BUFFER_SIZE 65535

#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <winsock2.h>
#include "netmon.h"


//---------------------------------------------------------------------------
class TMainForm : public TForm
{
__published: // IDE-managed Components
  TPanel *Panel1;
  TButton *btnCtrl;
  TListView *lsvPacket;
  TLabel *Label1;
  void __fastcall btnCtrlClick(TObject *Sender);
  void __fastcall lsvPacketColumnClick(TObject *Sender,
     TListColumn *Column);
  void __fastcall lsvPacketCompare(TObject *Sender, TListItem *Item1,
     TListItem *Item2, int Data, int &Compare);
  void __fastcall Label1Click(TObject *Sender);
private: // User declarations
  AnsiString __fastcall GetProtocolTxt(int Protocol);
public: // User declarations
  SOCKET   sock;
  SOCKADDR_IN addr_in;
  IP     ip;
  TCP     tcp;
  PSUHDR   psdHeader;
  char    RecvBuf[BUFFER_SIZE];
  bool    bStop;

  int iSortDirection;
  int iColumnToSort;
  
  __fastcall TMainForm(TComponent* Owner);
  __fastcall ~TMainForm();
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif

  偷了个懒, IP, TCP 头及一些宏定义用了 netmon.h 的头, 这个文件在 BCB6 的 include 目录下可以找得到, 其中与本程序相关内容如下:

//*************************************************************************//
//* Header File: netmon.h
//*************************************************************************//
//
// IP Packet Structure
//
typedef struct _IP
{
  union
  {
    BYTE  Version;
    BYTE  HdrLen;
  };
  BYTE ServiceType;
  WORD TotalLen;
  WORD ID;
  union
  {
    WORD  Flags;
    WORD  FragOff;
  };
  BYTE TimeToLive;
  BYTE Protocol;
  WORD HdrChksum;
  DWORD  SrcAddr;
  DWORD  DstAddr;
  BYTE Options[0];
} IP;

typedef IP * LPIP;
typedef IP UNALIGNED * ULPIP;

//
// TCP Packet Structure
//
typedef struct _TCP
  {
  WORD SrcPort;
  WORD DstPort;
  DWORD SeqNum;
  DWORD AckNum;
  BYTE DataOff;
  BYTE Flags;
  WORD Window;
  WORD Chksum;
  WORD UrgPtr;
  } TCP;

typedef TCP *LPTCP;
typedef TCP UNALIGNED * ULPTCP;

// upper protocols
#define PROTOCOL_STRING_ICMP_TXT    "ICMP"
#define PROTOCOL_STRING_TCP_TXT    "TCP"
#define PROTOCOL_STRING_UDP_TXT    "UDP"
#define PROTOCOL_STRING_SPX_TXT    "SPX"
#define PROTOCOL_STRING_NCP_TXT    "NCP"

#define PROTOCOL_STRING_UNKNOW_TXT   "UNKNOW"


  这个文件也有人声称没有.
//*************************************************************************//
//* Header File: mstcpip.h
//*************************************************************************//
// Copyright (c) Microsoft Corporation. All rights reserved.
#if _MSC_VER > 1000
#pragma once
#endif

/* Argument structure for SIO_KEEPALIVE_VALS */

struct tcp_keepalive {
  u_long onoff;
  u_long keepalivetime;
  u_long keepaliveinterval;
};

// New WSAIoctl Options

#define SIO_RCVALL      _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST   _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS  _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT  _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF     _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND    _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF   _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST  _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST  _WSAIOW(IOC_VENDOR,11)

// Values for use with SIO_RCVALL* options
#define RCVALL_OFF       0
#define RCVALL_ON       1
#define RCVALL_SOCKETLEVELONLY 2

  现在我们自已的 Sniffer 就做好了, Run, Start......哇, 这么多数据包, 都是从这一台机器上发出的, 它在干什么? 原来 Adminstrator 密码为空, 中了尼姆达病毒!

六. 小结
  优点: 实现简单, 不需要做驱动程序就可实现抓包.
  缺点: 数据包头不含帧信息, 不能接收到与 IP 同层的其它数据包, 如 ARP, RARP...
  这里提供的程序仅仅是一个 Sniffer 的例子, 没有对数据包进行进一步的分析. 写此文的目的在于熟悉Raw Socket 编程方法, 了解 TCP/IP 协议结构原理以及各协议之间的关系.

 

 

从上面的分析我们可以清楚的认识到,认识一个SNIFF需要对TCP/IP协议有着详细的了解,
否则你根本无法找到你需要的信息。有了上面的基础,你可以自己来做一个你需要的SNIFF了。
五常用的SNIFF
很少有原因会让你自己亲自动手来做一个自己的SNIFF,除非你是想了解他的原理,或者是
其他一些特别的原因,比如你要在某个特殊的环境拦截一些特殊的数据包。下面我们就来看
看一些在网络上经常使用的SNIFF。
(1)windows环境下
windows环境下当然是大名鼎鼎的netxray以及snifferpro了,实际上很多人都是用他在
windows环境下抓包来分析,不过我想很少有人笨到去在别人的机器上安装一个图形界面的SNIFF,
除非他和管理员很熟悉........netxray的使用就不多说了,反正windows下的东西就是
click,click,click,非常的方便用户。
(2)UNUX环境下
UNUX环境下的sniff可以说是百花齐放,一抓就是一大把,如sniffit,snoop,tcpdump,dsniff
等都是比较常见的,他们都有一个好处就是发布源代码,可以让你研究,当然也都是免费的:)
1.sniffit
sniffit可以运行在Solaris、SGI和Linux等平台上,由LawrenceBerkeleyLaboratory实验
室开发的一个免费的网络监听软件。最近Sniffit0.3.7也推出了NT版本,并也支持WINDOWS2000.
使用方法:
-v显示版本信息
-a以ASCII形式将监听的结果输出。
-A在进行记录时,所有不可打印的字符都用代替
-b等同于同时使用参数-t&-s。
-d将监听所得内容以十六进制方式显示在当前终端
-p记录连接到的包,0为所有端口。缺省为0。
-Pprotocol选择要检查的协议,缺省为TCP。可能的选择有IP、TCP、ICMP、UDP和他们的组合。
-s指定sniffer检查从发送的数据包。-t指定sniffer检查发送到的数据包。
-i进入交互模式
-l设定数据包大小,default是300字节
注:参数可以用@来表示一个IP范围,比如-t192.168.@-t和-s只适用于TCP/UDP数据包,对
于ICMP和IP也进行解释。但如果只选择了-p参数,则只用于TCP和UDP包。
举例说明:
#sniffit-a-p21-txxx.xxx.xxx.xxx
监听流向机器xxx.xxx.xxx.xxx的21端口(FTP)的信息,并以ASCII显示
#sniffit-d-p23-bxxx.xxx.xxx.xxx
监听所有流出或流入机器xxx.xxx.xxx.xxx的23端口(telnet)的信息,并以16进制显示
你可以在这里找到sniffithttp://reptile.rug.ac.be/~coder/sniffit/sniffit.html
2.snoop
snoop默认情况安装在Solaris下,是一个用于显示网络交通的程序,不过SNIFF是把双刃剑,
既然管理员能用他来监视自己的网络,当然一个心怀恶意的入侵者也可以用他来SNIFF自己感兴
趣的内容。值得一提的是,SNOOP被发现存在一个缓冲区溢出漏洞,当以导致入侵者以运行
snoop(通常为root)的身份远程进入系统。这是题外话,暂且就此打住。
使用方法:
[-a]#Listentopacketsonaudio
[-ddevice]#settabletole?,ie?,bf?,tr?
[-ssnaplen]#Truncatepackets
[-ccount]#Quitaftercountpackets
[-P]#TurnOFFpromiscuousmode
[-D]#Reportdroppedpackets
[-S]#Reportpacketsize
[-ifile]#Readpreviouslycapturedpackets
[-ofile]#Capturepacketsinfile
[-nfile]#Loadaddr-to-nametablefromfile
[-N]#Createaddr-to-nametable
[-tr|a|d]#Time:Relative,AbsoluteorDelta
[-v]#Verbosepacketdisplay
[-V]#Showallsummarylines
[-pfirst[,last]]#Selectpacket(s)todisplay
[-xoffset[,length]]#Hexdumpfromoffsetforlength
[-C]#Printpacketfiltercode
例如:
#snoop-osavedAB
监听机器A与B的谈话,并把内容存储于文件saved中
3.tcpdump
tcpdmp也算是一个很有名气的网络监听软件,FREEBSD还把他附带在了系统上,是一个被
很多UNIX高手认为是一个专业的网络管理工具。
使用方法:
tcpdump采用命令行方式,它的命令格式为:
tcpdump[-adeflnNOpqStvx][-c数量][-F文件名][-i网络接口][-r文件名]
[-ssnaplen][-T类型][-w文件名][表达式]
1.tcpdump的选项介绍
-a将网络地址和广播地址转变成名字;
-d将匹配信息包的代码以人们能够理解的汇编格式给出;
-dd将匹配信息包的代码以c语言程序段的格式给出;
-ddd将匹配信息包的代码以十进制的形式给出;
-e在输出行打印出数据链路层的头部信息;
-f将外部的Internet地址以数字的形式打印出来;
-l使标准输出变为缓冲行形式;
-n不把网络地址转换成名字;
-t在输出的每一行不打印时间戳;
-v输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
-vv输出详细的报文信息;
-c在收到指定的包的数目后,tcpdump就会停止;
-F从指定的文件中读取表达式,忽略其它的表达式;
-i指定监听的网络接口;
-r从指定的文件中读取包(这些包一般通过-w选项产生);
-w直接将包写入文件中,并不分析和打印出来;
-T将监听到的包直接解释为指定的类型的报文,常见的类型有rpc和snmp
2.tcpdump的表达式介绍
表达式是一个正则表达式,tcpdump利用它作为过滤报文的条件,如果一个报文满足表达式
的条件,则这个报文将会被捕获。如果没有给出任何条件,则网络上所有的信息包将会被截获。
在表达式中一般如下几种类型的关键字,一种是关于类型的关键字,主要包括host,net,
port,例如host210.27.48.2,指明210.27.48.2是一台主机,net202.0.0.0指明202.0.0.0
是一个网络地址,port23指明端口号是23。如果没有指定类型,缺省的类型是host.
第二种是确定传输方向的关键字,主要包括src,dst,dstorsrc,dstandsrc,这些关
键字指明了传输的方向。举例说明,src210.27.48.2,指明ip包中源地址是210.27.48.2,
dstnet202.0.0.0指明目的网络地址是202.0.0.0。如果没有指明方向关键字,则缺省是
srcordst关键字。
第三种是协议的关键字,主要包括fddi,ip,arp,rarp,tcp,udp等类型。Fddi指明是在
FDDI(分布式光纤数据接口网络)上的特定的网络协议,实际上它是"ether"的别名,fddi和ether
具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和分析。其他的
几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有
协议的信息包。
除了这三种类型的关键字之外,其他重要的关键字如下:
gateway,broadcast,less,greater,还有三种逻辑运算,取非运算是''not''''!'',与运算
是''and'',''&&'';或运算是''or'',''||''。
举例使用:
#tcpdumphostAAA.BBB.CCC.DDD
将监听IP地址为AAA.BBB.CCC.DDD的机器的通话
#tcpdumptcpport23hostAAA.BBB.CCC.DDD
将监听IP地址为AAA.BBB.CCC.DDD的机器的23端口的通话
4.dsniff
之所以要谈谈dsniff,是因为他不仅仅是一个sniff,在他的整个套件包中,包含了很多
其它有用的工具,如arpspoof,dnsspoof,macof,tcpkill等等,SNIFF的手段更加的多样和
复杂化。dsniff是由DugSong开发的你可以在他的主页上找到这个工具。目前dsniff支持
OpenBSD(i386),RedhatLinux(i386),和Solaris(sparc).并且在FreeBSD,DebianLinux,
SlackwareLinux,AIX,和HP-UX上也能运转得很好。但是dsniff需要几个其他的第三方软件进
行支持,他们分别是,BerkeleyDB,OpenSSL,libpcap,libnet,libnids。如果条件允
许的话,你最好能够亲自读一读dsniff的源代码,你可以在
http://naughty.monkey.org/~dugsong/找到dsniff。
六深入sniff
单纯的sniff的功能始终是局限的,所以在大多数的情况下,sniff往往和其他手段结合起来使
用,sniff和spoof已及其他技术手段结合在一起对网络构成的危害是巨大的。单纯的sniff好比缺
了一只腿,无法发挥大的作用,例如在sniff原理一节中我们讨论的例子里,我一再的强调我们使
用的是一个普通的HUB进行连接是有原因的,如果我们把在图一中的HUB用一个switch代替,那情况
就要复杂一些了,如图二所示:
图(二)
在图二中,我们的机器A、B、C与Switch相连接,而Switch通过路由器Router访问外部网络。我
们先来了解Switch的工作原理:
在我们图一中的HUB只是简单地把所接收到的信号通过所有端口(除了信号来的那个口)重复
发送出去不同,而图二中的Switch却可以检查每一个收到的数据包,并对数据包进行相应的处理。
在Switch内保存着每一个网段上所有节点的物理地址,只允许必要的网络流量通过Switch。举例来
说,当Switch接收到一个数据包之后,根据自身保存的网络地址表检查数据包内包含的发送和接收
方地址。如果接收方位于发送方网段,该数据包就会被Switch丢弃,不能通过交换机传送到其它的
网段;如果接收方和发送方位于两个不同的网段,该数据包就会被Switch转发到目标网段。这样,
通过交换机的过滤和转发,可以有效避免网络广播风暴,减少误包和错包的出现。顺便说一句,现在
Switch和HUB的价格相去无几,所以hub正逐渐被网络交换机取代。
现在回到我们的例子中来,在图二中仍然和图一一样,我们假设机器A上的管理员为了维护机器C,
使用了一个FTP命令向机器C进行远程登陆,那么在这里,数据是这样走的:首先机器A上的管理员输入
的登陆机器C的FTP口令经过应用层FTP协议、传输层TCP协议、网络层IP协议、数据链路层上的以太网
驱动程序一层一层的包裹,最后送到了物理层,我们的网线上。接下来数据帧送到了Switch上,而
Switch检查数据帧中的目的地址,并在他自身保存的网络地址表中知道了他应该把这数据帧发到机器
C那里,于是,接下来机器C接收到了从A发来的信息,发现他是发给自己的信息,于是进行分析处理。
OK,现在我们机器B上的管理员的好奇心只能深深的埋藏在心里了,因为数据包根本就没有经过他,
就算他把自己的网卡设置成混杂模式也是有力无处使。
在了解在一个Switch环境下原理后,我们结合一些手段去设法sniff,是的,我们可以做到这一点,
有许多的手段可以让管理员B满足他的好奇心,在下面我会提出几个办法,当然只是其中的一些办法
而已。
1ARPSpoof
在基于IP通信的内部网中,我们可以使用ARPSpoof的手段,了解什么是ARPSpoof的前提你先
要明白一下什么是ARP和RARP协议,什么是MAC地址,什么又是IP地址。ARP协议是地址转换协议,
RARP被称为反向地址转换协议,他们负责把IP地址和MAC地址进行相互转换对应。
ARPSpoof攻击的根本原理是因为计算机中维护着一个ARP高速缓存,并且这个ARP高速缓存是
随着计算机不断的发出ARP请求和收到ARP响应而不断的更新的,ARP高速缓存的目的是把机器的IP地
址和MAC地址相互映射。你可以使用arp命令来查看你自己的ARP高速缓存。现在设想一下,一个
Switch工作在数据链路层,他根据MAC地址来转发他所接收的数据包,而计算器维护的ARP高速缓存
却是动态的......你想到什么了吗?
为了加深理解,现在给我们的机器编上号,机器A:IP地址为10.0.0.1,MAC地址为
20-53-52-43-00-01,机器B:IP地址为10.0.0.2,MAC地址为20-53-52-43-00-02,机器C:IP地址
为10.0.0.3,MAC地址为20-53-52-43-00-03。现在机器B上的管理员是个聪明的家伙,他向机器
A发出一个ARPReply(协议没有规定一定要等ARPRequest出现才能发送ARPReply,也没有
规定一定要发送过ARPRequest才能接收ARPReply),其中的目的IP地址为10.0.0.1,目的MAC
地址为20-53-52-43-00-01,而源IP地址为10.0.0.3,源MAC地址为20-53-52-43-00-02,好了,
现在机器A更新了他的ARP高速缓存,并相信了IP地址为10.0.0.3的机器的MAC地址是
20-53-52-43-00-02。当机器A上的管理员发出一条FTP命令时---ftp10.0.0.3,数据包被送到了
Switch,Switch查看数据包中的目的地址,发现MAC为20-53-52-43-00-02,于是,他把数据包
发到了机器B上。再设想一下,如果不想影响A和C之间的通信该怎么办?你可以同时欺骗他们双方,
来一个man-in-middle。
当然,在实际的操作中你还需要考虑到一些其他的事,比如某些操作系统在会主动的发送ARP请
求包来更新相应的ARP入口等。
2.MACFlooding
在上面我们曾经提到过,Switch之所以能够由数据包中目的MAC地址判断出他应该把数据包发送到
那一个端口上是根据他本身维护的一张地址表。这张地址表可能是动态的也可能是静态的,这要看
Switch的厂商和Switch的型号来定,对于某些Switch来说,他维护的是一张动态的地址表,并且地
址表的大小是有上限的,比如3comSuperstackSwitch3300(3c16981Hardwarev.1Softwarev.2.10)
就是这样一种Switch,我们可以通过发送大量错误的地址信息而使SWITCH维护的地址表“溢出”,
从而使他变成广播模式来达到我们要sniff机器A与机器C之间的通信的目的。
3.FaketheMACaddress
伪造MAC地址也是一个常用的办法,不过这要基于你网络内的Switch是动态更新其地址表,这实
际上和我们上面说到的ARPSpoof有些类似,只不过现在你是想要Switch相信你,而不是要机器A
相信你。因为Switch是动态更新其地址表的,你要做的事情就是告诉Switch:HI,我是机器C。换成
技术上的问题你只不过需要向Switch发送伪造过的数据包,其中源MAC地址对应的是机器C的MAC地址,
现在Switch就把机器C和你的端口对应起来了。不过其中存在一个问题,现在机器C也会说了:
HI,Switch老大,我才是机器C呢!,现在你该怎么办?切,还用问!让他说不了话就可以了,
DOS还是其他什么,随便你了......
4.ICMPRouterAdvertisements
这主要是由ICMP路由器发现协议(IRDP)的缺陷引起的,在Windows95、98、2000及SunOS、
Solaris2.6等系统中,都使用了IRDP协议,SunOS系统只在某些特定的情况下使用该协议,而
Windows95,Windows95b,Windows98,Windows98se,和Windows2000都是默认的使用IRDP协议。
IRDP协议的主要内容就是告诉人们谁是路由器,设想一下,一个HACK利用IRDP宣称自己是路由器的
情况会有多么的糟糕!所有相信HACK的请求的机器把他们所有的数据都发送给HACK所控制的机器.........
5.ICMPRedirect
所谓ICMP重定向,就是指告诉机器向另一个不同的路由发送他的数据包,ICMP重定向通常使
用在这样的场合下,假设A与B两台机器分别位于同一个物理网段内的两个逻辑子网内,而A和B都
不知道这一点,只有路由器知道,当A发送给B的数据到达路由器的时候,路由器会向A送一个ICMP
重定向包裹,告诉A:HI,别再送数据给我转交了,你就直接送到B那里就可以了。设想一下,
一个hack完全可以利用这一点,使得A发送给B的数据经过他。
上面提到的这些方法只不是其中的一些,为了配合sniff能够工作得更有效率,还有其他许多
的办法,其实sniff的目的说穿了只有一个,就是抓包,从抓包这个概念上引伸下去,所有为了能
够抓到网络上的信息包而采用的技术都可以归入sniff,单纯的sniff是没有什么效率的。你还能
想到什么吗?进攻路由器,在路由器上放置sniff......,在系统内核中植入sniff......等等。
七如何防止SNIFF
防止sniff最有效的手段就是进行合理的网络分段,并在网络中使用交换机和网桥,在理想的情
况下使每一台机器都拥有自己的网络段,当然这会使你的网络建设费用增加很多,所以你可以尽量
使相互信任的机器属于同一个网段,使他们互相之间不必担心sniff的存在。并在网段于网段间进
行硬件屏障。你也可以使用加密技术对你在网络中传送的敏感数据如户ID或口令,你的银行帐号,
商业机密等进行加密,你可以选用SSH等加密手段。为了防止ARP欺骗,你可以使用永久的ARP
缓存条目,反正上面的攻击手段和原理你也看了,你就反过来想想该怎么办好了。不过有盾必有矛,
平时的安全意识才是最重要的。
(注:以下关于AntiSniff的介绍取至backend翻译整理的L0phtAntiSniff技术文档一文)
当你做做层层保护后,你还是怀疑自己的网络上存在sniff该怎么办?L0pht小组为了探测
sniff专门发布了一个软件AntiSniff,当然这个软件不是免费的:),AntiSniff工具用于检测局
域网中是否有机器处于混杂模式,AntiSniffVersion1.x被设计为运行在以太网的Windows系统中,
提供了简单易用的图形用户界面,AntiSniffVersion1.x主要工作在非交换环境下的本地网段中,
如果运行在交换环境下其功能将大打折扣。AntiSniffVer2.0将不但能在本地网段中,而且能够穿
过路由器和交换机进行工作。
◆操作系统类特殊测试
Linux内核测试
旧版本的Linux内核存在一个奇怪的特性,可被用于确定机器是否处于混杂模式。在正常情形下,
网卡会过滤和丢弃那些目标地址不是本机MAC地址或以太网广播地址的数据包。如果数据包的目标地
址为本机以太网地址或广播地址,将传送给内核进行处理,因为其认为该以太网数据帧包含了本机
的正确IP地址或该网络广播地址。如果网卡处于混杂模式,则每个数据包都会传递给操作系统进行
分析或处理。许多版本的Linux内核只检查数据包中的IP地址以确定是否存放到IP堆栈中进行处理。
为了利用这一点,AntiSniff构造一个无效以太网地址而IP地址有效的数据包。对于使用了这些内核
版本和处于混杂模式的Linux系统,由于只检查到IP地址有效而将其接收并存放到相应堆栈中。通过
在这个伪造的以太网数据帧中构造一个ICMPECHO请求,这些系统会返回响应包(如果处于混杂模式)
或忽略(如果不处于混杂模式),从而暴露其工作模式。当伪造的以太网数据帧中的IP地址设置为网络
广播地址时这个测试非常有效。AntiSniff的使用者可以修改伪造的以太网址,缺省值为66:66:66:66:66:66。
NetBSD
许多NetBSD内核具有与上述Linux内核相同的特性,不过伪造以太网数据帧中的IP地址必须设为广
播地址。
Windows95/98/NT
根据对网络驱动程序头文件的了解,可以知道当处于混杂模式时,Microsoft的操作系统会确切地检
查每个包的以太网地址。如果与网卡的以太网地址匹配,将作为目标IP地址为本机的数据包存放到相应
堆栈中处理。可以被利用的一点是系统对以太网广播包的分析。在正常情形下,例如机器工作在非混
杂模式下,网卡只向系统内核传输那些目标以太网址与其匹配或为以太网广播地址(ff:ff:ff:ff:ff:ff)
的数据包。如果机器处于混杂模式下,网络驱动程序仍然会检查每个数据包的以太网地址,但检查是否
为广播包时却只检查头8位地址是否为0xff。因此,为了使处于混杂模式的系统返回响应信息,
AntiSniff构造以太网地址为ff:00:00:00:00:00且含有正确目标IP地址的数据包,当Microsoft的操
作系统接收到这个数据包时,将根据网络驱动程序检查到的细微差别而返回响应包(如果处于混杂模式)
或丢弃这个数据包(如果处于非混杂模式)。
需要注意的是,这个检查与使用的网络驱动程序有关。Microsoft缺省的网络驱动程序具有以上特性,
大多数的厂商为了保持兼容性也继承了这些特性。不过有些网卡会在其硬件层中检查以太网地址的头8位,
所以可能会无论系统真正的状态是什么都总是返回正值。关于这类网卡和驱动程序请访问
AntiSniffVer1.x的web网站。
◆DNS测试
进行DNS测试的原因是许多攻击者使用的网络数据收集工具都对IP地址进行反向DNS解析,因为他们
希望根据域名寻找更有价值的主机。例如joepc1.foo.bar对攻击者的吸引力往往不如payroll.foo.bar
这种商业域名。此时这些工具就由被动型网络工具变为主动型网络工具了。而不监听网络通讯的机器
不会试图反向解析数据包中的IP地址。为了利用这一点,AntiSniffVer1.x使自身处于混杂模式下,
向网络发送虚假目标IP地址的数据包,然后监听是否有机器发送该虚假目标IP地址的反向DNS查询。
伪造数据包的以太网地址、检查目标、虚假目标IP地址可由用户定制。
◆网络和主机响应时间测试
这种测试已被证明是最有效的。它能够发现网络中处于混杂模式的机器,而不管其操作系统是什么。
警告,这个测试会在很短的时间内产生巨大的网络通讯流量。进行这种测试的理由是不处于混杂模式
的网卡提供了一定的硬件底层过滤机制。也就是说,目标地址非本地(广播地址除外)的数据包将被网卡
的固件丢弃。在这种情况下,骤然增加、但目标地址不是本地的网络通讯流量对操作系统的影响只会
很小。而处于混杂模式下的机器则缺乏此类底层的过滤,骤然增加、但目标地址不是本地的网络通讯
流量会对该机器造成较明显的影响(不同的操作系统/内核/用户方式会有不同)。这些变化可以通过网
络通讯流量工具监视到。
根据以上要点,AntiSniffVer1.x首先利用ICMPECHO请求及响应计算出需要检测机器的响应时间
基准和平均值。在得到这个数据后,立刻向本地网络发送大量的伪造数据包。与此同时再次发送测试
数据包以确定平均响应时间的变化值。非混杂模式的机器的响应时间变化量会很小,而混杂模式的机
器的响应时间变化量则通常会有1-4个数量级。为了对付攻击者和入侵者们最常用的多种工具,
AntiSniff进行了三种网络饱和度测试:SIXTYSIX、TCPSYN和THREEWAY。
*SIXTYSIX测试构造的数据包数据全为0x66。这些数据包不会被非混杂模式的机器接收,同时
方便使用常见的网络监听/分析工具(如tcpdump和snoop等)记录和捕获。
*TCPSYN测试构造的数据包包含有效的TCP头和IP头,同时TCP标志域的SYN位被设置。
*THREEWAY测试采取的原理基本上与TCPSYN一样,但更复杂些。在这种测试中两个实际不存在
的机器间多次建立完整的TCP三方握手通讯。它能够更好地欺骗那些骇客工具。
AntiSniffVer1.x中能够通过以上三种数据包测试发现正处于混杂模式机器的测试方法最好周
期性地进行和与以前的数据比较。响应时间测试第一次运行的数据还能够用于分析一个大型网络在
flooding和非flooding状态时的性能,并帮助工程师调整网络性能。一旦确信本地网络已运行在正
常(没有未经允许而处于混杂模式的机器)状态,就应该设置AntiSniff工具周期性运行。只要发现
某台机器性能(响应时间)发生数量级的变化,一般就能确定其正处于混杂模式。这种方法不需比较
两台独立系统间的性能数据,而只需比较同一台机器不同时候的数据就能确定该机器是否处于混杂
模式。
八结尾
本文旨在向你描述sniff的基本原理,为的是要使你不仅仅能够了解什么是sniff而已,而是要明
白sniff运转的根本原理,文章参考了大量的资料,牵涉到直接引用的已经注明出处和作者,非常的
感谢他们。在此还要感谢W.Richhard.Stevens,虽然其人已逝,但留下的TCP/IP三卷本真是造福了
大家,文章的很多地方也是拜他老人家指点迷经才得以感悟。最后还要感谢雀巢咖啡,让我得以熬
夜把这篇文章写完,呵呵,谢谢大家。

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

网络技术

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading



Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

关于作者

苗壮的头像

Email/MSN/QQ:i@miaozhuang.net

 

Calendar

<<  November 2008  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar

免费邮箱注册

注册@miaozhuang.net.cn免费邮箱

注册@miaozhuang.net免费邮箱

Tag cloud

RecentComments

Comment RSS