使用RawSocket進行網絡抓包


aw socket,即原始 套接字,可以接收本機網卡上的 數據幀或者數據包,對與監聽網絡的流量和分析是很有作用的,一共可以有3種方式創建這種socket。
中文名
原始 套接字
外文名
RAW SOCKET
作    用
網卡對該 數據幀進行硬過濾
類    型
3種方式
 

目錄

  1. 簡介
  2. 總結

簡介編輯

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}\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 } }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM