1、基于Raw Socket嗅探器设计和实现 功效: 基于Raw Socket实现IP 层以上原始数据包发送和接收。 一. 摘要 Raw Socket: 原始套接字,能够用它来发送和接收 IP 层以上原始数据包, 如 ICMP, TCP, UDP... int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); Sniffer(嗅探器)基础原理和实现过程 1. 把网卡置于混杂模式; 2. 捕捉数据包; 3. 分析数据包. 二. 把网卡置于混杂模式 在正常情况下,一个网络接口应该只响应两种数据帧: l
2、和自己硬件地址相匹配数据帧 l 发向全部机器广播数据帧 假如要网卡接收发向全部机器广播数据帧,就必需把网卡置于混杂模式。 用 Raw Socket 实现代码以下: //设置 IP 头操作选项 setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag); //把 sockRaw 绑定到当地网卡上 bind(sockRaw, (PSOCKADDR)&addrLocal, sizeof(addrLocal); //让 sockRaw 接收全部数据 ioctlsocket(sockRaw, SIO_R
3、CVALL, &dwValue); flag 标志是用来设置 IP 头操作, 也就是说要亲自处理 IP 头: bool flag = ture; addrLocal 为当地地址: SOCKADDR_IN addrLocal; dwValue 为输入输出参数, 为 1 时实施, 0 时取消: DWORD dwValue = 1; 三. 捕捉数据包 #define BUFFER_SIZE 65535 char RecvBuf[BUFFER_SIZE]; //接收任意数据包 recv(sockRaw, RecvBuf, BUFFER_SIZE, 0);
4、 四. 分析数据包 用 recv()接收到数据包中包含IP、TCP 等原始信息。要分析它首先得悉道这些结构. 数据包总体结构: ---------------------------------------------- | ip header | tcp header(or x header) | data | ---------------------------------------------- IP header structure: 4 8 16 32 bit |--------|---
5、 | Ver | IHL |Typeofservice| Total length | |--------|--------|----------------|--------------------------------| | Identification | Flags | Fragment offset | |--------|--------|----------------|----------------
6、 | Time to live | Protocol | Header checksum | |--------|--------|----------------|--------------------------------| | Source address | |--------|--------|----------------|--------------------------------| | Destination address
7、 | |--------|--------|----------------|--------------------------------| | Option + Padding | |--------|--------|----------------|--------------------------------| | Data | |--------|--------|----------------|---------------
8、 TCP header structure: 16 32 bit |--------------------------------|--------------------------------| | Source port | Destination port | |--------------------------------|--------------------------------| | Sequence number
9、 | |--------------------------------|--------------------------------| | Acknowledgement number | |--------------------------------|--------------------------------| | Offset | Resrvd |U|A|P|R|S|F| Window | |--------------------------------|-------
10、 | Checksum | Urgent pointer | |--------------------------------|--------------------------------| | Option + Padding | |--------------------------------|--------------------------------| | Data
11、 | |--------------------------------|--------------------------------| 五. 实现 Sniffer 用 BCB6 写一个 Simple Sniffer 代码, 仅供参考. (需要在工程文件里加入WS2_32.LIB这个文件) //*************************************************************************// //* CPP File: WMain.cpp //* Simple Sniffer by shadowstar
12、
//*
//*************************************************************************//
#include
13、 #pragma package(smart_init) #pragma resource "*.dfm" TMainForm *MainForm; //--------------------------------------------------------------------------- __fastcall TMainForm::TMainForm(TComponent* Owner) : TForm(Owner) { WSADATA WSAData; BOOL flag = true; int
14、 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 setu
15、p 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!");
16、 //获取当地 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)&ad
17、dr_in, sizeof(addr_in)) == SOCKET_ERROR) throw Exception("bind error!"); iSortDirection = 1; } //--------------------------------------------------------------------------- __fastcall TMainForm::~TMainForm() { WSACleanup(); } //----------------------------------------------------
18、 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 Ex
19、ception("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("WSA
20、Ioctl 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.
21、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(); } } //-----
22、 AnsiString __fastcall TMainForm::GetProtocolTxt(int Protocol) { switch (Protocol) { case IPPROTO_ICMP : //1 /* control message protocol */ return PROTOCOL_STRING_ICMP_TXT; case IPPROTO_TCP : //6
23、 /* tcp */ return PROTOCOL_STRING_TCP_TXT; case IPPROTO_UDP : //17 /* user datagram protocol */ return PROTOCOL_STRING_UDP_TXT; default : return PROTOCOL_STRING_UNKNOWN_TXT; } } //--------------------------------------------------------------------------- //
24、// //* Header File: WMain.h for WMain.cpp class TMainForm //*************************************************************************// //--------------------------------------------------------------------------- #if
25、ndef WMainH
#define WMainH
//---------------------------------------------------------------------------
#define BUFFER_SIZE 65535
#include
26、e
27、CtrlClick(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 declaratio
28、ns 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(TCo
29、mponent* Owner); __fastcall ~TMainForm(); }; //--------------------------------------------------------------------------- extern PACKAGE TMainForm *MainForm; //--------------------------------------------------------------------------- #endif IP, TCP 头及部分宏定义用了 netmon.h 头, 这个文件
30、在 BCB6 include 目录下能够找得到, 其中和本程序相关内容以下: //*************************************************************************// //* Header File: netmon.h //*************************************************************************// // // IP Packet Structure // typedef struct _IP { union {
31、 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 *
32、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
33、 #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" 这个文件也有些人声称没有. //*********
34、// //* Header File: mstcpip.h //*************************************************************************// // Copyright (c) Microsoft Corporation. All rights reserved. #if _MSC_VER > 1000 #pragma once #endif /* Argument
35、 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 _WS
36、AIOW(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_MCAS
37、TIF _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 六. 小结 优点: 实现简单, 不需要做驱动程序就可实现抓包. 缺点: 数据包头不含帧信息, 不能接收到和 IP 同层其它数据包, 如 ARP, RARP;没有对数据包进行深入分析。






