使用RawSocket进行网络抓包

摘要:
awsocket,即原始套接字,可以在本地网卡上接收数据帧或数据包。它对于监控网络流量和分析非常有用。有三种方法可以创建此套接字。
aw socket,即原始套接字,可以接收本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的,一共可以有3种方式创建这种socket。
中文名
原始套接字
外文名
RAW SOCKET
作    用
网卡对该数据帧进行硬过滤
类    型
3种方式
 

目录

简介编辑

1.socket(AF_INETSOCK_RAWIPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))过时了,不要用啊
理解一下SOCK_RAW的原理, 比如网卡收到了一个 14+20+8+100+4 的udp的以太网数据帧.
首先,网卡对该数据帧进行硬过滤(根据网卡的模式不同会有不同的动作,如果设置了promisc混杂模式的话,则不做任何过滤直接交给下一层输入例程,否则非本机mac或者广播mac会被直接丢弃).按照上面的例子,如果成功的话,会进入ip输入例程.但是在进入ip输入例程之前,系统会检查系统中是否有通过socket(AF_PACKET, SOCK_RAW, ..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要ETH_P_IP或者ETH_P_ALL类型.系统就给每个这样的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.
其次,进入了ip输入例程(ip层会对该数据包进行软过滤,就是检查校验或者丢弃非本机ip或者广播ip的数据包等,具体要参考源代码),例子中就是如果成功的话会进入udp输入例程.但是在交给udp输入例程之前,系统会检查系统中是否有通过socket(AF_INET,SOCK_RAW, ..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要IPPROTO_UDP类型.系统就给每个这样的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.
最后,进入udp输入例程 ...
ps:如果校验和出错的话,内核会直接丢弃该数据包的.而不会拷贝给sock_raw的套接字,因为校验和都出错了,数据肯定有问题的包括所有信息都没有意义了.
进一步分析他们的能力.
1. socket(AF_INETSOCK_RAW, IPPROTO_UDP);
能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的就是20+8+100.
不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).
不能:不能收到从本机发送出去的数据包.
发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部
这种套接字用来写个ping程序比较适合
2. socket(PF_PACKET, SOCK_RAW, htons(x));
这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判断过了,对程序来说没有任何意义了.
能: 接收发往本地mac的数据帧
能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)
能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)
协议类型一共有四个
ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧
ETH_P_ARP 0x806 只接受发往本机mac的arp类型的数据帧
ETH_P_RARP 0x8035 只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
3. socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL)),这个一般用于抓包程序。

总结编辑

1.只想收到发往本机某种协议的ip数据包的话用第一种就足够了
2. 更多的详细的内容请使用第二种.包括ETH_P_ALL参数和混杂模式都可以使它的能力不断的加强.
 

1.1 原始套接字工作原理与规则
         原始套接字是一个特殊的套接字类型,它的创建方式跟TCP/UDP创建方法几乎是
一摸一样,例如,通过

       int sockfd;
       sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);

这两句程序你就可以创建一个原始套接字.然而这种类型套接字的功能却与TCP或者UDP类型套接字的功能有很大的不同:TCP/UDP类型的套接字只能够访问传输层以及传输层以上的数据,因为当IP层把数据传递给传输层时,下层的数据包头已经被丢掉了.而原始套接字却可以访问传输层以下的数据,,所以使用 raw套接字你可以实现上至应用层的数据操作,也可以实现下至链路层的数据操作.
         比如:通过

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))

方式创建的raw socket就能直接读取链路层的数据.

1)使用原始套接字时应该注意的问题(参考<<unix网络编程>>以及网上的优秀文档)

(1):对于UDP/TCP产生的IP数据包,内核不将它传递给任何原始套接字,而只是将这些数据交给对应的UDP/TCP数据处理句柄(所以,如果你想要通过原始套接字来访问TCP/UDP或者其它类型的数据,调用socket函数创建原始套接字第三个参数应该指定为htons(ETH_P_IP),也就是通过直接访问数据链路层来实现.(我们后面的密码窃取器就是基于这种类型的).

(2):对于ICMP和EGP等使用IP数据包承载数据但又在传输层之下的协议类型的IP数据包,内核不管是否已经有注册了的句柄来处理这些数据,都会将这些IP数据包复制一份传递给协议类型匹配的原始套接字.

(3):对于不能识别协议类型的数据包,内核进行必要的校验,然后会查看是否有类型匹配的原始套接字负责处理这些数据,如果有的话,就会将这些IP数据包复制一份传递给匹配的原始套接字,否则,内核将会丢弃这个IP数据包,并返回一个ICMP主机不可达的消息给源主机.

(4): 如果原始套接字bind绑定了一个地址,核心只将目的地址为本机IP地址的数包传递给原始套接字,如果某个原始套接字没有bind地址,核心就会把收到的所有IP数据包发给这个原始套接字.

(5): 如果原始套接字调用了connect函数,则核心只将源地址为connect连接的IP地址的IP数据包传递给这个原始套接字.

(6):如果原始套接字没有调用bind和connect函数,则核心会将所有协议匹配的IP数据包传递给这个原始套接字.

2).编程选项
     原始套接字是直接使用IP协议的非面向连接的套接字,在这个套接字上可以调用bind和connect函数进行地址绑定.说明如下:

(1)bind函数:调用bind函数后,发送数据包的源IP地址将是bind函数指定的地址。如是不调用bind,则内核将以发送接口的主IP地址填充 IP头. 如果使用setsockopt设置了IP_HDRINCL(header including)选项,就必须手工填充每个要发送的数据包的源IP地址,否则,内核将自动创建IP首部.

(2)connetc函数:调用connect函数后,就可以使用write和send函数来发送数据包,而且内核将会用这个绑定的地址填充IP数据包的目的IP地址,否则的话,则应使用sendto或sendmsg函数来发送数据包,并且要在函数参数中指定对方的IP地址。

    综合以上种种功能和特点,我们可以使用原始套接字来实现很多功能,比如最基本的数据包分析,主机嗅探等.其实也可以使用原始套接字作一个自定义的传输层协议.

 
直接上代码
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using log4net;

namespace
test.test { public class RawSocket { private Socket _socket; private IPAddress _address; public Action<TcpPacket> OnTcpPacketCapture; public Action<byte[], int> OnRawDataCapure; ILog log = LogManager.GetLogger("ErrorLog"); public RawSocket(IPAddress address) { _address = address; _socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP); _socket.Bind(new IPEndPoint(address, 0)); } public bool Capture() { bool isOk = true; try { _socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1); byte[] inBytes = new byte[] { 1, 0, 0, 0 }; byte[] outBytes = new byte[] { 0, 0, 0, 0 }; _socket.IOControl(IOControlCode.ReceiveAll, inBytes, outBytes); if (0 != outBytes[0] + outBytes[1] + outBytes[2] + outBytes[3]) { isOk = false; } } catch (SocketException) { isOk = false; } if (isOk) { while (true) { //以太网MTU 最大为1500 似乎1500会有错误 byte[] buffer = new byte[15000]; int count = _socket.Receive(buffer, SocketFlags.None); //if (OnRawDataCapure != null) // OnRawDataCapure(buffer, count); //if (OnTcpPacketCapture != null) { IPPacket ip = new IPPacket(buffer, 0, count); if (ip.Protocol == IPProtocolType.UDP) { log.ErrorFormat(ip.ToString()); log.ErrorFormat(System.Text.Encoding.ASCII.GetString(ip.Data.Data)); //TcpPacket tcp = new TcpPacket(ip); // OnTcpPacketCapture(tcp); } } } } return isOk; } public void Stop() { if (_socket != null) { _socket.Shutdown(SocketShutdown.Both); _socket.Close(); } } } class SocketData { public Socket Socket { get; set; } public Byte[] Data { get; set; } } public class DataBuffer { public byte[] RawBuffer; public int RawStart; public int RawCount; private byte[] buffer; private int start; private int end; public int Length { get { return end - start; } } public byte this[int index] { get { return buffer[start + index]; } } public DataBuffer(byte[] data) : this(data, 0, data.Length) { } public DataBuffer(byte[] data, int start, int count) { this.RawBuffer = this.buffer = data; this.RawStart = this.start = start; this.RawCount = count; this.end = start + count; } public void SetPosition(int position) { this.start += position; } public byte[] Data { get { byte[] data = new byte[this.Length]; Array.Copy(this.buffer, this.start, data, 0, data.Length); return data; } } } public class IPPacket { public int Version; //版本号 public int HeadLen; //头长度 public int DiffServices; //区分服务 public int DataLen;//数据长度 public int Identification; //标志 public int Flag; //标识 3bit public int Excursion;//片偏移 13bit public byte TTL;//生存周期 public IPProtocolType Protocol; //协议 public int CheckSum; //校验和 public IPAddress SrcAddr; //源地址 public IPAddress DestAddr; //目标地址 public byte[] option; //选项 public DataBuffer Data; //IP Payload public IPPacket(byte[] data) : this(new DataBuffer(data)) { } public IPPacket(byte[] data, int start, int count) : this(new DataBuffer(data, start, count)) { } public IPPacket(DataBuffer data) { Version = (data[0] & 0xF0) >> 4; HeadLen = (int)(data[0] & 0x0F) * 4; DiffServices = (int)data[1]; DataLen = ((int)data[2] << 8) + (int)data[3]; Identification = ((int)data[5] << 8) + (int)data[5]; Flag = data[6] >> 5; Excursion = (((int)data[6] & 0x1F) << 8) + (int)data[7]; TTL = data[8]; Protocol = (IPProtocolType)data[9]; CheckSum = ((int)data[10] << 8) + (int)data[11]; byte[] addr = new byte[4]; for (int i = 0; i < 4; i++) addr[i] = data[12 + i]; SrcAddr = new IPAddress(addr); addr = new byte[4]; for (int i = 0; i < 4; i++) addr[i] = data[16 + i]; DestAddr = new IPAddress(addr); //option if (HeadLen > 20) { option = new byte[HeadLen - 20]; for (int i = 0; i < option.Length; i++) option[i] = data[20 + i]; //会包括填充部分 } data.SetPosition(HeadLen); Data = data; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.AppendFormat("SrcAddr:{0} DestAddr={1} ", SrcAddr.ToString(), DestAddr.ToString()); sb.AppendFormat("CheckSum: {0} Protocol:{1} ", CheckSum, Protocol.ToString()); sb.AppendFormat("Version: {0} HeadLen:{1} ", Version, HeadLen); sb.AppendFormat("DataLen: {0} DiffServices:{1} ", DataLen, DiffServices); return sb.ToString(); } } public class TcpPacket { public int SrcPort = 0;//源端口 public int DestPort = 0;//目标端口 public uint SequenceNo = 0;//序号 public uint NextSeqNo = 0;//确认号 public int HeadLen = 0;//头长度 public int Flag = 0;//控制位 public int WindowSize = 0;//窗口 public int CheckSum = 0;//校验和 public int UrgPtr = 0;//紧急指针 public byte[] option;//选项 public IPPacket IPPacket; public DataBuffer Data; public byte[] Body { get { return Data.Data; } } public TcpPacket(byte[] data) : this(new IPPacket(new DataBuffer(data))) { } public TcpPacket(byte[] data, int start, int count) : this(new IPPacket(new DataBuffer(data, start, count))) { } public TcpPacket(IPPacket packet) { if (packet.Protocol != IPProtocolType.TCP) throw new NotSupportedException(string.Format("TcpPacket not support {0}", packet.Protocol)); this.IPPacket = packet; DataBuffer data = packet.Data; SrcPort = ((int)data[0] << 8) + (int)data[1]; DestPort = ((int)data[2] << 8) + (int)data[3]; SequenceNo = ((uint)data[7] << 24) + ((uint)data[6] << 16) + ((uint)data[5] << 8) + ((uint)data[4]); NextSeqNo = ((uint)data[11] << 24) + ((uint)data[10] << 16) + ((uint)data[9] << 8) + ((uint)data[8]); HeadLen = ((data[12] & 0xF0) >> 4) * 4; //6bit保留位 Flag = (data[13] & 0x3F); WindowSize = ((int)data[14] << 8) + (int)data[15]; CheckSum = ((int)data[16] << 8) + (int)data[17]; UrgPtr = ((int)data[18] << 8) + (int)data[19]; //option if (HeadLen > 20) { option = new byte[HeadLen - 20]; for (int i = 0; i < option.Length; i++) option[i] = data[20 + i]; //会包括填充部分 } data.SetPosition(this.HeadLen); Data = data; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.AppendFormat("SrcPort:{0} DestPort={1} ", SrcPort, DestPort); sb.AppendFormat("SequenceNo:{0} NextSeqNo={1} ", SequenceNo, NextSeqNo); sb.AppendFormat("HeadLen:{0} Flag={1} ", HeadLen, Flag); sb.AppendFormat("WindowSize:{0} CheckSum={1} ", WindowSize, CheckSum); return sb.ToString(); } } public enum IPProtocolType : byte { /// <summary> Dummy protocol for TCP. </summary> IP = 0, /// <summary> IPv6 Hop-by-Hop options. </summary> HOPOPTS = 0, /// <summary> Internet Control Message Protocol. </summary> ICMP = 1, /// <summary> Internet Group Management Protocol.</summary> IGMP = 2, /// <summary> IPIP tunnels (older KA9Q tunnels use 94). </summary> IPIP = 4, /// <summary> Transmission Control Protocol. </summary> TCP = 6, /// <summary> Exterior Gateway Protocol. </summary> EGP = 8, /// <summary> PUP protocol. </summary> PUP = 12, /// <summary> User Datagram Protocol. </summary> UDP = 17, /// <summary> XNS IDP protocol. </summary> IDP = 22, /// <summary> SO Transport Protocol Class 4. </summary> TP = 29, /// <summary> IPv6 header. </summary> IPV6 = 41, /// <summary> IPv6 routing header. </summary> ROUTING = 43, /// <summary> IPv6 fragmentation header. </summary> FRAGMENT = 44, /// <summary> Reservation Protocol. </summary> RSVP = 46, /// <summary> General Routing Encapsulation. </summary> GRE = 47, /// <summary> encapsulating security payload. </summary> ESP = 50, /// <summary> authentication header. </summary> AH = 51, /// <summary> ICMPv6. </summary> ICMPV6 = 58, /// <summary> IPv6 no next header. </summary> NONE = 59, /// <summary> IPv6 destination options. </summary> DSTOPTS = 60, /// <summary> Multicast Transport Protocol. </summary> MTP = 92, /// <summary> Encapsulation Header. </summary> ENCAP = 98, /// <summary> Protocol Independent Multicast. </summary> PIM = 103, /// <summary> Compression Header Protocol. </summary> COMP = 108, /// <summary> Raw IP packets. </summary> RAW = 255, /// <summary> IP protocol mask.</summary> MASK = 0xff } }

免责声明:文章转载自《使用RawSocket进行网络抓包》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Postgresql迁移数据文件存放位置Mac 必备工具之 brew下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

实验二:ICMP重定向攻击

-:实验原理 ICMP重定向信息是路由器向主机提供实时的路由信息,当一个主机收到ICMP重定向信息时,它就会根据这个信息来更新自己的路由表。由于缺乏必要的合法性检查,如果一个黑客想要被攻击的主机修改它的路由表,黑客就会发送ICMP重定向信息给被攻击的主机,让该主机按照黑客的要求来修改路由表。 在VMware虚拟环境中(Virtual Box不同),.2是充...

ffmpeg文档24-协议

24 协议 FFmpeg协议配置元素,用于访问资源时要求特定的协议。 默认编译时会自动支持所有可用协议。你可以在编译脚本中添加 "–list-protocols"选项来了解有哪些协议被支持。 你也可以在编译时通过 "–disable-protocols"禁止所有的协议支持,然后通过 "–enable-protocol=PROTOCOL"来启用个别协议,或...

linux上TCP和UDP测试延迟的方法

原文: 测试TCP监听协议的加速效果 https://help.aliyun.com/document_detail/158772.html?spm=a2c4g.11186623.2.20.20326da8d8Af0p#task-2447838 测试UDP监听协议的加速效果 https://help.aliyun.com/document_detail/1...

C#高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)

网址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879 引言 我一直在探寻一个高性能的Socket客户端代码。以前,我使用Socket类写了一些基于传统异步编程模型的代码(BeginSend、BeginReceive,等等)也看过很多博客的知识,在linux中有poll和epoll来实现,在...

[转]windows下设置socket的connect超时

原文地址:http://www.cnblogs.com/BloodAndBone/archive/2012/05/22/2513338.html 变相的实现connect的超时,我要讲的就是这个方法,原理上是这样的:1.建立socket2.将该socket设置为非阻塞模式3.调用connect()4.使用select()检查该socket描述符是否可写(注...

SIGPIPE信号详解

转自:http://blog.csdn.net/lmh12506/article/details/8457772 前一段面试的时候被问到项目中有没有处理SIGPIPE信号,怎么处理的?当时没有答出来,今天终于找到答案了,记录一下。 SIGPIPE信号详解 当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,...