蛙蛙推荐:自己做一个抓包工具(类库是用的别人的)

蛙蛙推荐:自己做个抓包工具(半成品)
用wireshark的命令行模式和windump抓包有时候很难满足抓包的需求,比如我们在一台http服务器上抓http的某个头是指定值的包及iis给其的响应,而其它的包都不要,用现有工具好像就不好实现了,winddump的规则也顶多指定协议、端口之类,具体包的内容过滤上好像就束手无策了,于是想自己做一个,找了一些wincap开发的资料,貌似c#相关的资料不多,找到一个却不能调试,于是又找了一篇讲c#监控网络流量的文章,改造了一下,做了一个命令行抓包工具,因为遇到一些问题,所以还是半成品。

先贴代码,这是类库
using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.InteropServices;
using  System.Net.Sockets;
using  System.Net;

namespace  WawaSoft.WawaCapturer
{
    [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地址
    }

    
public   class  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 ); // 监听所有的数据包

        
public  RawSocket()                     // 构造函数
        {
            error_occurred 
=   false ;
            len_receive_buf 
=   4096 ;
            receive_buf_bytes 
=   new   byte [len_receive_buf];
        }
        
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 ;
        }

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

        
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 ;
                e.IPHeaderBuffer 
=   new   byte [e.HeaderLength];

                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.MessageBuffer 
=   new   byte [e.MessageLength];

                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);
        }
        
public   void  Run()  // 开始监听
        {
            IAsyncResult ar 
=  socket.BeginReceive(receive_buf_bytes,  0 , len_receive_buf, SocketFlags.None,  new  AsyncCallback(CallReceive),  this );
        }
        
private   void  CallReceive(IAsyncResult ar) // 异步回调
        {
            
int  received_bytes;
            received_bytes 
=  socket.EndReceive(ar);
            Receive(receive_buf_bytes, received_bytes);
            
if  (KeepRunning) Run();
        }
 
        
public   void  Shutdown()                                        // 关闭raw socket
        {
            
if  (socket  !=   null )
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
        }

        
public   delegate   void  PacketArrivedEventHandler(Object sender, PacketArrivedEventArgs args);
        
// 事件句柄:包到达时引发事件
         public   event  PacketArrivedEventHandler PacketArrival; // 声明时间句柄函数
         private   void  OnPacketArrival(PacketArrivedEventArgs e)
        {
            PacketArrivedEventHandler temp 
=  PacketArrival;
            
if  (temp  !=   null )
                temp(
this , e);
        }

        
public   class  PacketArrivedEventArgs : EventArgs
        {
            
public   uint  HeaderLength;
            
public   string  Protocol;
            
public   string  IPVersion;
            
public   string  OriginationAddress;
            
public   string  DestinationAddress;
            
public   string  OriginationPort;
            
public   string  DestinationPort;
            
public   uint  PacketLength;
            
public   uint  MessageLength;
            
public   byte [] ReceiveBuffer;
            
public   byte [] IPHeaderBuffer;
            
public   byte [] MessageBuffer;
            
public  PacketArrivedEventArgs()
            {
            }
            
public   override   string  ToString()
            {
                StringBuilder sb 
=   new  StringBuilder();
                sb.Append(
"" r " n---------------- " r " n " );
                sb.AppendFormat(
" src = {0}:{1}, dst= {2}:{3} " r " n " ,OriginationAddress,OriginationPort,
                    DestinationAddress, DestinationPort);
                sb.AppendFormat(
" protocol = {0}, ipVersion={1} " r " n " , Protocol, IPVersion);
                sb.AppendFormat(
" PacketLength={0},MessageLength={1} " ,PacketLength,MessageLength);
                sb.Append(
"" r " n---------------- " r " n " );
                
return  sb.ToString();
            }
        }

    }
}

具体原理不说了,详情请看这篇文章
http://www.cnblogs.com/onlytiancai/archive/2007/10/14/924075.html

这是控制台代码,愿意是想加上一个tcp分析类和http分析类,然后可以实现http头和内容的过滤。
using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.IO;

namespace  WawaSoft.WawaCapturer
{
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            RawSocket socket 
=   null ;

            
try
            {
                socket 
=   new  RawSocket();
                socket.CreateAndBindSocket(
" 192.168.1.100 " );
                
if  (socket.ErrorOccurred)
                {
                    Console.WriteLine(
" 监听出错了 " );
                    
return ;
                }
                socket.KeepRunning 
=   true ;
                socket.PacketArrival 
+=  socket_PacketArrival;
                socket.Run();
            }
            
catch  (Exception ex)
            {
                Console.WriteLine(ex);
                
throw ;
            }
            
finally
            {
                Console.Read();
                socket.Shutdown();
            }

        }

        
static   void  socket_PacketArrival( object  sender, RawSocket.PacketArrivedEventArgs args)
        {
            
if  (args.Protocol  ==   " TCP "   &&  (args.OriginationPort  ==   " 80 "   ||  args.DestinationPort  ==   " 80 " ))
            {
                Console.WriteLine(args);
                
byte [] httpData  =   new   byte [args.MessageLength - 20 ];
                Buffer.BlockCopy(args.MessageBuffer, 
20 , httpData,  0 , httpData.Length);

                
using  (BinaryWriter bw  =   new  BinaryWriter(File.Open( string .Format( " {0}.cap " ,
                    DateTime.Now.ToString(
" yyyyMMdd hh " )),
                    FileMode.OpenOrCreate)))
                {
                    bw.Write(DateTime.Now.ToString());
                    bw.Write(httpData, 
0 , httpData.Length);
                    bw.Write(
new   byte [] {  0 0 0 0  },  0 4 );  // 结束
                }
                
return ;

                
// System.IO.MemoryStream s = new System.IO.MemoryStream(httpData);
                
// System.IO.BinaryReader r = new System.IO.BinaryReader(s);
                
// byte temp = new byte();
                
// byte temp1;
                
// int i;
                
// bool find = false;
                
// for (i = 0; i < s.Length; i++)
                
// {
                
//     temp1 = r.ReadByte();
                
//     if (temp == 0x0d && temp1 == 0x0a)
                
//     {
                
//         find = true;
                
//         break;
                
//     }
                
//     temp = temp1;
                
// }
                
// if (!find) return;
                
// byte[] httpHeaderData = new byte[httpData.Length - i -1];
                
// Buffer.BlockCopy(httpData, i, httpHeaderData, 0, httpHeaderData.Length);                

                
// Console.WriteLine("http头内容如下"r"n{0}",Encoding.UTF8.GetString(httpHeaderData));

                
// byte[] body = new byte[httpData.Length - httpHeaderData.Length];
                
// Buffer.BlockCopy(httpData, httpHeaderData.Length, body, 0, body.Length);
                
// Console.WriteLine("http内容如下"r"n{0}", Encoding.UTF8.GetString(body));
            }
        }
    }
}

简单说明:ip的头是20个字节,在RawSocket里已经提取出来了,tcp的头也是20字节,我在console里也取了出来,但没有分析(要分析的话看相关链接的第二篇文章),就是说args.MessageBuffer的20字节以后就是http协议的内容了(假如是抓的http的包),然后http的内容基本上是文本的(当然http也传输图片啥的,取决于contenttype头),在第一个回车换行("r"n,也就是0x0d,0x0a)前面是http头部分,我们根据这个标志位可以取出http头,然后"r"n下面就是http的正文消息了,我们根据http头的ContentType来决定把正文的数据解析成一个图片还是一段html。如果我们只抓取html正文里包含"蛙蛙池塘"的数据包,我们就可以用响应编码类的getstring方法获取html正文字符串后用indexof方法来判断是否抓取该包。
以上纯属个人想法,结果编码的时候死活编不出来,问题如下,请高手指教。
1、我用Encoding.UTF8.GetString所有http的数据,结果中文死活出不来,我把byte[]保存到硬盘上用UE打开,转换编码,还是不能识别中文,我就抓的google的包,铁定是utf-8编码的,我就纳闷了,怎么能取出中文的数据呀。
2、每次触发PacketArrival事件,是不是有可能是收到半截儿的数据呀,比方是tcp的一次传输数据大于MTU的1500的值,一个数据传输了5次,会不会触发5次PacketArrival事件呀。难道还得自己根据ip协议的序号来拼合多次接受的数据不成?
3、在一个byte[]里怎么查找"r"n的值呀,我的算法是用一个循环加两个临时变量做的,貌似复杂了点儿,而且算法根本就不对好像,用Array.IndexOf<T>()方法和Array.FindAll方法貌似也不行,大家看看怎么弄比较好。

最后我就想着,做成一个命令行工具,参数里可以设置要抓取包的协议,来源地址,目的地址,来源端口,目的端口,数据里包含的字符串,有这几个就够了,因为我们一般在服务器出错,或者怀疑有人攻击服务器的时候会抓取某些包含特殊攻击字符或者来源于某些特定访问者(不一定是IP,IP的话用windump的规则就可以指定,而可能是一个http头或者cookie标识的一个应用层的用户)的数据包,要把所有包都抓回来,分析起来会很费力。

有了这个小工具,会很方便做网络应用的朋友,大家谁有兴趣,一起完善哦,哥们是积穷了,弄了三四个小时了还没弄好

相关链接:
用协议分析工具学习TCP/IP
http://www.cnpaf.net/class/OtherAnalysis/0532918532942694.html
TCP/IP协议数据报结构详解
http://www.itvue.com/Article/CISCO/CIROUTE/200607/1683.html
Raw Socket Capturing Using C#
http://www.codeproject.com/csharp/pktcap.asp

声明:类库是我从一篇文章里整理出来的,版权规原作者所有,但实在找不出出处了。

你可能感兴趣的:(工具)