`

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

 
阅读更多

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

分享到:
评论

相关推荐

    python网络原始套接字和流量嗅探.pptx

    Python在处理网络通信时,提供了一种称为原始套接字(SOCKET_RAW)的高级功能,允许程序员直接操作网络协议栈的较低层,例如IP和ICMP协议。这对于网络诊断、流量监控和协议开发非常有用。本文将深入探讨Python中原始...

    原始套节字Sniffer源码

    在计算机网络中,套接字(Socket)是进程间通信的一种方式,而原始套接字(Raw Socket)则允许程序员访问网络协议的底层细节,包括IP头和TCP/UDP头等。通过原始套接字,我们可以直接处理网络层的数据包,而不仅仅是...

    用RawSocket实现的Sniffer的C++Builder源程序

    在实现Sniffer时,通常会使用`recvfrom`或`sendto`系统调用来接收和发送数据,以及`setsockopt`来配置套接字选项,如设置IP_HDRINCL以指示操作系统不填充IP头部。通过解析接收到的数据包,可以获取到网络通信的各种...

    Linux网络编程:原始套接字编程及实例分析.docx

    3. **分析所有经过网络的数据包**:原始套接字可以用来开发网络嗅探器(sniffer),这类工具能够监听并捕获网络中的所有数据包,无论这些数据包的目的地是何处。 4. **IP地址伪装**:在进行网络安全测试或模拟攻击时...

    sniffer嗅探器C++

    1. **套接字编程**:C++中的网络编程主要依赖于套接字API,通过创建socket、bind、listen、accept和send/recv等函数来实现网络通信。嗅探器通常需要使用RAW套接字,因为它们能直接访问网络层的数据包,不受传输层...

    利用raw socket 仿sinffer

    在IT领域,原始套接字(Raw Socket)是一种特殊的网络编程接口,允许程序员直接操作网络协议的底层细节,包括IP头部、TCP头部或UDP头部。它不像普通的套接字那样处理高层协议的数据封装和解封装,而是允许我们访问...

    网络嗅探器的设计与实现

    嗅探器的核心机制基于对网络接口卡(NIC)的编程,通过特殊的套接字——原始套接字(Raw Socket)实现。不同于常规的流式套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM),原始套接字允许程序捕获所有经过网络...

    简单sniffer的源代码

    在Linux中,我们可以使用`socket(AF_INET, SOCK_RAW, IPPROTO_IP)`来创建一个原始套接字。 2. **数据包捕获** Sniffer的核心功能是捕获网络中的数据包。在源代码中,这通常通过调用`recvfrom()`函数来实现。该函数...

    一个简单的网络嗅探程序

    网络嗅探通常涉及到使用原始套接字(raw sockets)来捕获网络层的数据包。在大多数操作系统中,原始套接字允许应用程序绕过协议栈,直接接收和发送网络层的数据,包括IP、ICMP等。这使得我们能够获取到未经过TCP/IP...

    sniffer

    在压缩包子文件的文件名称列表中提到的"自己用rawsocket写的程序",表明这是一个使用原始套接字编程实现的网络嗅探器。原始套接字允许程序员绕过传输层,直接操作网络层的数据,因此可以捕获和发送IP数据包。这通常...

    sniffer小例子

    1. **设置套接字**:使用`socket()`函数创建一个原始套接字,因为嗅探需要读取所有流入流出的数据包,而不是只关注发送到特定目的地的包,所以需要使用`SOCK_RAW`类型的套接字。 2. **绑定接口**:使用`bind()`函数...

    C++编写的sniffer类

    2. **低级套接字编程**:嗅探器需要使用原始套接字(raw sockets)来捕获网络层的数据包,而非应用层的套接字。在C++中,可以使用`socket()`、`bind()`和`recvfrom()`等函数来实现。 3. **数据包过滤**:为了减少...

    网络嗅探器Sniffer源码 C语言编写 - vs2010编译

    在C语言中,我们可以利用套接字(socket)API来创建网络连接和接收数据。具体来说,我们首先需要创建一个RAW socket,因为它允许我们直接访问网络层的数据,而不涉及传输层的协议处理。 以下是一段简单的C语言...

    go-sniffer.zip

    Go-Sniffer的核心是利用了操作系统提供的原始套接字(raw socket)接口。原始套接字允许程序绕过网络协议栈的上层处理,直接访问到网络层的数据包。通过设置套接字为RAW模式,Go-Sniffer可以捕获到网络中的所有未...

    linux下sniffer程序的实现.pdf

    在创建PF_PACKET套接字时,可以选择SOCK_DGRAM或SOCK_RAW类型。前者让内核处理以太网头部,而后者允许程序完全控制以太网头,通常用于捕获原始数据包。 在创建SOCKET时,我们指定协议类型为ETH_P_IP,这样就可以...

    python-sniffer:python中简单数据包嗅探器的简单实现

    在Python中实现数据包嗅探,首先需要了解网络套接字(socket)编程。套接字是通信的两端,允许程序之间进行网络通信。在嗅探模式下,套接字可以设置为RAW模式,这样它就能接收原始的IP数据包,而不是经过高层协议...

    一段sniffer源码

    之后,通过`ioctlsocket`函数设置套接字选项为`SIO_RCVALL`,即开启嗅探模式,使得套接字可以接收所有经过本地网络接口的数据包。最后,在一个无限循环中,使用`recv`函数接收数据包,并调用`DecodeIPPacket`函数对...

    基于python的网络嗅探分析器(含报告)

    在Python中,实现网络嗅探通常会用到如`socket`库,特别是`socket.socket(socket.AF_PACKET, socket.SOCK_RAW)`来创建原始套接字,用于接收来自链路层的数据。此外,`struct`库可能用于解码数据包的头部信息,`pcapy...

    socketsniff

    绿色无需安装套接字嗅探工具 Description SocketSniff allows you to watch the Windows Sockets (WinSock) activity of the selected process. For each created socket, the following information is displayed:...

Global site tag (gtag.js) - Google Analytics