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