Socket編程--用C#製作Sniffer
/*
調試說明:在vs2003下調試通過,但在vs2005下調試不能通過,主要是多線程的問題.使用BackGroundWorker處理後,只能接收幾個封包然後就停止接收了,可能還要再學一下多線程編程,還望高手指教.
*/
代碼已改進,現在可用了.接下來的問題是完善對各种協議的分析.還有個問題,我的計算机在局域网里面,但沒有加入域,如何才能嗅探到在域里傳遞的數据啊?
以下是源代碼:
1.枚舉常用的變量
public enum Precedence
{
Routine = 0,
Priority = 1,
Immediate = 2,
Flash = 3,
FlashOverride = 4,
CRITICECP = 5,
InternetworkControl = 6,
NetworkControl = 7
}
public enum Delay
{
NormalDelay = 0,
LowDelay = 1
}
public enum Throughput
{
NormalThroughput = 0,
HighThroughput = 1
}
public enum Reliability
{
NormalReliability = 0,
HighReliability = 1
}
public enum Protocol
{
Ggp = 3,
Icmp = 1,
Idp = 22,
Igmp = 2,
IP = 4,
ND = 77,
Pup = 12,
Tcp = 6,
Udp = 17,
Other = -1
}
2.將封包封裝成類
public class Packet
{
private byte[] m_Raw;//保存原始封包數據
private DateTime m_Time;//時間
private int m_Version;//4位,版本
private int m_HeaderLength;//4位,ip頭的長度,一般都是20字節長
private Precedence m_Precedence;//優先級
private Delay m_Delay;//延時
private Throughput m_Throughput;//
private Reliability m_Reliability;//
private int m_TotalLength;//16位,封包總長度
private int m_Identification;//16位,ip包標識
private int m_TimeToLive;//8位,數據包在網絡上的存活時間
private Protocol m_Protocol;//8位,協議號
private byte[] m_Checksum;//校驗值
private string m_SourceAddress;//源地址
private string m_DestinationAddress;//目標地址
private int m_SourcePort;//源端口
private int m_DestinationPort;//目標端口
private byte[] m_data;//保存接收到的用戶數據
public Packet()
{
}
public Packet(byte[] raw, DateTime time)
{
if (raw==null)
{
throw new ArgumentNullException();
}
if (raw.Length<20)
{
throw new ArgumentException();
}
this.m_Raw = raw;//取原始數據包
this.m_Time = time;//取接收時間
this.m_HeaderLength = (raw[0] & 0xF) * 4;//取ip頭長度
if ((raw[0] & 0xF) < 5) {throw new ArgumentException();}//包長太短
this.m_Precedence = (Precedence)((raw[1] & 0xE0) >> 5);//取優先級
this.m_Delay = (Delay)((raw[1] & 0x10) >> 4);//取延時
this.m_Throughput = (Throughput)((raw[1] & 0x8) >> 3);//
this.m_Reliability = (Reliability)((raw[1] & 0x4) >> 2);//
this.m_TotalLength = raw[2] * 256 + raw[3];//取總長度
if ( ! (this.m_TotalLength == raw.Length)) { throw new ArgumentException();} //封包長度不正確
this.m_Identification = raw[4] * 256 + raw[5];//取標識值
this.m_TimeToLive = raw[8];//取存活時間
m_Protocol = (Protocol)raw[9];//取服務類型
m_Checksum = new byte[2];//取校驗值
m_Checksum[0] = raw[11];
m_Checksum[1] = raw[10];
//此處只對tcp,udp取正確的用戶數據,其余用戶數據都不正確.
switch(m_Protocol)
{
case Protocol.Tcp://tcp頭長20
m_data=new byte[m_TotalLength-m_HeaderLength-20];
Array.Copy(raw,m_HeaderLength+20,m_data, 0,m_TotalLength-m_HeaderLength-20);
break;
case Protocol.Udp://udp頭長8
m_data=new byte[m_TotalLength-m_HeaderLength-8];
Array.Copy(raw,m_HeaderLength+8,m_data, 0,m_TotalLength-m_HeaderLength-8);
break;
default:
m_data=new byte[m_TotalLength-m_HeaderLength];
Array.Copy(raw,m_HeaderLength,m_data, 0,m_TotalLength-m_HeaderLength);
break;
}
//取目錄地址及源地址
try
{
m_SourceAddress = GetIPAddress(raw, 12);
m_DestinationAddress = GetIPAddress(raw, 16);
}
catch (Exception e)
{
throw;
}
//取目標端口及源端口
if (m_Protocol == Protocol.Tcp || m_Protocol == Protocol.Udp)
{
m_SourcePort = raw[m_HeaderLength] * 256 + raw[m_HeaderLength + 1];
m_DestinationPort = raw[m_HeaderLength + 2] * 256 + raw[m_HeaderLength + 3];
}
else
{
m_SourcePort = -1;
m_DestinationPort = -1;
}
}
public int HeaderLength
{
get
{
return m_HeaderLength;
}
}
public string Data
{
get
{
string result="";
System.Text.ASCIIEncoding ac=new ASCIIEncoding();
result = ac.GetString(m_data);
return result;
}
}
public string GetIPAddress(byte[] bArray, int nStart)
{
byte[] tmp = new byte[4];
if (bArray.Length > nStart + 2)
{
tmp[0] = bArray[nStart];
tmp[1] = bArray[nStart + 1];
tmp[2] = bArray[nStart + 2];
tmp[3] = bArray[nStart + 3];
}
return tmp[0] + "." + tmp[1] + "." + tmp[2] + "." + tmp[3];
}
public int TotalLength
{
get { return m_TotalLength; }
}
public DateTime Time
{
get { return this.m_Time; }
}
public Protocol Protocol
{
get { return this.m_Protocol; }
}
public string SourceAddress
{
get { return this.m_SourceAddress; }
}
public string Source
{
get
{
if ( m_SourcePort != -1 )
{
return SourceAddress.ToString() + ":" + m_SourcePort.ToString();
}
else
{
return SourceAddress.ToString();
}
}
}
public string Destination
{
get
{
if (this.m_DestinationPort != -1)
{
return DestinationAddress.ToString() + ":" + m_DestinationPort.ToString();
}
else
{
return DestinationAddress.ToString();
}
}
}
public string DestinationAddress
{
get
{
return m_DestinationAddress;
}
}
}
3.封裝一個用於接收及處理封包的類
public class Monitor
{
public delegate void NewPacketEventHandler(Monitor m, Packet p);
public event NewPacketEventHandler NewPacket;
private Socket m_Monitor;//建立用於接收信息的Socket
private IPAddress m_Ip;//本地ip
private byte[] m_Buffer = new byte[65535];//保存接收數據
private const System.Int32 IOC_VENDOR = 0x18000000;
private const int IOC_IN = -2147483648;
private const int SIO_RCVALL = IOC_IN ^ IOC_VENDOR ^ 1;//接收所有封包
private const int SECURITY_BUILTIN_DOMAIN_RID = 0x20;
private const int DOMAIN_ALIAS_RID_ADMINS = 0x220;
public System.Net.IPAddress IP
{
get { return m_Ip; }
}
public byte[] Buffer
{
get { return m_Buffer; }
}
public Monitor()
{
//
// TODO: 在此添加构造函數
//
}
public Monitor(IPAddress IpAddress)
{
if (!(Environment.OSVersion.Platform == PlatformID.Win32NT) && Environment.OSVersion.Version.Major<5)
{
throw new NotSupportedException("This program requires Windows 2000, Windows XP or Windows .NET Server!");
}
m_Ip = IpAddress;
}
//開始偵聽
public void Start()
{
if (m_Monitor==null)
{
try
{
// 初始化接收Socket
m_Monitor = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
// bind到本地端口
m_Monitor.Bind(new IPEndPoint(IP, 0));
// 設定Socket低階作業模式
m_Monitor.IOControl(SIO_RCVALL, BitConverter.GetBytes(1), null);
// 開始接收封包數據
m_Monitor.BeginReceive(m_Buffer, 0, m_Buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
}
catch (Exception e)
{
m_Monitor = null;
throw new SocketException();
}
}
}
//處理封包
public void OnReceive(System.IAsyncResult ar)
{
try
{
// 完成接收封包數據
int received = m_Monitor.EndReceive(ar);
try
{
if (m_Monitor!=null)
{
byte[] pkt = new byte[received];
// 將封包轉化為字節數組
Array.Copy(Buffer, 0, pkt, 0, received);
// 處理封包數據
OnNewPacket(new Packet(pkt, DateTime.Now));
}
}
catch(Exception e)
{
throw;
}
// 繼續開始接收新的封包
m_Monitor.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None,new AsyncCallback(OnReceive), null);
}
catch (Exception e)
{
throw;
}
}
//處理封包的函數
protected void OnNewPacket(Packet p)
{
NewPacket(this, p);
}
//停止偵聽
public void Stop()
{
if (m_Monitor!=null)
{
m_Monitor.Close();//關閉Socket
}
m_Monitor = null;
}
}
4.主程序
private Monitor m_Monitor;
private ArrayList m_Packets;
private int m_PacketsSize;
private void button1_Click(object sender, System.EventArgs e)
{
if(this.button1.Text=="開始監控")
{
this.statusBar1.Text="監控中...";
this.richTextBox1.Text="";
m_PacketsSize=0;
StartMonitor() ;
this.button1.Text="停止監控";
}
else
{
m_Monitor.Stop();
this.button1.Text="開始監控";
this.statusBar1.Text="已經停止監控";
}
}
private void StartMonitor()
{
IPAddress[] hosts = Dns.Resolve(Dns.GetHostName()).AddressList;
if (hosts.Length == 0) { throw new NotSupportedException("This computer does not have non-loopback interfaces installed!");}
m_Packets = new ArrayList();
m_Monitor = new Monitor(hosts[0]);
//添加代理,每次有新的packet到時都觸發下面那個動作
m_Monitor.NewPacket+=new Monitor.NewPacketEventHandler(this.OnNewPacket);
m_Monitor.Start();
}
/*//這個方法用於把packet顯示到richTextBox1中
private void OnNewPacket(Monitor m, Packet p)
{
m_Packets.Add(p);
m_PacketsSize += p.TotalLength;
try
{
this.richTextBox1.AppendText(m_Packets.Count.ToString()+"/t時間:"+p.Time.ToString()+"協議:"+p.Protocol.ToString()+"/t源:"+p.Source.ToString()+" /t目標:"+p.Destination.ToString()+"/t總長:"+p.TotalLength.ToString("0000"));
this.richTextBox1.AppendText("/r");
this.richTextBox1.AppendText("/t內容:"+p.Data);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
statusBar1.Text = String.Format("Intercepted {0} packet(s) [{1} bytes]", m_Packets.Count, m_PacketsSize);
} */
改進: 將上面那段注釋掉的代碼改為以下內容,這個Sniffer就可以用了.在vs2005下運行成功.沒有在vs2003下做測試.但也應該可以用.
//這個方法用於把packet顯示到richTextBox1中
private void OnNewPacket(Monitor m, Packet p)
{
m_Packets.Add(p);
m_PacketsSize += p.TotalLength;
try
{
string result = m_Packets.Count.ToString() + "/t時間:" + p.Time.ToString() + "協議:" + p.Protocol.ToString() + "/t源:" + p.Source.ToString() + " /t目標:" + p.Destination.ToString() + "/t總長:" + p.TotalLength.ToString("0000");
result += "/r/n";
result += "/t內容:" + p.Data;
result += "/r/n";
CalcFinished(result);//计算完成需要在一个文本框里显示
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
delegate void changeText(string result);
private void CalcFinished(string result)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new changeText(CalcFinished), result);
}
else
{
this.richTextBox1.AppendText(result);
statusBar1.Text = String.Format("Intercepted {0} packet(s) [{1} bytes]", m_Packets.Count, m_PacketsSize);
}
}