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. 分析数据包. 二. 把网卡置于混杂模式 在正常的情况下,一个网络接口应当只响应两种数据帧:
2、 l 与自己硬件地址相匹配的数据帧 l 发向所有机器的广播数据帧 假如要网卡接受发向所有机器的广播数据帧,就必须把网卡置于混杂模式。 用 Raw Socket 实现代码如下: //设立 IP 头操作选项 setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag); //把 sockRaw 绑定到本地网卡上 bind(sockRaw, (PSOCKADDR)&addrLocal, sizeof(addrLocal); //让 sockRaw 接受所有的数据 ioctlsocket(sockRa
3、w, SIO_RCVALL, &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_S
4、IZE, 0); 四. 分析数据包 用 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 | |--------|--------|----------------|--------------------------------| | Destina
7、tion address | |--------|--------|----------------|--------------------------------| | Option + Padding | |--------|--------|----------------|--------------------------------| | Data | |--------|--------|----------------|---
8、 TCP header structure: 16 32 bit |--------------------------------|--------------------------------| | Source port | Destination port | |--------------------------------|--------------------------------| | Sequ
9、ence number | |--------------------------------|--------------------------------| | 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 b
12、y shadowstar
//*
//*************************************************************************//
#include
13、 #pragma package(smart_init) #pragma resource "*.dfm" TMainForm *MainForm; //--------------------------------------------------------------------------- __fastcall TMainForm::TMainForm(TComponent* Owner) : TForm(Owner) { WSADATA WSAData; BOOL flag =
14、 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 Exceptio
15、n("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("gethostnam
16、e 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,
17、PSOCKADDR)&addr_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) !
19、 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
20、 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(Get
21、ProtocolTxt(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(); }
22、 } //--------------------------------------------------------------------------- AnsiString __fastcall TMainForm::GetProtocolTxt(int Protocol) { switch (Protocol) { case IPPROTO_ICMP : //1 /* control message protocol */ return PROTOCOL_STRING_ICMP_TXT; case IPPROTO_T
23、CP : //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; } } //-------------------------------------------------------------------------
24、 //*************************************************************************// //* Header File: WMain.h for WMain.cpp class TMainForm //*************************************************************************// //--------------------------------------------------------------------
25、
#ifndef WMainH
#define WMainH
//---------------------------------------------------------------------------
#define BUFFER_SIZE 65535
#include 26、hpp>
#include 27、 __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: // 28、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;
__fastcal 29、l TMainForm(TComponent* Owner);
__fastcall ~TMainForm();
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif
IP, TCP 头及一些宏定义用了 n 30、etmon.h 的头, 这个文献在 BCB6 的 include 目录下可以找得到, 其中与本程序相关内容如下:
//*************************************************************************//
//* Header File: netmon.h
//*************************************************************************//
//
// IP Packet Structure
//
typedef struct _IP 31、
{
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;
typede 32、f 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;
// 33、 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"
这个文献也有人声称没 34、有.
//*************************************************************************//
//* Header File: mstcpip.h
//*************************************************************************//
// Copyright (c) Microsoft Corporation. All rights reserved.
#if _MSC_VER > 1000
#pragma once
#endif 35、
/* 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_RCVA 36、LL_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)
#defin 37、e 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
六. 小结
优点: 实现简朴, 不需要做驱动程序就可实现抓包.
缺陷: 数据包头不含帧信息, 不能接受到与 IP 同层的其它数据包, 如 ARP, RARP;没有对数据包进行进一步的分析。






