蛙蛙推荐:自己做一个抓包工具(类库是用的别人的)
蛙蛙推薦:自己做個抓包工具(半成品)
用wireshark的命令行模式和windump抓包有時候很難滿足抓包的需求,比如我們在一臺http服務器上抓http的某個頭是指定值的包及iis給其的響應,而其它的包都不要,用現有工具好像就不好實現了,winddump的規則也頂多指定協議、端口之類,具體包的內容過濾上好像就束手無策了,于是想自己做一個,找了一些wincap開發的資料,貌似c#相關的資料不多,找到一個卻不能調試,于是又找了一篇講c#監控網絡流量的文章,改造了一下,做了一個命令行抓包工具,因為遇到一些問題,所以還是半成品。
先貼代碼,這是類庫
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Runtime.InteropServices;
usingSystem.Net.Sockets;
usingSystem.Net;
namespaceWawaSoft.WawaCapturer
{
[StructLayout(LayoutKind.Explicit)]
publicstructIPHeader
{
[FieldOffset(0)]
publicbyteip_verlen;//I4位首部長度+4位IP版本號
[FieldOffset(1)]
publicbyteip_tos;//8位服務類型TOS
[FieldOffset(2)]
publicushortip_totallength;//16位數據包總長度(字節)
[FieldOffset(4)]
publicushortip_id;//16位標識
[FieldOffset(6)]
publicushortip_offset;//3位標志位
[FieldOffset(8)]
publicbyteip_ttl;//8位生存時間TTL
[FieldOffset(9)]
publicbyteip_protocol;//8位協議(TCP,UDP,ICMP,Etc.)
[FieldOffset(10)]
publicushortip_checksum;//16位IP首部校驗和
[FieldOffset(12)]
publicuintip_srcaddr;//32位源IP地址
[FieldOffset(16)]
publicuintip_destaddr;//32位目的IP地址
}
publicclassRawSocket
{
privateboolerror_occurred;//套接字在接收包時是否產生錯誤
publicboolKeepRunning;//是否繼續進行
privatestaticintlen_receive_buf;//得到的數據流的長度
byte[]receive_buf_bytes;//收到的字節
privateSocketsocket=null;//聲明套接字
constintSIO_RCVALL=unchecked((int)0x98000001);//監聽所有的數據包
publicRawSocket()//構造函數
{
error_occurred=false;
len_receive_buf=4096;
receive_buf_bytes=newbyte[len_receive_buf];
}
publicvoidCreateAndBindSocket(stringIP)//建立并綁定套接字
{
socket=newSocket(AddressFamily.InterNetwork,SocketType.Raw,ProtocolType.IP);
socket.Blocking=false;//置socket非阻塞狀態
socket.Bind(newIPEndPoint(IPAddress.Parse(IP),0));//綁定套接字
if(SetSocketOption()==false)error_occurred=true;
}
privateboolSetSocketOption()//設置rawsocket
{
boolret_value=true;
try
{
socket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.HeaderIncluded,1);
byte[]IN=newbyte[4]{1,0,0,0};
byte[]OUT=newbyte[4];
//低級別操作模式,接受所有的數據包,這一步是關鍵,必須把socket設成raw和IPLevel才可用SIO_RCVALL
intret_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;
}
returnret_value;
}
publicboolErrorOccurred
{
get
{
returnerror_occurred;
}
}
//解析接收的數據包,形成PacketArrivedEventArgs事件數據類對象,并引發PacketArrival事件
unsafeprivatevoidReceive(byte[]buf,intlen)
{
bytetemp_protocol=0;
uinttemp_version=0;
uinttemp_ip_srcaddr=0;
uinttemp_ip_destaddr=0;
shorttemp_srcport=0;
shorttemp_dstport=0;
IPAddresstemp_ip;
PacketArrivedEventArgse=newPacketArrivedEventArgs();//新網絡數據包信息事件
fixed(byte*fixed_buf=buf)
{
IPHeader*head=(IPHeader*)fixed_buf;//把數據流整和為IPHeader結構
e.HeaderLength=(uint)(head->ip_verlen&0x0F)<<2;
e.IPHeaderBuffer=newbyte[e.HeaderLength];
temp_protocol=head->ip_protocol;
switch(temp_protocol)//提取協議類型
{
case1:e.Protocol="ICMP";break;
case2:e.Protocol="IGMP";break;
case6:e.Protocol="TCP";break;
case17: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=newIPAddress(temp_ip_srcaddr);
e.OriginationAddress=temp_ip.ToString();
temp_ip=newIPAddress(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=newbyte[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);
}
publicvoidRun()//開始監聽
{
IAsyncResultar=socket.BeginReceive(receive_buf_bytes,0,len_receive_buf,SocketFlags.None,newAsyncCallback(CallReceive),this);
}
privatevoidCallReceive(IAsyncResultar)//異步回調
{
intreceived_bytes;
received_bytes=socket.EndReceive(ar);
Receive(receive_buf_bytes,received_bytes);
if(KeepRunning)Run();
}
publicvoidShutdown()//關閉rawsocket
{
if(socket!=null)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
publicdelegatevoidPacketArrivedEventHandler(Objectsender,PacketArrivedEventArgsargs);
//事件句柄:包到達時引發事件
publiceventPacketArrivedEventHandlerPacketArrival;//聲明時間句柄函數
privatevoidOnPacketArrival(PacketArrivedEventArgse)
{
PacketArrivedEventHandlertemp=PacketArrival;
if(temp!=null)
temp(this,e);
}
publicclassPacketArrivedEventArgs:EventArgs
{
publicuintHeaderLength;
publicstringProtocol;
publicstringIPVersion;
publicstringOriginationAddress;
publicstringDestinationAddress;
publicstringOriginationPort;
publicstringDestinationPort;
publicuintPacketLength;
publicuintMessageLength;
publicbyte[]ReceiveBuffer;
publicbyte[]IPHeaderBuffer;
publicbyte[]MessageBuffer;
publicPacketArrivedEventArgs()
{
}
publicoverridestringToString()
{
StringBuildersb=newStringBuilder();
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");
returnsb.ToString();
}
}
}
}
具體原理不說了,詳情請看這篇文章
http://www.cnblogs.com/onlytiancai/archive/2007/10/14/924075.html
這是控制臺代碼,愿意是想加上一個tcp分析類和http分析類,然后可以實現http頭和內容的過濾。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.IO;
namespaceWawaSoft.WawaCapturer
{
classProgram
{
staticvoidMain(string[]args)
{
RawSocketsocket=null;
try
{
socket=newRawSocket();
socket.CreateAndBindSocket("192.168.1.100");
if(socket.ErrorOccurred)
{
Console.WriteLine("監聽出錯了");
return;
}
socket.KeepRunning=true;
socket.PacketArrival+=socket_PacketArrival;
socket.Run();
}
catch(Exceptionex)
{
Console.WriteLine(ex);
throw;
}
finally
{
Console.Read();
socket.Shutdown();
}
}
staticvoidsocket_PacketArrival(objectsender,RawSocket.PacketArrivedEventArgsargs)
{
if(args.Protocol=="TCP"&&(args.OriginationPort=="80"||args.DestinationPort=="80"))
{
Console.WriteLine(args);
byte[]httpData=newbyte[args.MessageLength-20];
Buffer.BlockCopy(args.MessageBuffer,20,httpData,0,httpData.Length);
using(BinaryWriterbw=newBinaryWriter(File.Open(string.Format("{0}.cap",
DateTime.Now.ToString("yyyyMMddhh")),
FileMode.OpenOrCreate)))
{
bw.Write(DateTime.Now.ToString());
bw.Write(httpData,0,httpData.Length);
bw.Write(newbyte[]{0,0,0,0},0,4);//結束
}
return;
//System.IO.MemoryStreams=newSystem.IO.MemoryStream(httpData);
//System.IO.BinaryReaderr=newSystem.IO.BinaryReader(s);
//bytetemp=newbyte();
//bytetemp1;
//inti;
//boolfind=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=newbyte[httpData.Length-i-1];
//Buffer.BlockCopy(httpData,i,httpHeaderData,0,httpHeaderData.Length);
//Console.WriteLine("http頭內容如下"r"n{0}",Encoding.UTF8.GetString(httpHeaderData));
//byte[]body=newbyte[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
聲明:類庫是我從一篇文章里整理出來的,版權規原作者所有,但實在找不出出處了。
總結
以上是生活随笔為你收集整理的蛙蛙推荐:自己做一个抓包工具(类库是用的别人的)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试题: 数据库 sql优化 sql练
- 下一篇: popd命令