C#版的抓包软件
来源:互联网 发布:三角洲部队 百度软件 编辑:程序博客网 时间:2024/05/29 18:47
NetAnalyzer实现结构
在上一篇中介绍一点VC++开发环境的配置,与基本的运行方式。因为NetAnalyzer使用的C#作为开发语言,所以在此主要介绍一些在C#环境下的开发环境的配置,与一些基本开发情况,力求在完成本篇后后,读者可以制作一个简单的抓包程序。
在开始编程前先要介绍连个.Net类库SharpPcap.dll与PacketDotNet.dll。在2004年Tamir Gal为了完成他的毕业设计,其中有一些内容需要使用Winpcap来实现网络数据分析,但希望可以使用简单易用的C#开发语言。于是建立了SharpPcap项目,Tamir Gal为了节省时间,并没用将网络数据采集部分与分析部分分开,甚至有些代码混杂在UI代码中。而且在实现了很少的WinPcapAPI接口,并没用提供相关的开发文档。
因为这样,Tamir决定重新设计SharPcap,并推出了1.x一些列的版本,最终在2007年完成了SharpPcap1.6.2版本。
在2008年11月Chris Morgan接替了Tamir的工作,重新设计了SharpPcap的API,开始支持Linux 和MAC(在Linux 与MAC平台的相关技术请参见Mono开发平台)。之后将数据采集于协议分析分别封装在不同的程序集类库中即形成了SharpPcap于Packet.Net。
下载地址:http://sourceforge.net/projects/sharppcap/ 在GNU协议下的开源代码。
(1) SharpPcap.dll
SharpPcap中封装了Winpcap提供的大部分API函数,在该类库中,我们可以获取主机网卡,及其信息。并定义了对网卡的各种操作方法,如打开网卡,开始抓包,停止抓包等,再抓包中提供了,同步方式与异步方式,方便进行不同的环境,对于更加详细的介绍,将会在下一章提到。
(2) Packet.Net
在程序集中的名称为PacketDotNet,该类库负责对捕获的数据进行分析解析,目前提供可以分析的协议:
Ethernet、SLL (Linux Cooked-Mode Capture) 、ARP、IPv4 、IPv6 、TCP 、UDP 、ICMPv4、ICMPv6 、IGMPv2 、PPPoE 、PTP 、LLDP 、Wake-on-LAN(WOL)
在NetAnalyzer此基础上,在类库中增加了PPP、LCP、CiscoHDLC等协议。
为了可以分析应用层协议,在NetAnalyzer设计了ApplicationProtocol类库,目前提供HTTP、FTP、DNS、SMTP、POP3、SSDP协议的解析,本节中不会对应用层协议进行分析。
在以下的内容中,我们将分七个部分,来完成一个简单的抓包程序。同上面一样,所有的程序都在Visual Studio2013中完成,只是在这里我们首先要得到:
SharpPcap.dll版本4.0.0
PacketDotNet.dl版本0.11.0
可以通过上面的网址获取。
1 获取网络适配器(网卡)
首先新建工程,开发语言选择C#,类型选择Windows窗体应用程序,命名为MySniffer,图形界面如图1所示。
图1 建立MySniffer工程
点击确定,在解决方案资源管理器中右击引用添加引用, 此时打开添加应用对话框,选择浏览选项卡,并找到SharpPcap与PacketDotNet类库将其添加至工程中。如图2所示,
图2从引用中右击添加引用
在添加完成之后,项目应用中应该可以看到这两个类库的的引用文件。
首先我们来获取需要监听的网卡,在窗体中添加如下代码:
1 private void loadDevice()// 获取网卡方法 2 { 3 try 4 { 5 foreach (var i in CaptureDeviceList.Instance) 6 { 7 comDeviceList.Items.Add(i.Description); //在combox中添加 8 } 9 if (comDeviceList.Items.Count > 0)10 {11 comDeviceList.SelectedIndex = 0;//自动选中第一个12 }13 }14 catch (Exception ex)15 {16 MessageBox.Show(ex.Message);17 return;18 }19 }
我们通过调用SharpPcap.CaptureDeviceList.Instance 单例,获取一个包含所用网卡的列表CaptureDeviceList,然后把以此把网卡的描述信息添加到顶部工具栏comDeviceList 中
在代码中我们加一个全局变量device用来指示当前控制的网卡,
1 ICaptureDevice device;// 定义设备
所以当我们每次去选择不同的网卡时调用下面的方法
1 //选择网卡2 private void comDeviceList_SelectedIndexChanged(object sender, EventArgs e)3 {4 device = CaptureDeviceList.Instance[comDeviceList.SelectedIndex];5 }
完成运行如图
图3获取的网卡以及其信息
接下来我们直接来点精彩的,加入如下代码:
1 List<RawCapture> packetList = new List<RawCapture>();//捕获的数据列表 2 List<RawCapture> bufferList;//缓存列表 3 Thread AnalyzerThread;//分析数据的线程 4 object threadLock = new object();//线程锁定 5 bool isStartAnalyzer;//用于表示是否启动分析线程的标志 6 DeviceMode devMode = DeviceMode.Promiscuous;//数据采集模式 7 int readTimeOut = 1000; 8 delegate void DataGridRowsShowHandler(RawCapture packet); 9 10 11 /// <summary> 12 /// 启动网卡 13 /// </summary> 14 private void Start() 15 { 16 if (device == null || device.Started) 17 return; 18 bufferList = new List<RawCapture>(); 19 Clear();//清理原有的数据 20 isStartAnalyzer = true; 21 StartAnalyzer();//启动分析线程 22 try 23 { 24 device.OnPacketArrival += new PacketArrivalEventHandler(device_OnPacketArrival); 25 //默认使用混杂模式,超时 1000 26 device.Open(devMode, readTimeOut); 27 device.Filter = comFilter.Text; 28 device.StartCapture(); 29 30 UIConfig(true); 31 } 32 catch (Exception ex) 33 { 34 MessageBox.Show(ex.Message); 35 36 UIConfig(false); 37 } 38 39 } 40 /// <summary> 41 /// 启动分析 42 /// </summary> 43 private void StartAnalyzer() 44 { 45 AnalyzerThread = new Thread(new ThreadStart(analysrThreadMethod)); 46 AnalyzerThread.IsBackground = true; 47 AnalyzerThread.Start(); 48 49 } 50 /// <summary> 51 /// 停止 52 /// </summary> 53 private void Stop() 54 { 55 try 56 { 57 if (device != null && device.Started) 58 { 59 device.StopCapture(); 60 device.Close(); 61 } 62 63 isStartAnalyzer = false; 64 if (AnalyzerThread !=null && AnalyzerThread.IsAlive) 65 { 66 AnalyzerThread.Abort(); 67 } 68 } 69 catch (Exception ex) 70 { 71 MessageBox.Show(ex.Message); 72 } 73 UIConfig(false); 74 } 75 76 /// <summary> 77 /// Sharpcap 获取数据包之后的回调 78 /// </summary> 79 /// <param name="sender"></param> 80 /// <param name="e"></param> 81 void device_OnPacketArrival(object sender, CaptureEventArgs e) 82 { 83 lock (threadLock) 84 { 85 bufferList.Add(e.Packet); 86 } 87 } 88 89 /// <summary> 90 /// 数据分析线程 (使用缓存方式,可避免数据堵塞) 91 /// </summary> 92 93 private void analysrThreadMethod() 94 { 95 while (isStartAnalyzer) 96 { 97 bool isShoudSleep = true; 98 lock (threadLock) 99 {100 if (bufferList.Count != 0)101 isShoudSleep = false;102 }103 if (isShoudSleep)//104 {105 Thread.Sleep(200);106 }107 else108 {109 List<RawCapture> tmpPacketList;110 lock (threadLock) //获取数据111 {112 tmpPacketList = bufferList;113 bufferList = new List<RawCapture>();//构建新列表114 packetList.AddRange(tmpPacketList);115 }116 foreach (var i in tmpPacketList)117 {118 this.Invoke(new DataGridRowsShowHandler(ShowDataRows), i);119 120 }121 }122 }123 }124 125 /// <summary>126 /// 在datagridview中显示获取的网络数据127 /// </summary>128 /// <param name="packet"></param>129 private void ShowDataRows(RawCapture packet)130 {131 try132 {133 dataGridPacket.Rows.Add(rowsBulider.Row(packet, ++packetIndex));//加载DataGridRows;134 }135 catch (Exception ex)136 {137 138 }139 }
这里通过在抓包启动之前,预先启动一个分析线程,用于独立进行数据解析,接下来就是在DataGridView中添加数据了,这部分写的比较渣,请轻喷,毕竟是几年前的代码了,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows.Forms; 6 using SharpPcap; 7 using PacketDotNet; 8 using System.Xml; 9 namespace MySniffer 10 { 11 class DataBuilder 12 { 13 //标记当前数据是否有效 14 15 #region 构建数据行 16 /// <summary> 17 /// DataGridRow 18 /// </summary> 19 /// <returns>返回字符串数据</returns> 20 public string[] Row(RawCapture rawPacket, uint packetID) 21 { 22 string[] rows = new string[6]; 23 24 rows[0] = string.Format("{0:D7}", packetID);//编号 25 rows[1] = "Unknown"; 26 rows[2] = rawPacket.Data.Length.ToString();//数据长度bytes 27 rows[3] = "--"; 28 rows[4] = "--"; 29 rows[5] = "--"; 30 31 Packet packet = Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data); 32 33 EthernetPacket ep = EthernetPacket.GetEncapsulated(packet); 34 if (ep != null) 35 { 36 rows[1] = "Ethernet(v2)"; 37 rows[3] = Format.MacFormat(ep.SourceHwAddress.ToString()); 38 rows[4] = Format.MacFormat(ep.DestinationHwAddress.ToString()); 39 rows[5] = "[" + ep.Type.ToString() + "]"; 40 41 #region IP 42 IpPacket ip = IpPacket.GetEncapsulated(packet); 43 if (ip != null) 44 { 45 if (ip.Version == IpVersion.IPv4) 46 { 47 rows[1] = "IPv4"; 48 } 49 else 50 { 51 rows[1] = "IPv6"; 52 } 53 rows[3] = ip.SourceAddress.ToString(); 54 rows[4] = ip.DestinationAddress.ToString(); 55 rows[5] = "[下层协议:" + ip.NextHeader.ToString() + "] [版本:" + ip.Version.ToString() + "]"; 56 57 TcpPacket tcp = TcpPacket.GetEncapsulated(packet); 58 if (tcp != null) 59 { 60 rows[1] = "TCP"; 61 rows[3] += " [" + tcp.SourcePort.ToString() + "]"; 62 rows[4] += " [" + tcp.DestinationPort.ToString() + "]"; 63 64 return rows; 65 } 66 UdpPacket udp = UdpPacket.GetEncapsulated(packet); 67 if (udp != null) 68 { 69 rows[1] = "UDP"; 70 rows[3] += " [" + udp.SourcePort.ToString() + "]"; 71 rows[4] += " [" + udp.DestinationPort.ToString() + "]"; 72 return rows; 73 } 74 75 ICMPv4Packet icmpv4 = ICMPv4Packet.GetEncapsulated(packet); 76 if (icmpv4 != null) 77 { 78 rows[1] = "ICMPv4"; 79 rows[5] = "[校验:" + icmpv4.Checksum.ToString() + "] [类型:" + icmpv4.TypeCode.ToString() + "] [序列号:" + icmpv4.Sequence.ToString() + "]"; 80 return rows; 81 } 82 ICMPv6Packet icmpv6 = ICMPv6Packet.GetEncapsulated(packet); 83 if (icmpv6 != null) 84 { 85 rows[1] = "ICMPv6"; 86 rows[5] = "[Code:" + icmpv6.Code.ToString() + "] [Type" + icmpv6.Type.ToString() + "]"; 87 return rows; 88 } 89 IGMPv2Packet igmp = IGMPv2Packet.GetEncapsulated(packet); 90 if (igmp != null) 91 { 92 rows[1] = "IGMP"; 93 rows[5] = "[只适用于IGMPv2] [组地址:" + igmp.GroupAddress.ToString() + "] [类型:" + igmp.Type.ToString() + "]"; 94 return rows; 95 } 96 return rows; 97 } 98 #endregion 99 100 ARPPacket arp = ARPPacket.GetEncapsulated(packet);101 if (arp != null)102 {103 rows[1] = "ARP";104 rows[3] = Format.MacFormat(arp.SenderHardwareAddress.ToString());105 rows[4] = Format.MacFormat(arp.TargetHardwareAddress.ToString());106 rows[5] = "[Arp操作方式:" + arp.Operation.ToString() + "] [发送者:" + arp.SenderProtocolAddress.ToString() + "] [目标:" + arp.TargetProtocolAddress.ToString() + "]";107 return rows;108 }109 WakeOnLanPacket wp = WakeOnLanPacket.GetEncapsulated(packet);110 if (wp != null)111 {112 rows[1] = "Wake On Lan";113 rows[3] = Format.MacFormat(ep.SourceHwAddress.ToString());114 rows[4] = Format.MacFormat(wp.DestinationMAC.ToString());115 rows[5] = "[唤醒网络地址:" + wp.DestinationMAC.ToString() + "] [有效性:" + wp.IsValid().ToString() + "]";116 return rows;117 }118 PPPoEPacket poe = PPPoEPacket.GetEncapsulated(packet);119 if (poe != null)120 {121 rows[1] = "PPPoE";122 rows[5] = poe.Type.ToString() + " " + poe.Version.ToString();123 return rows;124 125 }126 LLDPPacket llp = LLDPPacket.GetEncapsulated(packet);127 if (llp != null)128 {129 rows[1] = "LLDP";130 rows[5] = llp.ToString();131 return rows;132 }133 return rows;134 }135 //链路层136 PPPPacket ppp = PPPPacket.GetEncapsulated(packet);137 if (ppp != null)138 {139 rows[1] = "PPP";140 rows[3] = "--";141 rows[4] = "--";142 rows[5] = "协议类型:" + ppp.Protocol.ToString();143 return rows;144 145 }146 //PPPSerial147 PppSerialPacket ppps = PppSerialPacket.GetEncapsulated(packet);148 if (ppps != null)149 {150 rows[1] = "PPP";151 rows[3] = "--";152 rows[4] = "0x" + ppps.Address.ToString("X2");153 rows[5] = "地址:" + ppps.Address.ToString("X2") + " 控制:" + ppps.Control.ToString() + " 协议类型:" + ppps.Protocol.ToString();154 return rows;155 }156 //Cisco HDLC157 CiscoHDLCPacket hdlc = CiscoHDLCPacket.GetEncapsulated(packet);158 if (hdlc != null)159 {160 rows[1] = "Cisco HDLC";161 rows[3] = "--";162 rows[4] = "0x" + hdlc.Address.ToString("X2");163 rows[5] = "地址:" + hdlc.Address.ToString("X2") + " 控制:" + hdlc.Control.ToString() + " 协议类型:" + hdlc.Protocol.ToString();164 return rows;165 }166 #warning 需要测试167 PacketDotNet.Ieee80211.MacFrame ieee = Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data) as PacketDotNet.Ieee80211.MacFrame;168 if (ieee != null)169 {170 rows[1] = "IEEE802.11 MacFrame";171 rows[3] = "--";172 rows[4] = "--";173 rows[5] = "帧校验序列:" + ieee.FrameCheckSequence.ToString() + " 封装帧:" + ieee.FrameControl .ToString();174 return rows;175 }176 PacketDotNet.Ieee80211.RadioPacket ieeePacket = Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data) as PacketDotNet.Ieee80211.RadioPacket;177 if (ieeePacket != null)178 {179 rows[1] = "IEEE Radio";180 rows[5] = "Version=" + ieeePacket.Version.ToString();181 }182 LinuxSLLPacket linux = Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data) as LinuxSLLPacket;183 if (linux != null)184 {185 rows[1] = "LinuxSLL";186 rows[5] = "Tyep=" + linux.Type.ToString() + " Protocol=" + linux.EthernetProtocolType.ToString();187 }188 return rows;189 }190 }191 #endregion192 193 194 195 196 }
虽然写的比较渣,但是思路还是蛮清晰的,额~~有砖头~~~~~
先不管了,来一起看看结果吧
图4 采集到网络数据
终于有点激动了,那么我们接下来就是要实现详细的数据了,不过看了上面的方式代码的解析方式,你会不会有点想法呢。好吧,我这边只把TCP的解析方式放在这里了
1 //Tcp 2 TreeNode TCPNode; 3 TreeNode TcpFlagNode; 4 TreeNode TcpChecksumNode; 5 TreeNode TcpOptionsNode; 6 private void TCP(TcpPacket tcp) 7 { 8 if (TCPNode == null) 9 {10 TCPNode = CreatNode("TCP", 6);11 }12 TCPNode.Nodes.Clear();13 //port14 TCPNode.Nodes.Add("Source Port: " + tcp.SourcePort.ToString());15 TCPNode.Nodes.Add("Destination Port: " + tcp.DestinationPort.ToString());16 // Seq and Ack17 TCPNode.Nodes.Add("Sequence Number: " + tcp.SequenceNumber.ToString() + " [0x" + tcp.SequenceNumber.ToString("X") + "]");18 TCPNode.Nodes.Add("Acknowledgement Number: " + tcp.AcknowledgmentNumber.ToString() + " [0x" + tcp.AcknowledgmentNumber.ToString("X") + "]");19 //Data Offset20 TCPNode.Nodes.Add("Data Offset: " + (tcp.DataOffset * 4).ToString() + " [0x" + tcp.DataOffset.ToString("X") + "]");21 //Flags22 #region Flags23 if (TcpFlagNode == null)24 {25 TcpFlagNode = new TreeNode();26 }27 TcpFlagNode.Nodes.Clear();28 TcpFlagNode.Text = "Flags: [" + Format.TcpFlagType(tcp) + "] [0x" + string.Format("{0:X2}", tcp.AllFlags) + "]";29 TcpFlagNode.Nodes.Add("000. .... .... = Reserved");30 TcpFlagNode.Nodes.Add("...0 .... .... = Nonce");31 TcpFlagNode.Nodes.Add(".... " + Format.getStaus(tcp.CWR) + "... .... = CWR");32 TcpFlagNode.Nodes.Add(".... ." + Format.getStaus(tcp.ECN) + ".. .... = ECN-Echo");33 TcpFlagNode.Nodes.Add(".... .." + Format.getStaus(tcp.Urg) + ". .... = URG");34 TcpFlagNode.Nodes.Add(".... ..." + Format.getStaus(tcp.Ack) + " .... = ACK");35 TcpFlagNode.Nodes.Add(".... .... " + Format.getStaus(tcp.Psh) + "... = PSH");36 TcpFlagNode.Nodes.Add(".... .... ." + Format.getStaus(tcp.Rst) + ".. = RST");37 TcpFlagNode.Nodes.Add(".... .... .." + Format.getStaus(tcp.Syn) + ". = SYN");38 TcpFlagNode.Nodes.Add(".... .... ..." + Format.getStaus(tcp.Fin) + " = FIN");39 TCPNode.Nodes.Add(TcpFlagNode);40 #endregion41 //WinSize42 TCPNode.Nodes.Add("Window Size: " + tcp.WindowSize.ToString());43 //check Sum44 if (TcpChecksumNode == null)45 {46 TcpChecksumNode = new TreeNode();47 }48 TcpChecksumNode.Nodes.Clear();49 TcpChecksumNode.Text = "Checksum: 0x" + tcp.Checksum.ToString("X") + " [" + (tcp.ValidChecksum ? "Valid" : "Invalid") + "]";50 if (!tcp.ValidChecksum)51 {52 TCPNode.BackColor = TcpChecksumNode.BackColor = System.Drawing.Color.Red;53 TCPNode.ForeColor = TcpChecksumNode.ForeColor = System.Drawing.Color.White;54 }55 else56 {57 TCPNode.BackColor = TcpChecksumNode.BackColor = Tree.BackColor;58 TCPNode.ForeColor = TcpChecksumNode.ForeColor = Tree.ForeColor;59 }60 61 TcpChecksumNode.Nodes.Add("Correct: " + tcp.ValidTCPChecksum.ToString());62 TCPNode.Nodes.Add(TcpChecksumNode);63 //Urgent64 TCPNode.Nodes.Add("Urgent Pointer: " + tcp.UrgentPointer.ToString() + " [0x" + tcp.UrgentPointer.ToString("X") + "]");65 //Options66 if (tcp.Options.Length > 0)67 {68 if (TcpOptionsNode == null)69 {70 TcpOptionsNode = new TreeNode();71 }72 TcpOptionsNode.Nodes.Clear();73 TcpOptionsNode.Text = "Options: " + tcp.Options.Length.ToString() + " bytes";74 // [0x" + BitConverter.ToString(tcp.Options).Replace("-", "").PadLeft(12, '0') + "]75 if (tcp.OptionsCollection != null)76 {77 var tmpColl = tcp.OptionsCollection;78 for (int i = 0; i < tmpColl.Count; i++)79 {80 TcpOptionsNode.Nodes.Add(tmpColl[i].ToString());81 }82 }83 TCPNode.Nodes.Add(TcpOptionsNode);84 }85 Tree.Nodes.Add(TCPNode);86 87 88 AppNode(tcp.PayloadData, tcp.SourcePort, tcp.DestinationPort);89 }
好了接下来让我们看看最终的结果吧
图5 带有数据协议解析的成果图
接下来就是一个关于文件存取的功能,毕竟可以把数据保存下来也是一件很酷的事情,(好吧,我是真的想不出一个好的理由……)
1 //打开文件 2 private void btnOpen_Click(object sender, EventArgs e) 3 { 4 OpenFileDialog od = new OpenFileDialog(); 5 od.Filter = "pcap文件|*.pcap"; 6 7 if (od.ShowDialog() == DialogResult.OK) 8 { 9 Clear();10 11 ICaptureDevice offDev = new SharpPcap.LibPcap.CaptureFileReaderDevice(od.FileName);12 RawCapture tempPacket;13 offDev.Open();14 while ((tempPacket = offDev.GetNextPacket()) != null)15 {16 packetList.Add(tempPacket);17 ShowDataRows(tempPacket);18 }19 offDev.Close();20 21 }22 }23 24 25 //文件保存26 private void btnSave_Click(object sender, EventArgs e)27 {28 SaveFileDialog sd = new SaveFileDialog();29 sd.Filter = "Pcap文件|*.pcap";30 if (sd.ShowDialog() == DialogResult.OK)31 {32 var offDev = new SharpPcap.LibPcap.CaptureFileWriterDevice(sd.FileName);33 foreach (var i in packetList)34 {35 offDev.Write(i);36 }37 MessageBox.Show("文件保存成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);38 }39 }
因为这里我们是直接通过Sharppcap调用winpcap内置的数据包存取方式,这样的好处是,我们可以用Wrieshark等一些其他抓包工具打开这些文件,当然如果你愿意也可以用自己的方式存取,只要保证在内存中可以转为RawCapture的数据列表就可以了
到这里了,制作一个简单的协议分析基本是完成了,这里是代码下载地址:http://yun.baidu.com/s/1dDDELzF
最后是一些个别说明,我们在监听网卡的时候有个模式的选项
DeviceMode devMode = DeviceMode.Promiscuous;//数据采集模式
这里有连个模式一个常规模式 DeviceMode.Normal 和一个混杂模式 DeviceMode.Promiscuous主要是用于判断在网卡的数据监听方式,混杂模式就是接收所有经过网卡的数据包,包括不是发给本机的包。默认情况下网卡只把发给本机的包(包括广播包)传递给上层程序,其它的包一律丢弃。简单的讲,混杂模式就是指网卡能接受所有通过它的数据流,不管是什么格式,什么地址的。事实上,计算机收到数据包后,由网络层进行判断,确定是递交上层(传输层),还是丢弃,还是递交下层(数据链路层、MAC子层)转发。(这段来自百度)
还有一个就是关于超时设定,我们直接在程序中定义为1000ms 也就是1秒,也就是说通过winpcap每个1s向程序推送一次监听到的数据,这个值一定要合理设置,如果设置过短,就可能以为频繁提交数据造成性能开销和引起丢包,而如果过长再回造成界面响应慢,总之自己看情况设定吧
好了今天就写到这里吧,不足之处还请指正,祝你阅读愉快
NetAnalyzer下载地址
NetAnalzyer交流群:39753670 (PS 只提供交流平台,群主基本不说话^_^)
[转载请保留作者信息 作者:冯天文 网址:http://www.cnblogs.com/twzy/p/4769797.html ]
- C#版的抓包软件
- 抓包软件的学习
- 类似wireshark的抓网络包软件
- Wireshark抓包软件的使用
- 抓包软件fiddler
- 软件抓包
- 抓包辅助软件
- C# 网络抓包
- C#抓包
- Sniffer抓包软件学习
- 抓包软件charles4.0
- mac 抓包软件 Charles
- 链表(抓包软件)
- CatchPacket网络抓包软件
- Mac抓包软件Charles
- Charles抓包软件简介
- 基于WinPcap的抓包软件开发(一)
- 推荐一款比较好用的网络抓包软件
- Docker私有仓库管理之Harbor搭建
- ServerSocket 与 Socket的区别
- 安卓+apache ftp server上传文件遇到的问题:服务器ip地址、异步线程的使用
- DAY4作业-结合盒子模型,发布一张卡片
- 纯HTML+CSS静态百度登录界面制作
- C#版的抓包软件
- 解决使用jQuery采用append添加的元素事件无效的方法
- centos7 克隆后的虚拟机配置过程
- 二叉树:三叉链表
- PopWindow 仿淘宝消息浮窗弹出
- MySQL数据查询(日总结)
- nginx代理websocket,出现websocket 1006 (abnormal closure)错误
- meta功能总结
- poj2752_Seek the Name,Seek the Fame_KMP(next数组定义的应用)