Raw Socket(原始套接字)实现Sniffer(嗅探)
来源:互联网 发布:中医真的有用吗 知乎 编辑:程序博客网 时间:2024/05/17 05:17
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(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- Raw Socket(原始套接字)
- 原始套接字1(Raw Socket)
- 原始套接字2(Raw Socket)
- 原始套接字Raw Socket基础-- WSADATA wsaData(转)
- raw socket(原始套接字)工作原理与规则
- raw socket(原始套接字)工作原理与规则
- Linux下的raw Socket(原始套接字)编程
- eclipse插件收藏
- loadrunner 破解
- 一套web服务系统架构总结
- Sublime Text 3 开发编辑神器 以及插件安装。(下)
- Android Launcher2 源码分析 续
- Raw Socket(原始套接字)实现Sniffer(嗅探)
- c++ primer -- 泛型算法
- C#简介
- HTML5_文本元素
- cocos lua 加密与解密 混淆 (版本cocos3.4)
- 机器视觉面试总结
- SQLite的多线程应用与iOS开发
- 经验及技巧:WindowsPhone获取是否进行拨电话信息[使用PhoneCallTask]
- 再转一篇:linux命令之sed, awk, grep, cut篇