您的位置:首页 > 理论基础 > 计算机网络

使用RawSocket进行网络抓包

2016-04-28 13:47 831 查看
aw socket,即原始套接字,可以接收本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的,一共可以有3种方式创建这种socket。

中文名原始套接字外文名RAW SOCKET作 用网卡对该数据帧进行硬过滤类 型3种方式

目录

1 简介

2 总结

简介编辑

1.socket(AF_INET, SOCK_RAW, IPPROTO_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_INET, SOCK_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创建方法几乎是
一摸一样,例如,通过

CODE:
[Copy to clipboard]

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

CODE:
[Copy to clipboard]

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}\r\n", SrcAddr.ToString(), DestAddr.ToString());
sb.AppendFormat("CheckSum: {0} Protocol:{1}\r\n", CheckSum, Protocol.ToString());
sb.AppendFormat("Version: {0} HeadLen:{1}\r\n", Version, HeadLen);
sb.AppendFormat("DataLen: {0} DiffServices:{1}\r\n", 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}\r\n", SrcPort, DestPort);
sb.AppendFormat("SequenceNo:{0} NextSeqNo={1}\r\n", SequenceNo, NextSeqNo);
sb.AppendFormat("HeadLen:{0} Flag={1}\r\n", HeadLen, Flag);
sb.AppendFormat("WindowSize:{0} CheckSum={1}\r\n", 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
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: