您的位置:首页精文荟萃软件资讯 → Raw Socket(原始套接字)实现Sniffer(嗅探)

Raw Socket(原始套接字)实现Sniffer(嗅探)

时间:2004/10/7 18:25:00来源:本站整理作者:蓝点我要评论(0)

 

Raw Socket(原始套接字)实现Sniffer(嗅探)


  一. 摘要
  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
#pragma hdrstop

#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#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 协议结构原理以及各协议之间的关系.

相关阅读 Windows错误代码大全 Windows错误代码查询激活windows有什么用Mac QQ和Windows QQ聊天记录怎么合并 Mac QQ和Windows QQ聊天记录Windows 10自动更新怎么关闭 如何关闭Windows 10自动更新windows 10 rs4快速预览版17017下载错误问题Win10秋季创意者更新16291更新了什么 win10 16291更新内容windows10秋季创意者更新时间 windows10秋季创意者更新内容kb3150513补丁更新了什么 Windows 10补丁kb3150513是什么

文章评论
发表评论

热门文章 360快剪辑怎么使用 36金山词霸如何屏幕取词百度收购PPS已敲定!3

最新文章 微信3.6.0测试版更新了微信支付漏洞会造成哪 360快剪辑怎么使用 360快剪辑软件使用方法介酷骑单车是什么 酷骑单车有什么用Apple pay与支付宝有什么区别 Apple pay与贝贝特卖是正品吗 贝贝特卖网可靠吗

人气排行 xp系统停止服务怎么办?xp系统升级win7系统方电脑闹钟怎么设置 win7电脑闹钟怎么设置office2013安装教程图解:手把手教你安装与qq影音闪退怎么办 QQ影音闪退解决方法VeryCD镜像网站逐个数,电驴资料库全集同步推是什么?同步推使用方法介绍QQ2012什么时候出 最新版下载EDiary——一款好用的电子日记本