C#的Raw Socket實現網路封包監視

来源:http://www.cnblogs.com/dreamman/archive/2016/06/01/5550224.html
-Advertisement-
Play Games

同Winsock1相比,Winsock2最明顯的就是支持了Raw Socket套接字類型,使用Raw Socket,可把網卡設置成混雜模式,在這種模式下,我們可以收到網路上的IP包,當然包括目的不是本機的IP包,通過原始套接字,我們也可以更加自如地控制Windows下的多種協議,而且能夠對網路底層的 ...


同Winsock1相比,Winsock2最明顯的就是支持了Raw Socket套接字類型,使用Raw Socket,可把網卡設置成混雜模式,在這種模式下,我們可以收到網路上的IP包,當然包括目的不是本機的IP包,通過原始套接字,我們也可以更加自如地控制Windows下的多種協議,而且能夠對網路底層的傳輸機制進行控制。  在本文例子中,nbyte.BasicClass命名空間實現了RawSocket類,它包含了我們實現數據包監視的核心技術。在實現這個類之前,需要先寫一個IP頭結構,來暫時存放一些有關網路封包的信息:

[StructLayout(LayoutKind.Explicit)] 
public struct IPHeader 

  [FieldOffset(0)] public byte ip_verlen; //I4位首部長度+4位IP版本號 
  [FieldOffset(1)] public byte ip_tos; //8位服務類型TOS 
  [FieldOffset(2)] public ushort ip_totallength; //16位數據包總長度(位元組) 
  [FieldOffset(4)] public ushort ip_id; //16位標識 
  [FieldOffset(6)] public ushort ip_offset; //3位標誌位 
  [FieldOffset(8)] public byte ip_ttl; //8位生存時間 TTL 
  [FieldOffset(9)] public byte ip_protocol; //8位協議(TCP, UDP, ICMP, Etc.) 
  [FieldOffset(10)] public ushort ip_checksum; //16位IP首部校驗和 
  [FieldOffset(12)] public uint ip_srcaddr; //32位源IP地址 
  [FieldOffset(16)] public uint ip_destaddr; //32位目的IP地址 
}


這樣,當每一個封包到達時候,可以用強制類型轉化把包中的數據流轉化為一個個IPHeader對象。 

下麵就開始寫RawSocket類了,一開始,先定義幾個參數,包括: 

private bool error_occurred; //套接字在接收包時是否產生錯誤 
public bool KeepRunning; //是否繼續進行 
private static int len_receive_buf; //得到的數據流的長度 
byte [] receive_buf_bytes; //收到的位元組 
private Socket socket = null; //聲明套接字


 還有一個常量: 

const int SIO_RCVALL = unchecked((int)0x98000001);//監聽所有的數據包


   這裡的SIO_RCVALL是指示RawSocket接收所有的數據包,在以後的IOContrl函數中要用,在下麵的構造函數中,實現了對一些變數參數的初始化: 

public RawSocket() //構造函數 

  error_occurred=false; 
  len_receive_buf = 4096; 
  receive_buf_bytes = new byte[len_receive_buf]; 
}


下麵的函數實現了創建RawSocket,並把它與終結點(IPEndPoint:本機IP和埠)綁定: 

public void CreateAndBindSocket(string IP) //建立並綁定套接字 

  socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP); 
  socket.Blocking = false; //置socket非阻塞狀態 
  socket.Bind(new IPEndPoint(IPAddress.Parse(IP), 0)); //綁定套接字 

  if (SetSocketOption()==false) error_occurred=true; 
}


 其中,在創建套接字的一句 

socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);


中有3個參數: 

第一個參數是設定地址族,MSDN上的描述是“指定 Socket 實例用來解析地址的定址方案”,當要把套接字綁定到終結點(IPEndPoint)時,需要使用InterNetwork成員,即採用IP版本4的地址格式,這也是當今大多數套接字編程所採用一個定址方案(AddressFamily)。 

第二個參數設置的套接字類型就是我們使用的Raw類型了,SocketType是一個枚舉數據類型,Raw套接字類型支持對基礎傳輸協議的訪問。通過使用 SocketType.Raw,你不光可以使用傳輸控制協議(Tcp)和用戶數據報協議(Udp)進行通信,也可以使用網際消息控制協議 (Icmp) 和 Internet 組管理協議 (Igmp) 來進行通信。在發送時,您的應用程式必須提供完整的 IP 標頭。所接收的數據報在返回時會保持其 IP 標頭和選項不變。 

第三個參數設置協議類型,Socket 類使用 ProtocolType 枚舉數據類型向 Windows Socket API 通知所請求的協議。這裡使用的是IP協議,所以要採用ProtocolType.IP參數。 

在CreateAndBindSocket函數中有一個自定義的SetSocketOption函數,它和Socket類中的SetSocketOption不同,我們在這裡定義的是具有IO控制功能的SetSocketOption,它的定義如下: 

private bool SetSocketOption() //設置raw socket 

  bool ret_value = true; 
  try 
  { 
   socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1); 
   byte []IN = new byte[4]{1, 0, 0, 0}; 
   byte []OUT = new byte[4]; 

   //低級別操作模式,接受所有的數據包,這一步是關鍵,必須把socket設成raw和IP Level才可用  SIO_RCVALL 
   int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT); 
   ret_code = OUT[0] + OUT[1] + OUT[2] + OUT[3];//把4個8位位元組合成一個32位整數 
   if(ret_code != 0) ret_value = false; 
  } 
  catch(SocketException) 
  { 
   ret_value = false; 
  } 
  return ret_value; 
}


其中,設置套接字選項時必須使套接字包含IP包頭,否則無法填充IPHeader結構,也無法獲得數據包信息。 

int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT);


是函數中最關鍵的一步了,因為,在windows中我們不能用Receive函數來接收raw socket上的數據,這是因為,所有的IP包都是先遞交給系統核心,然後再傳輸到用戶程式,當發送一個raws socket包的時候(比如syn),核心並不知道,也沒有這個數據被髮送或者連接建立的記錄,因此,當遠端主機回應的時候,系統核心就把這些包都全部丟掉,從而到不了應用程式上。所以,就不能簡單地使用接收函數來接收這些數據報。要達到接收數據的目的,就必須採用嗅探,接收所有通過的數據包,然後進行篩選,留下符合我們需要的。可以通過設置SIO_RCVALL,表示接收所有網路上的數據包。接下來介紹一下IOControl函數。MSDN解釋它說是設置套接字為低級別操作模式,怎麼低級別操作法?其實這個函數與API中的WSAIoctl函數很相似。WSAIoctl函數定義如下: 

int WSAIoctl( 
  SOCKET s, //一個指定的套接字 
  DWORD dwIoControlCode, //控制操作碼 
  LPVOID lpvInBuffer, //指向輸入數據流的指針 
  DWORD cbInBuffer, //輸入數據流的大小(位元組數) 
  LPVOID lpvOutBuffer, // 指向輸出數據流的指針 
  DWORD cbOutBuffer, //輸出數據流的大小(位元組數) 
  LPDWORD lpcbBytesReturned, //指向輸出位元組流數目的實數值 
  LPWSAOVERLAPPED lpOverlapped, //指向一個WSAOVERLAPPED結構 
  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//指向操作完成時執行的常式 
);


C#的IOControl函數不像WSAIoctl函數那麼複雜,其中只包括其中的控制操作碼、輸入位元組流、輸出位元組流三個參數,不過這三個參數已經足夠了。我們看到函數中定義了一個位元組數組:byte []IN = new byte[4]{1, 0, 0, 0}實際上它是一個值為1的DWORD或是Int32,同樣byte []OUT = new byte[4];也是,它整和了一個int,作為WSAIoctl函數中參數lpcbBytesReturned指向的值。 

因為設置套接字選項時可能會發生錯誤,需要用一個值傳遞錯誤標誌: 

public bool ErrorOccurred 

  get 
  { 
   return error_occurred; 
  } }

下麵的函數實現的數據包的接收:

//解析接收的數據包,形成PacketArrivedEventArgs事件數據類對象,並引發PacketArrival事件 
unsafe private void Receive(byte [] buf, int len) 

  byte temp_protocol=0; 
  uint temp_version=0; 
  uint temp_ip_srcaddr=0; 
  uint temp_ip_destaddr=0; 
  short temp_srcport=0; 
  short temp_dstport=0; 
  IPAddress temp_ip; 

  PacketArrivedEventArgs e=new PacketArrivedEventArgs();//新網路數據包信息事件 

  fixed(byte *fixed_buf = buf) 
  { 
   IPHeader * head = (IPHeader *) fixed_buf;//把數據流整和為IPHeader結構 
   e.HeaderLength=(uint)(head->ip_verlen & 0x0F) << 2; 

   temp_protocol = head->ip_protocol; 
   switch(temp_protocol)//提取協議類型 
   { 
    case 1: e.Protocol="ICMP"; break; 
    case 2: e.Protocol="IGMP"; break; 
    case 6: e.Protocol="TCP"; break; 
    case 17: e.Protocol="UDP"; break; 
    default: e.Protocol= "UNKNOWN"; break; 
   } 

   temp_version =(uint)(head->ip_verlen & 0xF0) >> 4;//提取IP協議版本 
   e.IPVersion = temp_version.ToString(); 

   //以下語句提取出了PacketArrivedEventArgs對象中的其他參數 
   temp_ip_srcaddr = head->ip_srcaddr; 
   temp_ip_destaddr = head->ip_destaddr; 
   temp_ip = new IPAddress(temp_ip_srcaddr); 
   e.OriginationAddress =temp_ip.ToString(); 
   temp_ip = new IPAddress(temp_ip_destaddr); 
   e.DestinationAddress = temp_ip.ToString(); 

   temp_srcport = *(short *)&fixed_buf[e.HeaderLength]; 
   temp_dstport = *(short *)&fixed_buf[e.HeaderLength+2]; 
   e.OriginationPort=IPAddress.NetworkToHostOrder(temp_srcport).ToString(); 
   e.DestinationPort=IPAddress.NetworkToHostOrder(temp_dstport).ToString(); 

   e.PacketLength =(uint)len; 
   e.MessageLength =(uint)len - e.HeaderLength; 

   e.ReceiveBuffer=buf; 
   //把buf中的IP頭賦給PacketArrivedEventArgs中的IPHeaderBuffer 
   Array.Copy(buf,0,e.IPHeaderBuffer,0,(int)e.HeaderLength); 
   //把buf中的包中內容賦給PacketArrivedEventArgs中的MessageBuffer 
   Array.Copy(buf,(int)e.HeaderLength,e.MessageBuffer,0,(int)e.MessageLength); 
  } 
  //引發PacketArrival事件 
  OnPacketArrival(e); 
}


大家註意到了,在上面的函數中,我們使用了指針這種所謂的不安全代碼,可見在C#中指針和移位運算這些原始操作也可以給程式員帶來編程上的便利。在函數中聲明PacketArrivedEventArgs類對象,以便通過OnPacketArrival(e)函數通過事件把數據包信息傳遞出去。其中PacketArrivedEventArgs類是RawSocket類中的嵌套類,它繼承了系統事件(Event)類,封裝了數據包的IP、埠、協議等其他數據包頭中包含的信息。在啟動接收數據包的函數中,我們使用了非同步操作的方法,以下函數開啟了非同步監聽的介面: 

public void Run() //開始監聽 

  IAsyncResult ar = socket.BeginReceive(receive_buf_bytes, 0, len_receive_buf, SocketFlags.None, new AsyncCallback(CallReceive), this); 
}


Socket.BeginReceive函數返回了一個非同步操作的介面,併在此介面的生成函數BeginReceive中聲明瞭非同步回調函數CallReceive,並把接收到的網路數據流傳給receive_buf_bytes,這樣就可用一個帶有非同步操作的介面參數的非同步回調函數不斷地接收數據包: 

private void CallReceive(IAsyncResult ar)//非同步回調 

  int received_bytes; 
  received_bytes = socket.EndReceive(ar); 
  Receive(receive_buf_bytes, received_bytes); 
  if (KeepRunning) Run(); 
}


此函數當掛起或結束非同步讀取後去接收一個新的數據包,這樣能保證讓每一個數據包都能夠被程式探測到。 

下麵通過聲明代理事件句柄來實現和外界的通信: 

public delegate void PacketArrivedEventHandler(Object sender, PacketArrivedEventArgs args); 
//事件句柄:包到達時引發事件 
public event PacketArrivedEventHandler PacketArrival;//聲明時間句柄函數


這樣就可以實現對數據包信息的獲取,採用非同步回調函數,可以提高接收數據包的效率,並通過代理事件把封包信息傳遞到外界。既然能把所有的封包信息傳遞出去,就可以實現對數據包的分析了:)不過RawSocket的任務還沒有完,最後不要望了關閉套接字啊: 

public void Shutdown() //關閉raw socket 

  if(socket != null) 
  { 
   socket.Shutdown(SocketShutdown.Both); 
   socket.Close(); 
  } }


以上介紹了RawSocket類通過構造IP頭獲取了包中的信息,並通過非同步回調函數實現了數據包的接收,並使用時間代理句柄和自定義的數據包信息事件類把數據包信息發送出去,從而實現了網路數據包的監視,這樣我們就可以在外部添加一些函數對數據包進行分析了。

 

轉載:http://www.51cto.com/specbook/22/4194.htm


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • .NET提供了很多序列化對象的方法,瞭解他們之間的區別才能更好地確定使用哪一種序列化方式並正確地使用。本文從下麵幾個方面對標題中的三種序列化方法進行了分析。 範圍:Property Or Field Or Both 可見性:Public or Private Or All 可訪問性:Readonly ...
  • 聲明:本系列為原創,分享本人現用框架,未經本人同意,禁止轉載!http://yuangang.cnblogs.com 希望大家好好一步一步做,所有的技術和項目,都毫無保留的提供,希望大家能自己跟著做一套,還有,請大家放心,只要大家喜歡,有人需要,絕對不會爛尾,我會堅持寫完~ 如果你感覺文章有幫助,點 ...
  • 1.參數化查詢模糊查詢 sql語句: create proc procegDataAp( @UserName nvarchar(50))asselect * from users where userName=@UserName 給參數賦值 1 <%@ Page Language="C#" Auto ...
  • 1.SQL註入:SQL註入攻擊是web應用程式的一種安全漏洞,可以將不安全的數據提交給運用程式,使應用程式在伺服器上執行不安全的sql命令。使用該攻擊可以輕鬆的登錄運用程式。 例如:該管理員賬號密碼為xiexun,該sql的正確語句應該為: 如果在沒有做任何處理的情況下,在登錄名文本框中輸入(xux ...
  • 常用快捷鍵 自動生成頭部註釋 代碼片段 NuGet Team Foundation 常用的VS快捷鍵 查看與設置快捷鍵 一般在菜單裡面我們直接就可以看到一些功能的快捷鍵。另外,可以依次通過 菜單欄-工具-選項-環境-鍵盤 中查看和設置對應功能的快捷鍵 推薦幾個我比較常用的快捷鍵 我用的是VS2015 ...
  • 1、頁面後臺代碼添加如下靜態變數: 2、在處理數據的開始,初始化total和startTime變數: 3、在處理數據過程中,不斷累加cur: 4、前端每隔200毫秒獲取進度: 5、後臺計算進度: 效果圖(文字錯了,不是“導入進度”,而是“數據處理進度:”): ...
  • 公司業務量比較大,接了很多項目,為了縮短開發周期老闆讓我牽頭搭建了一個敏捷開發框架。 我們主要的業務是做OA、CRM、ERP一類的管理系統,一個通用的後臺搭出來,再配合一些快速開發的組件開發效率能提高很多。 另外老闆一再強調要支持APP開發,一次開發能部署到安卓和IOS上。 作為開篇之作,先介紹一下 ...
  • 定製路由系統 路由系統是靈活可配置的,當然還可以通過下麵這兩種方式定製路由系統,來滿足其他需求。 1、 通過創建自定義的RouteBase實現; 2、 通過創建自定義路由處理程式實現。 創建自定義的RouteBase實現 創建自定義的RouteBase實現,需要實現一個RouteBase的派生類,而 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...