曾經寫過一個遠程網絡抓包工具,為啥原遠程抓了?在解決現網問題,或者統計數據,需要快速准確的抓取特點的網絡吧。我們應用都是windows機器,經常需要抓XX服務某A機到XX服務B機的網絡包,定位機器、查部署情況、IP地址、負載配置、同時操作多台設備等等都特別耗時,因為有了這個工具的設計初衷。於是開始設計使用調用Winpcap組件實現去抓,后來發現生產環境很多機器並未安裝,同時winpcap 也無法支持silence方式安裝。
所以不得不想其他方案,這里就想到了RawSocket了,通過RawSocket 獲取全量網絡包,讓后實現winpcap相同的filter 功能,將RawSocket 數據量保持為標准的pcap格式文件,問題得到完美的解決。
代碼實現如下:
1. rawsocket
1: public class RawSocket
2: {
3: private Socket _socket;
4: private IPAddress _address;
5: public Action<TcpPacket> OnTcpPacketCapture;
6: public Action<byte[], int> OnRawDataCapure;
7: public RawSocket(IPAddress address)
8: {
9: _address = address;
10: _socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
11: _socket.Bind(new IPEndPoint(address, 0));
12: }
13:
14: public bool Capture()
15: {
16: bool isOk = true;
17: try
18: {
19: _socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);
20: byte[] inBytes = new byte[] { 1, 0, 0, 0 };
21: byte[] outBytes = new byte[] { 0, 0, 0, 0 };
22: _socket.IOControl(IOControlCode.ReceiveAll, inBytes, outBytes);
23: if (0 != outBytes[0] + outBytes[1] + outBytes[2] + outBytes[3]) { isOk = false; }
24: }
25: catch (SocketException)
26: {
27: isOk = false;
28: }
29:
30: if (isOk)
31: {
32: while (true)
33: {
34: //以太網MTU 最大為1500
35: byte[] buffer = new byte[1500];
36:
37: int count = _socket.Receive(buffer, SocketFlags.None);
38:
39: if (OnRawDataCapure != null)
40: OnRawDataCapure(buffer, count);
41:
42: if (OnTcpPacketCapture != null)
43: {
44: IPPacket ip = new IPPacket(buffer,0,count);
45: Console.WriteLine(ip);
46: if (ip.Protocol == IPProtocolType.TCP)
47: {
48: TcpPacket tcp = new TcpPacket(ip);
49: OnTcpPacketCapture(tcp);
50: }
51: }
52: }
53: }
54:
55: return isOk;
56: }
57:
58: public void Stop()
59: {
60: if (_socket != null)
61: {
62: _socket.Shutdown(SocketShutdown.Both);
63: _socket.Close();
64: }
65: }
66: }
89: class SocketData
90: {
91: public Socket Socket { get; set; }
92: public Byte[] Data { get; set; }
93: }
94: public class DataBuffer
95: {
96: public byte[] RawBuffer;
97: public int RawStart;
98: public int RawCount;
99:
100: private byte[] buffer;
101: private int start;
102: private int end;
103: public int Length { get { return end - start; } }
104: public byte this[int index] { get { return buffer[start + index]; } }
105:
106: public DataBuffer(byte[] data)
107: : this(data, 0, data.Length) { }
108:
109: public DataBuffer(byte[] data, int start, int count)
110: {
111: this.RawBuffer= this.buffer = data;
112: this.RawStart= this.start = start;
113: this.RawCount = count;
114: this.end = start + count;
115: }
116:
117: public void SetPosition(int position)
118: {
119: this.start += position;
120: }
121:
122: public byte[] Data
123: {
124: get
125: {
126: byte[] data = new byte[this.Length];
127: Array.Copy(this.buffer, this.start, data, 0, data.Length);
128: return data;
129: }
130: }
131: }
132:
133: public class IPPacket
134: {
135: public int Version; //版本號
136: public int HeadLen; //頭長度
137: public int DiffServices; //區分服務
138: public int DataLen;//數據長度
139: public int Identification; //標志
140: public int Flag; //標識 3bit
141: public int Excursion;//片偏移 13bit
142: public byte TTL;//生存周期
143: public IPProtocolType Protocol; //協議
144: public int CheckSum; //校驗和
145: public IPAddress SrcAddr; //源地址
146: public IPAddress DestAddr; //目標地址
147: public byte[] option; //選項
148: public DataBuffer Data; //IP Payload
149:
150: public IPPacket(byte[] data) : this(new DataBuffer(data)) { }
151: public IPPacket(byte[] data, int start, int count) : this(new DataBuffer(data, start, count)) { }
152:
153: public IPPacket(DataBuffer data)
154: {
155: Version = (data[0] & 0xF0) >> 4;
156: HeadLen = (int)(data[0] & 0x0F) * 4;
157: DiffServices = (int)data[1];
158: DataLen = ((int)data[2] << 8) + (int)data[3];
159: Identification = ((int)data[5] << 8) + (int)data[5];
160: Flag = data[6] >> 5;
161: Excursion = (((int)data[6] & 0x1F) << 8) + (int)data[7];
162: TTL = data[8];
163: Protocol = (IPProtocolType)data[9];
164: CheckSum = ((int)data[10] << 8) + (int)data[11];
165:
166: byte[] addr = new byte[4];
167: for (int i = 0; i < 4; i++)
168: addr[i] = data[12 + i];
169: SrcAddr = new IPAddress(addr);
170:
171: addr = new byte[4];
172: for (int i = 0; i < 4; i++)
173: addr[i] = data[16 + i];
174: DestAddr = new IPAddress(addr);
175:
176: //option
177: if (HeadLen > 20)
178: {
179: option = new byte[HeadLen - 20];
180: for (int i = 0; i < option.Length; i++)
181: option[i] = data[20 + i]; //會包括填充部分
182: }
183:
184: data.SetPosition(HeadLen);
185: Data = data;
186: }
187:
188: public override string ToString()
189: {
190: StringBuilder sb = new StringBuilder();
191: sb.AppendFormat("SrcAddr:{0} DestAddr={1}\r\n", SrcAddr.ToString(), DestAddr.ToString());
192: sb.AppendFormat("CheckSum: {0} Protocol:{1}\r\n", CheckSum, Protocol.ToString());
193: sb.AppendFormat("Version: {0} HeadLen:{1}\r\n", Version, HeadLen);
194: sb.AppendFormat("DataLen: {0} DiffServices:{1}\r\n", DataLen, DiffServices);
195: return sb.ToString();
196: }
197: }
198:
199: public class TcpPacket
200: {
201: public int SrcPort = 0;//源端口
202: public int DestPort = 0;//目標端口
203: public uint SequenceNo = 0;//序號
204: public uint NextSeqNo = 0;//確認號
205: public int HeadLen = 0;//頭長度
206: public int Flag = 0;//控制位
207: public int WindowSize = 0;//窗口
208: public int CheckSum = 0;//校驗和
209: public int UrgPtr = 0;//緊急指針
210: public byte[] option;//選項
211: public IPPacket IPPacket;
212: public DataBuffer Data;
213: public byte[] Body { get { return Data.Data; } }
214:
215: public TcpPacket(byte[] data) : this(new IPPacket(new DataBuffer(data))) { }
216: public TcpPacket(byte[] data, int start, int count) : this(new IPPacket(new DataBuffer(data, start, count))) { }
217:
218: public TcpPacket(IPPacket packet)
219: {
220: if (packet.Protocol != IPProtocolType.TCP)
221: throw new NotSupportedException(string.Format("TcpPacket not support {0}", packet.Protocol));
222: this.IPPacket = packet;
223:
224: DataBuffer data = packet.Data;
225: SrcPort = ((int)data[0] << 8) + (int)data[1];
226: DestPort = ((int)data[2] << 8) + (int)data[3];
227:
228: SequenceNo = ((uint)data[7] << 24) + ((uint)data[6] << 16) + ((uint)data[5] << 8) + ((uint)data[4]);
229: NextSeqNo = ((uint)data[11] << 24) + ((uint)data[10] << 16) + ((uint)data[9] << 8) + ((uint)data[8]);
230:
231: HeadLen = ((data[12] & 0xF0) >> 4)*4;
232: //6bit保留位
233: Flag = (data[13] & 0x3F);
234: WindowSize = ((int)data[14] << 8) + (int)data[15];
235: CheckSum = ((int)data[16] << 8) + (int)data[17];
236: UrgPtr = ((int)data[18] << 8) + (int)data[19];
237: //option
238: if (HeadLen > 20)
239: {
240: option = new byte[HeadLen - 20];
241: for (int i = 0; i < option.Length; i++)
242: option[i] = data[20 + i]; //會包括填充部分
243: }
244:
245: data.SetPosition(this.HeadLen);
246: Data = data;
247: }
248:
249: public override string ToString()
250: {
251: StringBuilder sb = new StringBuilder();
252: sb.AppendFormat("SrcPort:{0} DestPort={1}\r\n", SrcPort, DestPort);
253: sb.AppendFormat("SequenceNo:{0} NextSeqNo={1}\r\n", SequenceNo, NextSeqNo);
254: sb.AppendFormat("HeadLen:{0} Flag={1}\r\n", HeadLen, Flag);
255: sb.AppendFormat("WindowSize:{0} CheckSum={1}\r\n", WindowSize, CheckSum);
256: return sb.ToString();
257: }
258: }
259:
260: public enum IPProtocolType : byte
261: {
262: /// <summary> Dummy protocol for TCP. </summary>
263: IP = 0,
264: /// <summary> IPv6 Hop-by-Hop options. </summary>
265: HOPOPTS = 0,
266: /// <summary> Internet Control Message Protocol. </summary>
267: ICMP = 1,
268: /// <summary> Internet Group Management Protocol.</summary>
269: IGMP = 2,
270: /// <summary> IPIP tunnels (older KA9Q tunnels use 94). </summary>
271: IPIP = 4,
272: /// <summary> Transmission Control Protocol. </summary>
273: TCP = 6,
274: /// <summary> Exterior Gateway Protocol. </summary>
275: EGP = 8,
276: /// <summary> PUP protocol. </summary>
277: PUP = 12,
278: /// <summary> User Datagram Protocol. </summary>
279: UDP = 17,
280: /// <summary> XNS IDP protocol. </summary>
281: IDP = 22,
282: /// <summary> SO Transport Protocol Class 4. </summary>
283: TP = 29,
284: /// <summary> IPv6 header. </summary>
285: IPV6 = 41,
286: /// <summary> IPv6 routing header. </summary>
287: ROUTING = 43,
288: /// <summary> IPv6 fragmentation header. </summary>
289: FRAGMENT = 44,
290: /// <summary> Reservation Protocol. </summary>
291: RSVP = 46,
292: /// <summary> General Routing Encapsulation. </summary>
293: GRE = 47,
294: /// <summary> encapsulating security payload. </summary>
295: ESP = 50,
296: /// <summary> authentication header. </summary>
297: AH = 51,
298: /// <summary> ICMPv6. </summary>
299: ICMPV6 = 58,
300: /// <summary> IPv6 no next header. </summary>
301: NONE = 59,
302: /// <summary> IPv6 destination options. </summary>
303: DSTOPTS = 60,
304: /// <summary> Multicast Transport Protocol. </summary>
305: MTP = 92,
306: /// <summary> Encapsulation Header. </summary>
307: ENCAP = 98,
308: /// <summary> Protocol Independent Multicast. </summary>
309: PIM = 103,
310: /// <summary> Compression Header Protocol. </summary>
311: COMP = 108,
312: /// <summary> Raw IP packets. </summary>
313: RAW = 255,
314: /// <summary> IP protocol mask.</summary>
315: MASK = 0xff
316: }
2. PcapDumper 用戶保存pcap文件格式:
1: public class PcapDumper
2: {
3: private PcapStream context;
4: public PcapDumper(Stream stream)
5: {
6: context = new PcapStream(stream);
7: PcapHeader header = new PcapHeader();
8: header.Writer(context);
9: stream.Flush();
10: }
11:
12: public void Write(byte[] buffer, int offset, int count)
13: {
14: RecordPacket record = new RecordPacket(buffer, offset, count);
15: record.Write(context);
16: }
17:
18: public void Write(TcpPacket packet)
19: {
20: Write(packet.IPPacket.Data.RawBuffer, packet.IPPacket.Data.RawStart, packet.IPPacket.Data.RawCount);
21: }
22:
23: public void Write(IPPacket packet)
24: {
25: Write(packet.Data.RawBuffer, packet.Data.RawStart, packet.Data.RawCount);
26: }
27:
28: public void Flush()
29: {
30: context.Flush();
31: }
32: }
33:
34: public class PcapHeader
35: {
36: //for intel x86 save as litle-endian
37: public uint MagicNumber = 0xa1b2c3d4;// native byte ordering. magic number
38: public ushort VersionMajor = 0x2; //current 2.4
39: public ushort VersionMinor = 0x4;
40: public uint TimeZone = 0;//timeezone 實際上沒有被使用過
41: public uint SigFigs = 0;//useless
42: public uint Snaplen = 65535;//maxlen just set the max
43: public uint Network = 1;//Ethernet
44:
45: public void Writer(PcapStream stream)
46: {
47: stream.Write(MagicNumber);
48: stream.Write(VersionMajor);
49: stream.Write(VersionMinor);
50: stream.Write(TimeZone);
51: stream.Write(SigFigs);
52: stream.Write(Snaplen);
53: stream.Write(Network);
54: }
55: }
56:
57:
58: public class RecordPacket
59: {
60: private DateTime unixTime = new DateTime(1970, 1, 1);
61: private int EntherNetLen = 14;//srcIP+dstIP+ verion 14b 頭
62: private int offset;
63: private int count;
64: private byte[] buffer;
65:
66: public RecordPacket(byte[] buffer, int offset, int count)
67: {
68: DateTime time = DateTime.UtcNow;
69: TimeSec = (uint)((time - unixTime).TotalSeconds);
70: TimeMicroSec = (uint)time.Millisecond * 1000;
71: CapLen = Len = (uint)count + 14;
72: this.offset = offset;
73: this.count = count;
74: this.buffer = buffer;
75: }
76:
77: public uint TimeSec; /* timestamp seconds */
78: public uint TimeMicroSec; /* timestamp microseconds */
79: public uint CapLen; /* number of octets of packet saved in file */
80: public uint Len; /* actual length of packet */
81:
82: public void Write(PcapStream stream)
83: {
84: stream.Write(TimeSec);
85: stream.Write(TimeMicroSec);
86: stream.Write(CapLen);
87: stream.Write(Len);
88: stream.Write((ulong)1, 6);
89: stream.Write((ulong)2, 6); //這兩個應該是鏈路mac地址,隨便代替就行
90: stream.Write(8);
91: stream.Write(0);//IP 0x08
92: stream.Write(buffer, offset, count);
93: }
94: }
95:
96: public class PcapStream
97: {
98: private byte[] tempBuffer = new byte[8];
99: private Stream stream;
100: public PcapStream(Stream stream)
101: {
102: this.stream = stream;
103: }
104:
105: public void Flush()
106: {
107: stream.Flush();
108: }
109:
110: public void Write(ushort v)
111: {
112: Write(v, 2);
113: }
114:
115: public void Write(uint v)
116: {
117: Write(v, 4);
118: }
119:
120: public void Write(ulong v)
121: {
122: Write(v, 8);
123: }
124:
125: public void Write(ulong v, int numbytes)
126: {
127: ulong val = v;
128: for (int x = 0; x < numbytes; x++)
129: {
130: tempBuffer[x] = (byte)(val & 0xff);
131: val >>= 8;
132: }
133: stream.Write(tempBuffer, 0, numbytes);
134: }
135:
136: public void Write(byte value)
137: {
138: stream.WriteByte(value);
139: }
140:
141: public void Write(byte[] buffer, int offset, int count)
142: {
143: stream.Write(buffer, offset, count);
144: }
145:
146: public void Write(byte[] buffer)
147: {
148: stream.Write(buffer, 0, buffer.Length);
149: }
150: }
151: }
152:
153: /// 參考資料:http://wiki.wireshark.org/Development/LibpcapFileFormat
154: //magic_number: used to detect the file format itself and the byte ordering. The writing application writes 0xa1b2c3d4 with it's native byte ordering format into this field. The reading application will read either 0xa1b2c3d4 (identical) or 0xd4c3b2a1 (swapped). If the reading application reads the swapped 0xd4c3b2a1 value, it knows that all the following fields will have to be swapped too.
155: //version_major, version_minor: the version number of this file format (current version is 2.4)
156: //thiszone: the correction time in seconds between GMT (UTC) and the local timezone of the following packet header timestamps. Examples: If the timestamps are in GMT (UTC), thiszone is simply 0. If the timestamps are in Central European time (Amsterdam, Berlin, ...) which is GMT + 1:00, thiszone must be -3600. In practice, time stamps are always in GMT, so thiszone is always 0.
157: //sigfigs: in theory, the accuracy of time stamps in the capture; in practice, all tools set it to 0
158: //snaplen: the "snapshot length" for the capture (typically 65535 or even more, but might be limited by the user), see: incl_len vs. orig_len below
159: //network: link-layer header type, specifying the type of headers at the beginning of the packet (e.g. 1 for Ethernet, see tcpdump.org's link-layer header types page for details); this can be various types such as 802.11, 802.11 with various radio information, PPP, Token Ring, FDDI, etc.
160: //0 BSD loopback devices, except for later OpenBSD
161: //1 Ethernet, and Linux loopback devices
162: //6 802.5 Token Ring
163: //7 ARCnet
164: //8 SLIP
165: //9 PPP
166: //10 FDDI
167: //100 LLC/SNAP-encapsulated ATM
168: //101 raw IP, with no link
169: //102 BSD/OS SLIP
170: //103 BSD/OS PPP
171: //104 Cisco HDLC
172: //105 802.11
173: //108 later OpenBSD loopback devices (with the AF_value in network byte order)
174: //113 special Linux cooked capture
175: //114 LocalTalk
176:
177: //ts_sec: the date and time when this packet was captured. This value is in seconds since January 1, 1970 00:00:00 GMT; this is also known as a UN*X time_t. You can use the ANSI C time() function from time.h to get this value, but you might use a more optimized way to get this timestamp value. If this timestamp isn't based on GMT (UTC), use thiszone from the global header for adjustments.
178: //ts_usec: the microseconds when this packet was captured, as an offset to ts_sec. Beware: this value shouldn't reach 1 second (1 000 000), in this case ts_sec must be increased instead!
179: //incl_len: the number of bytes of packet data actually captured and saved in the file. This value should never become larger than orig_len or the snaplen value of the global header.
180: //orig_len: the length of the packet as it appeared on the network when it was captured. If incl_len and orig_len differ, the actually saved packet size was limited by snaplen.
3. 最后是Filter功能:
1: class PcapFilter
2: {
3: private const string CONST_AND = "and";
4: private const string CONST_OR = "or";
5: private const string CONST_HOST = "host";
6: private const string CONST_TCP = "tcp";
7: private const string CONST_PORT = "port";
8:
9: private string filter;
10: private Func<TcpPacket, bool> filterAction = (packet) => { return true; };
11: private List<Func<TcpPacket, bool>> _funcs=new List<Func<TcpPacket,bool>>();
12: private bool _isAnd = false;
13:
14: public PcapFilter(string filter)
15: {
16: if (!string.IsNullOrEmpty(filter))
17: {
18: this.filter = filter.Trim().ToLower();
19: if (!string.IsNullOrEmpty(this.filter))
20: TryParserFilter();
21: }
22: }
23:
24: private void TryParserFilter()
25: {
26: string[] sp = filter.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
27: sp = sp.Where(p => p.Trim().Length > 0).ToArray();
28:
29: if (sp.Where(p => p == CONST_OR || p == CONST_AND).Count() > 1)
30: throw new NotSupportedException("only support one or/and");
31:
32: TokenState ns = TokenState.Name;
33: string name = string.Empty;
34:
35: for (int i=0;i<sp.Length;i++)
36: {
37: if (ns == TokenState.Name)
38: {
39: if (sp[i] == CONST_TCP && sp[i + 1] == CONST_PORT)
40: {
41: name = CONST_TCP;
42: i++;
43: ns = TokenState.Value;
44: }
45: else if (sp[i] == CONST_HOST)
46: {
47: name = CONST_HOST;
48: ns = TokenState.Value;
49: }
50: else
51: throw new NotSupportedException();
52: }
53: else if (ns == TokenState.Operation)
54: {
55: if (sp[i] == CONST_AND)
56: _isAnd = true;
57: else
58: _isAnd = false;
59: ns = TokenState.Name;
60: }
61: else if (ns == TokenState.Value)
62: {
63: ParseAction(name, sp[i]);
64: ns = TokenState.Operation;
65: }
66: }
67:
68: filterAction=(packet)=>
69: {
70: if (_funcs.Count > 0)
71: {
72: if (_funcs.Count == 1)
73: return _funcs[0](packet);
74: else
75: {
76: if(_isAnd)
77: return _funcs[0](packet) && _funcs[1](packet);
78: }
79: }
80: return true;
81: };
82: }
83:
84: private void ParseAction(string name, string value)
85: {
86: if (name == CONST_HOST)
87: {
88: IPAddress ip = IPAddress.Parse(value);
89: _funcs.Add(new Func<TcpPacket, bool>((pachet) =>
90: {
91: return pachet.IPPacket.SrcAddr.Equals(ip) || pachet.IPPacket.DestAddr.Equals(ip);
92: }));
93: }
94: else
95: {
96: int port = Convert.ToInt32(value);
97: _funcs.Add(new Func<TcpPacket, bool>((packet) =>
98: {
99: return packet.SrcPort == port || packet.DestPort == port;
100: }));
101: }
102: }
103:
104: public bool IsFilter(TcpPacket packet)
105: {
106: return filterAction(packet);
107: }
108: }
109:
110: enum TokenState
111: {
112: Name,
113: Value,
114: Operation,
115: }