科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件用C#的Raw Socket实现网络封包监视(2)

用C#的Raw Socket实现网络封包监视(2)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在CreateAndBindSocket函数中有一个自定义的SetSocketOption函数,它和Socket类中的SetSocketOption不同

来源:soft6 2008年5月16日

关键字: 封包监视 网络 C# Windows

  • 评论
  • 分享微博
  • 分享邮件

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

}

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章