• 软件测试技术
  • 软件测试博客
  • 软件测试视频
  • 开源软件测试技术
  • 软件测试论坛
  • 软件测试沙龙
  • 软件测试资料下载
  • 软件测试杂志
  • 软件测试人才招聘
    暂时没有公告

字号: | 推荐给好友 上一篇 | 下一篇

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

发布: 2007-6-23 19:14 | 作者:   | 来源:   | 查看: 18次 | 进入软件测试论坛讨论

领测软件测试网

   

一. 摘要

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

延伸阅读

文章来源于领测软件测试网 https://www.ltesting.net/


关于领测软件测试网 | 领测软件测试网合作伙伴 | 广告服务 | 投稿指南 | 联系我们 | 网站地图 | 友情链接
版权所有(C) 2003-2010 TestAge(领测软件测试网)|领测国际科技(北京)有限公司|软件测试工程师培训网 All Rights Reserved
北京市海淀区中关村南大街9号北京理工科技大厦1402室 京ICP备10010545号-5
技术支持和业务联系:info@testage.com.cn 电话:010-51297073

软件测试 | 领测国际ISTQBISTQB官网TMMiTMMi认证国际软件测试工程师认证领测软件测试网