用Visual C 實現網路封包監視

2021-04-15 13:44:41 字數 4128 閱讀 9871

本文向大家介紹windows sockets的一些關於用c#實現的原始套接字(raw socket)的程式設計,以及在此基礎上實現的網路封包監視技術。同winsock1相比,winsock2最明顯的就是支援了raw socket套接字型別,使用raw socket,可把網絡卡設定成混雜模式,在這種模式下,我們可以收到網路上的ip包,當然包括目的不是本機的ip包,通過原始套接字,我們也可以更加自如地控制windows下的多種協議,而且能夠對網路底層的傳輸機制進行控制。

在本文例子中,我在nbyte.basicclass命名空間實現了rawsocket類,它包含了我們實現資料報監視的核心技術。在實現這個類之前,需要先寫乙個ip頭結構,來暫時存放一些有關網路封包的資訊:

[structlayout(layoutkind.explicit)]

public struct ipheader

這樣,當每乙個封包到達時候,可以用強制型別轉化把包中的資料流轉化為乙個個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() //建構函式

下面的函式實現了建立rawsocket,並把它與終結點(ipendpoint:本機ip和埠)繫結:

public void createandbindsocket(string ip) //建立並繫結套接字

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

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

; 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)

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, //指向輸出位元組流數目的實數值

); c#的iocontrol函式不像wsaioctl函式那麼複雜,其中只包括其中的控制操作碼、輸入位元組流、輸出位元組流三個引數,不過這三個引數已經足夠了。我們看到函式中定義了乙個位元組陣列:byte in = new byte[4]實際上它是乙個值為1的dword或是int32,同樣byte out = new byte[4];也是,它整和了乙個int,作為wsaioctl函式中引數lpcbbytesreturned指向的值。

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

public bool erroroccurred }

下面的函式實現的資料報的接收:

//解析接收的資料報,形成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

本文**

用Visual C 輕鬆實現報表處理

資料庫在企業軟體系統中應用廣泛,而報表的顯示與列印成為此類軟體必備的功能 前言資料庫在企業軟體系統中應用廣泛,而報表的顯示與列印成為此類軟體必備的功能。可惜vc 並沒有整合報表處理工具,但其強大的功能再加上市面上功能完備的報表處理工具使這一工作變得容易,本文介紹了在vc 環境中利用seagate公司...

用Visual C 實現排序演算法大全

1.引言 2005年10月25 26日,包括筆者在內的十多位成員組隊參加了武漢原動力的野外拓展 outward bound 在攀岩懸崖之前,教官組織了這樣的乙個遊戲專案 教官將團隊裡的所有成員分開,然後用布條蒙上大家的眼睛,接著給每人乙個3位或4位的數字。他要求成員們蒙著眼睛集合,在不說話也看不到彼...

用Visual C 實現遠端執行緒嵌入技術

遠端執行緒技術指的是通過在另乙個程序中建立遠端執行緒的方法進入那個程序的記憶體位址空間。我們知道,在程序中,可以通過createthread函式建立執行緒,被建立的新執行緒與主線程 就是程序啟動時被同時自動建立的那個執行緒 共享位址空間以及其他的資源。但是很少有人知道,通過createremotet...