簡介編輯
總結編輯
1.1 原始套接字工作原理與規則
原始套接字是一個特殊的套接字類型,它的創建方式跟TCP/UDP創建方法幾乎是
一摸一樣,例如,通過
sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);
這兩句程序你就可以創建一個原始套接字.然而這種類型套接字的功能卻與TCP或者UDP類型套接字的功能有很大的不同:TCP/UDP類型的套接字只能夠訪問傳輸層以及傳輸層以上的數據,因為當IP層把數據傳遞給傳輸層時,下層的數據包頭已經被丟掉了.而原始套接字卻可以訪問傳輸層以下的數據,,所以使用 raw套接字你可以實現上至應用層的數據操作,也可以實現下至鏈路層的數據操作.
比如:通過
方式創建的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地址。
綜合以上種種功能和特點,我們可以使用原始套接字來實現很多功能,比如最基本的數據包分析,主機嗅探等.其實也可以使用原始套接字作一個自定義的傳輸層協議.
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 } }