Raw Socket(原始套接字)實(shí)現(xiàn)Sniffer(嗅探)一. 摘要 Raw Socket: 原始套接字 可以用它來(lái)發(fā)送和接收 IP 層以上的原始數(shù)據(jù)包, 如 ICMP, TCP, UDP...
int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
這樣我們就創(chuàng)建了一個(gè) Raw Socket
Sniffer: 嗅探器 關(guān)于嗅探器的原理我想大多數(shù)人可能都知道 1. 把網(wǎng)卡置于混雜模式; 2. 捕獲數(shù)據(jù)包; 3. 分析數(shù)據(jù)包.
但具體的實(shí)現(xiàn)知道的人恐怕就不是那么多了. 好, 現(xiàn)在讓我們用 Raw Socket 的做一個(gè)自已的 Sniffer.
二. 把網(wǎng)卡置于混雜模式 在正常的情況下,一個(gè)網(wǎng)絡(luò)接口應(yīng)該只響應(yīng)兩種數(shù)據(jù)幀: 一種是與自己硬件地址相匹配的數(shù)據(jù)幀 一種是發(fā)向所有機(jī)器的廣播數(shù)據(jù)幀 如果要網(wǎng)卡接收所有通過(guò)它的數(shù)據(jù), 而不管是不是發(fā)給它的, 那么必須把網(wǎng)卡置于混雜模式. 也就是說(shuō)讓它的思維混亂, 不按正常的方式工作. 用 Raw Socket 實(shí)現(xiàn)代碼如下:
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag); //設(shè)置 IP 頭操作選項(xiàng) bind(sockRaw, (PSOCKADDR)&addrLocal, sizeof(addrLocal); //把 sockRaw 綁定到本地網(wǎng)卡上 ioctlsocket(sockRaw, SIO_RCVALL, &dwValue); //讓 sockRaw 接受所有的數(shù)據(jù)
flag 標(biāo)志是用來(lái)設(shè)置 IP 頭操作的, 也就是說(shuō)要親自處理 IP 頭: bool flag = ture; addrLocal 為本地地址: SOCKADDR_IN addrLocal; dwValue 為輸入輸出參數(shù), 為 1 時(shí)執(zhí)行, 0 時(shí)取消: DWORD dwValue = 1; 沒(méi)想到這么簡(jiǎn)單吧?
三. 捕獲數(shù)據(jù)包 你的 sockRaw 現(xiàn)在已經(jīng)在工作了, 可以在局域網(wǎng)內(nèi)其它的電腦上用 Sniffer 檢測(cè)工具檢測(cè)一下, 看你的網(wǎng)卡是否處于混雜模式(比如 DigitalBrain 的 ARPKiller). 不能讓他白白的浪費(fèi)資源啊, 抓包!
recv(sockRaw, RecvBuf, BUFFER_SIZE, 0); //接受任意數(shù)據(jù)包
#define BUFFER_SIZE 65535 char RecvBuf[BUFFER_SIZE]; 越來(lái)越發(fā)現(xiàn) Sniffer 原來(lái)如此的簡(jiǎn)單了, 這么一個(gè)函數(shù)就已經(jīng)完成抓取數(shù)據(jù)包的任務(wù)了.
四. 分析數(shù)據(jù)包 這回抓來(lái)的包和平常用 Socket 接受的包可就不是一回事兒了, 里面包含 IP, TCP 等原始信息. 要分析它首先得知道這些結(jié)構(gòu). 數(shù)據(jù)包的總體結(jié)構(gòu): ---------------------------------------------- | 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 | |--------------------------------|--------------------------------|
五. 實(shí)現(xiàn) Sniffer OK! 現(xiàn)在都清楚了, 還等什么. 下面是我用 BCB6 寫(xiě)的一個(gè) Simple Sniffer 的代碼, 僅供參考. (需要在工程文件里加入WS2_32.LIB這個(gè)文件) //*************************************************************************// //* 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) { WSADATA WSAData; BOOL flag = true; int nTimeout = 1000; char LocalName[16]; struct hostent *pHost;
//檢查 Winsock 版本號(hào) 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!");
//設(shè)置IP頭操作選項(xiàng) if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)) == SOCKET_ERROR) throw Exception("setsockopt IP_HDRINCL error!");
//獲取本機(jī)名 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; //設(shè)置 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"; //設(shè)置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 <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 { __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
偷了個(gè)懶, IP, TCP 頭及一些宏定義用了 netmon.h 的頭, 這個(gè)文件在 BCB6 的 include 目錄下可以找得到, 其中與本程序相關(guān)內(nèi)容如下:
//*************************************************************************// //* 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"
這個(gè)文件也有人聲稱沒(méi)有. //*************************************************************************// //* 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
現(xiàn)在我們自已的 Sniffer 就做好了, Run, Start......哇, 這么多數(shù)據(jù)包, 都是從這一臺(tái)機(jī)器上發(fā)出的, 它在干什么? 原來(lái) Adminstrator 密碼為空, 中了尼姆達(dá)病毒!
六. 小結(jié) 優(yōu)點(diǎn): 實(shí)現(xiàn)簡(jiǎn)單, 不需要做驅(qū)動(dòng)程序就可實(shí)現(xiàn)抓包. 缺點(diǎn): 數(shù)據(jù)包頭不含幀信息, 不能接收到與 IP 同層的其它數(shù)據(jù)包, 如 ARP, RARP... 這里提供的程序僅僅是一個(gè) Sniffer 的例子, 沒(méi)有對(duì)數(shù)據(jù)包進(jìn)行進(jìn)一步的分析. 寫(xiě)此文的目的在于熟悉Raw Socket 編程方法, 了解 TCP/IP 協(xié)議結(jié)構(gòu)原理以及各協(xié)議之間的關(guān)系.
|