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

1 摘要

   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这个文件) 

<span style="color: rgb(51, 51, 51); font-family: 宋体; font-size: 14px; line-height: 28px; text-indent: 28px; background-color: rgb(240, 240, 240);">//*************************************************************************// </span>
//* 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) 
{ 
<span style="white-space:pre">	</span>WSADATA WSAData; 
<span style="white-space:pre">	</span>BOOL  flag  = true; 
<span style="white-space:pre">	</span>int   nTimeout = 1000; 
<span style="white-space:pre">	</span>char  LocalName[16]; 
<span style="white-space:pre">	</span>struct hostent *pHost;

<span style="white-space:pre">	</span>//检查 Winsock 版本号 
<span style="white-space:pre">	</span>if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0) 
<span style="white-space:pre">		</span>throw Exception("WSAStartup error!");

<span style="white-space:pre">	</span>//初始化 Raw Socket 
<span style="white-space:pre">	</span>if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == INVALID_SOCKET) 
<span style="white-space:pre">		</span>throw Exception("socket setup error!");

<span style="white-space:pre">	</span>//设置IP头操作选项 
<span style="white-space:pre">	</span>if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)) == SOCKET_ERROR) 
<span style="white-space:pre">		</span>throw Exception("setsockopt IP_HDRINCL error!");

<span style="white-space:pre">	</span>//获取本机名 
<span style="white-space:pre">	</span>if (gethostname((char*)LocalName, sizeof(LocalName)-1) == SOCKET_ERROR) 
<span style="white-space:pre">		</span>throw Exception("gethostname error!");

<span style="white-space:pre">	</span>//获取本地 IP 地址 
<span style="white-space:pre">	</span>if ((pHost = gethostbyname((char*)LocalName)) == NULL) 
<span style="white-space:pre">		</span>throw Exception("gethostbyname error!");

<span style="white-space:pre">	</span>addr_in.sin_addr  = *(in_addr *)pHost->h_addr_list[0]; //IP 
<span style="white-space:pre">	</span>addr_in.sin_family = AF_INET; 
<span style="white-space:pre">	</span>addr_in.sin_port  = htons(57274);

<span style="white-space:pre">	</span>//把 sock 绑定到本地地址上 
<span style="white-space:pre">	</span>if (bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) 
<span style="white-space:pre">		</span>throw Exception("bind error!");
<span style="white-space:pre">	</span>
iSortDirection = 1; 
} 
//--------------------------------------------------------------------------- 
__fastcall TMainForm::~TMainForm() 
{ 
<span style="white-space:pre">	</span>WSACleanup(); 
} 
//---------------------------------------------------------------------------

void __fastcall TMainForm::btnCtrlClick(TObject *Sender) 
{ 
<span style="white-space:pre">	</span>TListItem *Item; 
<span style="white-space:pre">	</span>DWORD dwValue; 
<span style="white-space:pre">	</span>int nIndex = 0;

<span style="white-space:pre">	</span>if (btnCtrl->Caption == "&Start") 
<span style="white-space:pre">	</span>{ 
<span style="white-space:pre">		</span>dwValue = 1; 
<span style="white-space:pre">		</span>//设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包 
<span style="white-space:pre">		</span>if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0) 
<span style="white-space:pre">			</span>throw Exception("ioctlsocket SIO_RCVALL error!"); 
<span style="white-space:pre">		</span>bStop = false; 
<span style="white-space:pre">		</span>btnCtrl->Caption = "&Stop"; 
<span style="white-space:pre">		</span>lsvPacket->Items->Clear(); 
<span style="white-space:pre">	</span>} 
<span style="white-space:pre">	</span>else 
<span style="white-space:pre">	</span>{ 
<span style="white-space:pre">		</span>dwValue = 0; 
<span style="white-space:pre">		</span>bStop = true; 
<span style="white-space:pre">		</span>btnCtrl->Caption = "&Start"; 
<span style="white-space:pre">		</span>//设置SOCK_RAW为SIO_RCVALL,停止接收 
<span style="white-space:pre">		</span>if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0) 
<span style="white-space:pre">			</span>throw Exception("WSAIoctl SIO_RCVALL error!"); 
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>while (!bStop) 
<span style="white-space:pre">	</span>{ 
<span style="white-space:pre">		</span>if (recv(sock, RecvBuf, BUFFER_SIZE, 0) > 0) 
<span style="white-space:pre">		</span>{ 
<span style="white-space:pre">			</span>nIndex++; 

<span style="white-space:pre">			</span>ip = *(IP*)RecvBuf; 
<span style="white-space:pre">			</span>tcp = *(TCP*)(RecvBuf + (ip.HdrLen & IP_HDRLEN_MASK));

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

AnsiString __fastcall TMainForm::GetProtocolTxt(int Protocol) 
{ 
<span style="white-space:pre">	</span>switch (Protocol) 
<span style="white-space:pre">	</span>{ 
<span style="white-space:pre">	</span>case IPPROTO_ICMP :      //1        /* control message protocol */ 
<span style="white-space:pre">		</span>return PROTOCOL_STRING_ICMP_TXT; 
<span style="white-space:pre">	</span>case IPPROTO_TCP :      //6        /* tcp */ 
<span style="white-space:pre">		</span>return PROTOCOL_STRING_TCP_TXT; 
<span style="white-space:pre">	</span>case IPPROTO_UDP :      //17       /* user datagram protocol */ 
<span style="white-space:pre">		</span>return PROTOCOL_STRING_UDP_TXT; 
<span style="white-space:pre">	</span>default : 
<span style="white-space:pre">		</span>return PROTOCOL_STRING_UNKNOWN_TXT; 
<span style="white-space:pre">	</span>} 
} 
//---------------------------------------------------------------------------
//*************************************************************************// 
//* 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 
{ 
<span style="white-space:pre">	</span>__published: // IDE-managed Components 
<span style="white-space:pre">	</span>TPanel *Panel1; 
<span style="white-space:pre">	</span>TButton *btnCtrl; 
<span style="white-space:pre">	</span>TListView *lsvPacket; 
<span style="white-space:pre">	</span>TLabel *Label1; 
<span style="white-space:pre">	</span>void __fastcall btnCtrlClick(TObject *Sender); 
<span style="white-space:pre">	</span>void __fastcall lsvPacketColumnClick(TObject *Sender, TListColumn *Column); 
<span style="white-space:pre">	</span>void __fastcall lsvPacketCompare(TObject *Sender, TListItem *Item1, TListItem *Item2, int Data, int &Compare); 
<span style="white-space:pre">	</span>void __fastcall Label1Click(TObject *Sender); 
<span style="white-space:pre">	</span>private: // User declarations 
<span style="white-space:pre">		</span>AnsiString __fastcall GetProtocolTxt(int Protocol); 
<span style="white-space:pre">	</span>public: // User declarations 
<span style="white-space:pre">		</span>SOCKET   sock; 
<span style="white-space:pre">		</span>SOCKADDR_IN addr_in; 
<span style="white-space:pre">		</span>IP     ip; 
<span style="white-space:pre">		</span>TCP     tcp; 
<span style="white-space:pre">		</span>PSUHDR   psdHeader; 
<span style="white-space:pre">		</span>char    RecvBuf[BUFFER_SIZE]; 
<span style="white-space:pre">		</span>bool    bStop;

<span style="white-space:pre">		</span>int iSortDirection; 
<span style="white-space:pre">		</span>int iColumnToSort; 

<span style="white-space:pre">		</span>__fastcall TMainForm(TComponent* Owner); 
<span style="white-space:pre">		</span>__fastcall ~TMainForm(); 
}; 
//--------------------------------------------------------------------------- 
extern PACKAGE TMainForm *MainForm; 
//--------------------------------------------------------------------------- 
#endif

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

//*************************************************************************// 
//* Header File: netmon.h 
//*************************************************************************// 
// 
// IP Packet Structure 
// 
typedef struct _IP 
{ 
<span style="white-space:pre">	</span>union 
<span style="white-space:pre">	</span>{ 
<span style="white-space:pre">		</span>BYTE  Version; 
<span style="white-space:pre">		</span>BYTE  HdrLen; 
<span style="white-space:pre">	</span>}; 
<span style="white-space:pre">	</span>BYTE ServiceType; 
<span style="white-space:pre">	</span>WORD TotalLen; 
<span style="white-space:pre">	</span>WORD ID; 
<span style="white-space:pre">	</span>union 
<span style="white-space:pre">	</span>{ 
<span style="white-space:pre">		</span>WORD  Flags; 
<span style="white-space:pre">		</span>WORD  FragOff; 
<span style="white-space:pre">	</span>}; 
<span style="white-space:pre">	</span>BYTE TimeToLive; 
<span style="white-space:pre">	</span>BYTE Protocol; 
<span style="white-space:pre">	</span>WORD HdrChksum; 
<span style="white-space:pre">	</span>DWORD  SrcAddr; 
<span style="white-space:pre">	</span>DWORD  DstAddr; 
<span style="white-space:pre">	</span>BYTE Options[0]; 
} IP;
typedef IP * LPIP; 
typedef IP UNALIGNED * ULPIP;
// 
// TCP Packet Structure 
// 
typedef struct _TCP 
{ 
<span style="white-space:pre">	</span>WORD SrcPort; 
<span style="white-space:pre">	</span>WORD DstPort; 
<span style="white-space:pre">	</span>DWORD SeqNum; 
<span style="white-space:pre">	</span>DWORD AckNum; 
<span style="white-space:pre">	</span>BYTE DataOff; 
<span style="white-space:pre">	</span>BYTE Flags; 
<span style="white-space:pre">	</span>WORD Window; 
<span style="white-space:pre">	</span>WORD Chksum; 
<span style="white-space:pre">	</span>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 { 
<span style="white-space:pre">	</span>u_long onoff; 
<span style="white-space:pre">	</span>u_long keepalivetime; 
<span style="white-space:pre">	</span>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 协议结构原理以及各协议之间的关系.

Raw Socket是一种底层的网络通信方式,它可以直接访问网络层和传输层的协议,因此可以用来实现网络嗅探器(Sniffer)。在Linux系统中,可以使用C语言编写Raw Socket程序,以下是一个基于Raw Socket的简单Sniffer实现: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <netinet/ip.h> #define BUFFER_SIZE 65536 void sniffer(char *interface) { int sock_raw; struct sockaddr_in saddr; unsigned char *buffer = (unsigned char*)malloc(BUFFER_SIZE); sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP); if (sock_raw < 0) { printf("Socket Error\n"); return; } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(0); saddr.sin_addr.s_addr = inet_addr(interface); if (bind(sock_raw, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { printf("Bind Error\n"); return; } while(1) { int data_size = recvfrom(sock_raw, buffer, BUFFER_SIZE, 0, NULL, NULL); if (data_size < 0) { printf("Recvfrom Error\n"); return; } struct ethhdr *eth = (struct ethhdr *)buffer; if (ntohs(eth->h_proto) != ETH_P_IP) { continue; } struct iphdr *ip = (struct iphdr *)(buffer + sizeof(struct ethhdr)); printf("Source IP: %s\n", inet_ntoa(*(struct in_addr *)&ip->saddr)); printf("Dest IP: %s\n", inet_ntoa(*(struct in_addr *)&ip->daddr)); printf("\n"); } close(sock_raw); free(buffer); } int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s interface\n", argv[0]); return 0; } sniffer(argv[1]); return 0; } ``` 在上面的代码中,我们使用了socket()函数创建了一个原始套接字(AF_INET、SOCK_RAW、IPPROTO_TCP),并使用bind()函数将其绑定到指定的网络接口上。在循环中,我们使用recvfrom()函数接收数据包,并对其进行解析,只输出IP协议的包。注意,为了方便输出,我们使用了inet_ntoa()函数将IP地址转换为字符串格式。 要运行这个程序,需要使用root权限,并指定一个网络接口作为参数,例如: ``` sudo ./sniffer eth0 ``` 其中,eth0是你要监听的网络接口。运行后,程序将会输出所有经过该接口的IP协议数据包的源IP地址和目的IP地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值