rawsocket:C# Raw Socket实现网络封包监视来源: 发布时间:星期四, 2009年2月12日 浏览:86次 评论:0
谈起编程大家也许会想起QQ和IE没错还有许多网络工具如P2P、NetMeeting等在应用层实现应用也是用来实现Socket是个网络编程接口实现于网络应用层WindowsSocket包括了套系统组件充分利用了MicrosoftWindows消息驱动特点Socket规范标准1.1版是在1993年1月发行并广泛用于此后出现Windows9x操作系统中Socket规范标准2.2版(其在Windows平台上版本是Winsock2.2也叫Winsock2)在1996年5月发行WindowsNT5.0及以后版本Windows系统支持Winsock2在Winsock2中支持多个传输协议原始套接字重叠I/O模型、服务质量控制等 本文向大家介绍WindowsSockets些有关用C#实现原始套接字(RawSocket)编程以及在此基础上实现网络封包监视技术同Winsock1相比Winsock2最明显就是支持了RawSocket套接字类型使用RawSocket可把网卡设置成混杂模式在这种模式下我们可以收到网络上IP包当然包括目不是本机IP包通过原始套接字我们也可以更加自如地控制Windows下多种协议而且能够对网络底层传输机制进行控制 在本文例子中我在n.BasicClass命名空间实现了RawSocket类它包含了我们实现数据包监视核心技术在实现这个类的前需要先写个IP头结构来暂时存放些有关网络封包信息: [StructLayout(LayoutKind.Explicit)] publicstructIPHeader { [FieldOff(0)]publicip_verlen;//I4位首部长度+4位IP版本号 [FieldOff(1)]publicip_tos;//8位服务类型TOS [FieldOff(2)]publicuip_totallength;//16位数据包总长度(字节) [FieldOff(4)]publicuip_id;//16位标识 [FieldOff(6)]publicuip_off;//3位标志位 [FieldOff(8)]publicip_ttl;//8位生存时间TTL [FieldOff(9)]publicip_protocol;//8位协议(TCP,UDP,ICMP,Etc.) [FieldOff(10)]publicuip_checksum;//16位IP首部校验和 [FieldOff(12)]publicuip_srcaddr;//32位源IP地址 [FieldOff(16)]publicuip_destaddr;//32位目IP地址 } 这样当每个封包到达时候可以用强制类型转化把包中数据流转化为个个IPHeader对象 下面就开始写RawSocket类了开始先定义几个参数包括: privateboolerror_occurred;//套接字在接收包时是否产生 publicboolKeepRunning;//是否继续进行 privatelen_receive_buf;//得到数据流长度 receive_buf_s;//收到字节 privateSocket=null;//声明套接字 还有个常量: constSIO_RCVALL=unchecked(()0x98000001);//监听所有数据包 这里SIO_RCVALL是指示RawSocket接收所有数据包在以后IOContrl中要用在下面构造中实现了对些变量参数化: publicRawSocket//构造 { error_occurred=false; len_receive_buf=4096; receive_buf_s=[len_receive_buf]; } 下面实现了创建RawSocket并把它和终结点(IPEndPo:本机IP和端口)绑定: publicvoidCreateAndBindSocket(IP)//建立并绑定套接字 { =Socket(AddressFamily.InterNetwork,SocketType.Raw,ProtocolType.IP); .Blocking=false;//置非阻塞状态 .Bind(IPEndPo(IPAddress.Parse(IP),0));//绑定套接字 (SetSocketOptionfalse)error_occurred=true; } 其中在创建套接字句=Socket(AddressFamily.InterNetwork,SocketType.Raw,ProtocolType.IP);中有3个参数: 第个参数是设定地址族MSDN上描述是“指定Socket例子用来解析地址寻址方案”当要把套接字绑定到终结点(IPEndPo)时需要使用InterNetwork成员即采用IP版本4地址格式这也是当今大多数套接字编程所采用个寻址方案(AddressFamily) 第 2个参数设置套接字类型就是我们使用Raw类型了SocketType是个枚举数据类型Raw套接字类型支持对基础传输协议访问通过使用SocketType.Raw你不光可以使用传输控制协议(Tcp)和用户数据报协议(Udp)进行通信也可以使用网际消息控制协议(Icmp)和Internet组管理协议(Igmp)来进行通信在发送时您应用必须提供完整IP标头所接收数据报在返回时会保持其IP标头和选项不变 第 3个参数设置协议类型Socket类使用ProtocolType枚举数据类型向WindowsSocketAPI通知所请求协议这里使用是IP协议所以要采用ProtocolType.IP参数 在CreateAndBindSocket中有个自定义SetSocketOption它和Socket类中SetSocketOption区别我们在这里定义是具有IO控制功能SetSocketOption它定义如下: privateboolSetSocketOption//设置raw { boolret_value=true; try { .SetSocketOption(SocketOptionLevel.IP,SocketOptionName.HeaderIncluded,1); IN=[4]{1,0,0,0}; OUT=[4]; //低级别操作模式,接受所有数据包这步是关键必须把设成raw和IPLevel才可用SIO_RCVALL ret_code=.IOControl(SIO_RCVALL,IN,OUT); ret_code=OUT[0]+OUT[1]+OUT[2]+OUT[3];//把4个8位字节合成个32位整数 (ret_code!=0)ret_value=false; } catch(SocketException) { ret_value=false; } ret_value; } 其中设置套接字选项时必须使套接字包含IP包头否则无法填充IPHeader结构也无法获得数据包信息 ret_code=.IOControl(SIO_RCVALL,IN,OUT);是中最关键步了在windows中我们不能用Receive来接收raw上数据这是所有IP包都是先递交给系统核心然后再传输到用户当发送个raws包时候(比如syn)核心并不知道也没有这个数据被发送或者连接建立记录因此当远端主机回应时候系统核心就把这些包都全部丢掉从而到不了应用上所以就不能简单地使用接收来接收这些数据报要达到接收数据目就必须采用嗅探接收所有通过数据包然后进行筛选留下符合我们需要可以通过设置SIO_RCVALL表示接收所有网络上数据包接下来介绍下IOControlMSDN解释它说是设置套接字为低级别操作模式如何低级别操作法?其实这个和API中WSAIoctl很相似WSAIoctl定义如下: WSAIoctl( SOCKETs,//个指定套接字 DWORDdwIoControlCode,//控制操作码 LPVOIDlpvInBuffer,//指向输入数据流指针 DWORDcbInBuffer,//输入数据流大小(字节数) LPVOIDlpvOutBuffer,//指向输出数据流指针 DWORDcbOutBuffer,//输出数据流大小(字节数) LPDWORDlpcbBytesReturned,//指向输出字节流数目实数值 LPWSAOVERLAPPEDlpOverlapped,//指向个WSAOVERLAPPED结构 LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine//指向操作完成时执行例程 ); C#IOControl不像WSAIoctl那么复杂其中只包括其中控制操作码、输入字节流、输出字节流 3个参数不过这 3个参数已经足够了我们看到中定义了个字节:IN=[4]{1,0,0,0}实际上它是个值为1DWORD或是Int32同样OUT=[4];也是它整和了个,作为WSAIoctl中参数lpcbBytesReturned指向值 设置套接字选项时可能会发生需要用个值传递标志: publicboolErrorOccurred { get { error_occurred; } } 下面实现数据包接收: //解析接收数据包形成PacketArrivedEventArgs事件数据类对象并引发PacketArrival事件 unsafeprivatevoidReceive(buf,len) { temp_protocol=0; utemp_version=0; utemp_ip_srcaddr=0; utemp_ip_destaddr=0; temp_srcport=0; temp_dstport=0; IPAddresstemp_ip; PacketArrivedEventArgse=PacketArrivedEventArgs;//新网络数据包信息事件 fixed(*fixed_buf=buf) { IPHeader*head=(IPHeader*)fixed_buf;//把数据流整和为IPHeader结构 e.HeaderLength=(u)(head->ip_verlen&0x0F)<<2; temp_protocol=head->ip_protocol; switch(temp_protocol)//提取协议类型 { 1:e.Protocol="ICMP";; 2:e.Protocol="IGMP";; 6:e.Protocol="TCP";; 17:e.Protocol="UDP";; default:e.Protocol="UNKNOWN";; } temp_version=(u)(head->ip_verlen&0xF0)>>4;//提取IP协议版本 e.IPVersion=temp_version.; //以下语句提取出了PacketArrivedEventArgs对象中其他参数 temp_ip_srcaddr=head->ip_srcaddr; temp_ip_destaddr=head->ip_destaddr; temp_ip=IPAddress(temp_ip_srcaddr); e.OriginationAddress=temp_ip.; temp_ip=IPAddress(temp_ip_destaddr); e.DestinationAddress=temp_ip.; temp_srcport=*(*)&fixed_buf[e.HeaderLength]; temp_dstport=*(*)&fixed_buf[e.HeaderLength+2]; e.OriginationPort=IPAddress.NetworkToHostOrder(temp_srcport).; e.DestinationPort=IPAddress.NetworkToHostOrder(temp_dstport).; e.PacketLength=(u)len; e.MessageLength=(u)len-e.HeaderLength; e.ReceiveBuffer=buf; //把buf中IP头赋给PacketArrivedEventArgs中IPHeaderBuffer Array.Copy(buf,0,e.IPHeaderBuffer,0,()e.HeaderLength); //把buf中包中内容赋给PacketArrivedEventArgs中MessageBuffer Array.Copy(buf,()e.HeaderLength,e.MessageBuffer,0,()e.MessageLength); } //引发PacketArrival事件 OnPacketArrival(e); } 大家注意到了在上面中我们使用了指针这种所谓不安全代码可见在C#中指针和移位运算这些原始操作也可以给员带来编程上便利在中声明PacketArrivedEventArgs类对象以便通过OnPacketArrival(e)通过事件把数据包信息传递出去其中PacketArrivedEventArgs类是RawSocket类中嵌套类它继承了系统事件(Event)类封装了数据包IP、端口、协议等其他数据包头中包含信息在启动接收数据包中我们使用了异步操作思路方法以下开启了异步监听接口: publicvoidRun//开始监听 { IAsyncResultar=.BeginReceive(receive_buf_s,0,len_receive_buf,SocketFlags.None,AsyncCallback(CallReceive),this); } Socket.BeginReceive返回了个异步操作接口并在此接口生成BeginReceive中声明了异步回调CallReceive并把接收到网络数据流传给receive_buf_s这样就可用个带有异步操作接口参数异步回调不断地接收数据包: privatevoidCallReceive(IAsyncResultar)//异步回调 { received_s; received_s=.EndReceive(ar); Receive(receive_buf_s,received_s); (KeepRunning)Run; } 此当挂起或结束异步读取后去接收个新数据包这样能保证让每个数据包都能够被探测到 下面通过声明代理事件句柄来实现和外界通信: publicdelegatevoidPacketArrivedEventHandler(Objectsender,PacketArrivedEventArgsargs); //事件句柄:包到达时引发事件 publiceventPacketArrivedEventHandlerPacketArrival;//声明时间句柄 这样就可以实现对数据包信息获取采用异步回调可以提高接收数据包效率并通过代理事件把封包信息传递到外界既然能把所有封包信息传递出去就可以实现对数据包分析了:)不过RawSocket任务还没有完最后不要望了关闭套接字啊: publicvoidShutdown//关闭raw { (!=null) { .Shutdown(SocketShutdown.Both); .Close; } } 以上介绍了RawSocket类通过构造IP头获取了包中信息并通过异步回调实现了数据包接收并使用时间代理句柄和自定义数据包信息事件类把数据包信息发送出去从而实现了网络数据包监视这样我们就可以在外部添加些对数据包进行分析了 0
相关文章读者评论发表评论 |