C#抓包工具实现源代码

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

先贴代码,这是类库
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using https://www.360docs.net/doc/2217118788.html,.Sockets;
using https://www.360docs.net/doc/2217118788.html,;

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 = *(sh

ort*)&fixed_buf[e.HeaderLength];
temp_dstport = *(short*)&fixed_buf[e.HeaderLength + 2];
e.OriginationPort = https://www.360docs.net/doc/2217118788.html,workToHostOrder(temp_srcport).ToString();
e.DestinationPort = https://www.360docs.net/doc/2217118788.html,workToHostOrder(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();
}
}

}
}
具体原理不说了,详情请看这篇文章
https://www.360docs.net/doc/2217118788.html,/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()方法和Array.FindAll方法貌似也不行,大家看看怎么弄比较好。



相关文档
最新文档