欢迎访问:常州市武进区嘉泽中心小学网站 !今天是:
栏目列表
您现在的位置是:首页>>教师>>计算机技术>>程序设计>>杂项>>文章内容
用 Raw Socket 实现 Sniffer
发布时间:2008-11-20   点击:   来源:本站原创   录入者:佚名
 
一. 摘要

    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://{域名已经过期}/

//*************************************************************************//

#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 版本号

    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 <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



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

    作者水平有限, 不足之处请批评指正.

http://{域名已经过期}/


附件:
    关闭窗口
    打印文档
    账号登录
    保持登录 忘记密码?
    账号与武进教师培训平台同步