Program the network package monitoring with the Raw Socket under C #
Talking up Socket programming, everyone may think of QQ and IE, yes. There are also many network tools such as P2P, NetMeeting, etc. Applications implemented by the application layer, but also to be implemented with socket. Socket is a network programming interface that implements the network application layer, and Windows Socket includes a system component that takes advantage of the characteristics of Microsoft Windows messaging. The Socket specification version 1.1 is released in January 1993 and is widely used in the Windows9x operating system that will thereafter. Socket Specification 2.2 (which is Winsock2.2 on the Windows platform, also called Winsock2) In May 1996, Windows NT 5.0 and later version of Windows system support Winsock2, in Winsock2, support multiple transport protocols Original socket, overlapping I / O model, service quality control, etc.
This article introduces some of Windows Sockets to the programming of the raw socket (Raw Socket) implemented with C #, and the network packet monitoring technology implemented on this basis. Compared with Winsock1, Winsock2 is the most obvious that supports the RAW Socket socket type. Using RAW socket, you can set the network card into a mixed mode. In this mode, we can receive the IP package on the network, of course, including the purpose Not a native IP package, through the original socket, we can also control the various protocols under Windows more freely, and can control the transmission mechanism of the network underlayer.
In this example, I implemented the RawSocket class in the NBYTE.BASICCLASS namespace, which contains core technologies we implements packet monitoring. Before achieving this class, you need to write an IP header structure to temporarily store some information about the network package:
[StructLayout (layoutkind.explicit)] public struct ipheader {[fieldoffset (0)] public byte ip_verlen; // i4 top length 4-bit IP version number [Fieldoffset (1)] public byte ip_tos; // 8 service type TOS [Fieldoffset (2)] public ushort ip_totallength; // 16-bit packet total length (byte) [fieldoffset (4)] public ushort ip_id; // 16-bit identifier [Fieldoffset (6)] public ushort ip_offset; // 3-bit flag [fieldoffset (8)] public byte ip_ttl; // 8-bit survival time TTL [Fieldoffset (9)] public byte ip_protocol; // 8-bit protocol (TCP, UDP, ICMP, ETC.) [FieldoffSet (10 )] public ushort ip_checksum; // 16-bit IP header checksum [Fieldoffset (12)] public uint ip_srcaddr; // 32 bitsource IP address [fieldoffset (16)] public uint ip_destaddr; // 32 bit IP address}
Thus, when each packet arrives, the data stream in the package can be converted into one IpHeader object with a mandatory type conversion. Let's start writing the Rawsocket class. First, a few parameters are defined first, including: private bool error_occurred; // Sockets Whether to generate error public bool keeprunning when receiving the package; // Continue Private Static int LEN_RECEIVE_BUF; / / Get the length of the data stream byte [] receive_buf_bytes; // Received byte private socket socket = null; // The declaration socket also has a constant: const Int sio_rcvall = unchecked ((int) 0x98000001); // Monitoring All Packets The SIO_RCVALL here is that the RawSocket receives all packets, in the later IOCONTRL function, in the following constructor, initialization of some variable parameters:
Public RawSocket () // Constructor {Error_occurred = false; len_receive_buf = 4096; Receive_Buf_BYTES = New Byte [LEN_RECEIVE_BUF];
The following function implements created RawSocket and binds it with endpoints (IpendPoint: Native IP and port): Public Void CreateAndBindsocket (String IP) // Create and bind socket {socket = new socket (addressFamily. Internetwork, sockettype.raw, protocoltype.ip); socket.blocking = false; // Set Socket Non-Block Status Socket.bind (iPadDress.Parse (IP), 0)); // Binding Socket
IF (setSocketoption () == false Error_occurred = true;} where a Socket = New Socket (AddressFamily.Internetwork, SocketType.RAW, ProtocolType.IP) is created: 3 parameters:
The first parameter is to set the address family, the description on the MSDN is "Specify the address of the socket instance to resolve the address", when you want to bind the socket to the endpoint (IpendPoint), you need to use the Internetwork member, That is, the address format of IP version 4 is used, which is also a addressing scheme (addressFamily) using most of today's socket programming.
The second parameter set type is the type of RAW we use. SocketType is an enumeration data type, and the RAW socket type supports access to the underlying Transfer Protocol. By using sockettype.raw, you can communicate using the Transmission Control Protocol (TCP) and User Data Normalization (UDP), or use the Internet Control Protocol (ICMP) and Internet Group Management Protocol (IGMP) to communicate. When sending, your application must provide a complete IP header. The received datagram is returned while returning the IP header and option constant. The third parameter setting protocol type, the Socket class uses the ProtocolType enumeration data type to notify the requested protocol to the Windows Socket API. The IP protocol is used here, so use the protocoltype.ip parameter.
There is a custom setSocketOption function in the createandbindsocket function, which is different from the setSocketopption in the Socket class. We define the setSocketoption with the IO control function. It is as follows:
private bool SetSocketOption () // set the 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]; // Low level mode of operation accepts all packets, this step is the key, you must set up Socket to RAW and IP Level can be available for SiO_rcvall int RET_CODE = Socket.iocontrol (SiO_RCVALL, IN, OUT; RET_CODE = OUT [0] OUT [1] OUT [2] OUT [3]; // Synthes up 4 8-bit bytes of 32-bit integer IF (RET_CODE! = 0 ) RET_VALUE = false;} cat_value = false;} Return Ret_Value;}
Among them, set the socket option to enable the socket containing an IP cladding, otherwise the ipHeader structure cannot be populated, and packet information cannot be obtained. int RET_CODE = Socket.ioControl (SiO_RCVALL, IN, OUT); is the most critical step in the function, because we can't use the Receive function in Windows to receive data on the Raw Socket, because all IP packs It is first submitted to the system core, then transfer to the user program, when sending a RAWS Socket package (such as SYN), the core does not know, there is no record that is sent or established, so when the remote host When responding, the core of the system throws all of these packages so that it is not possible. Therefore, it is not possible to simply use the received function to receive these datagrams. To reach the purpose of receiving data, you must use sniffing, receive all passed packets, and then filter, leaving the need to meet what we need. You can display the data packets on all networks by setting SiO_RCVALL. Let's introduce the IOCONTROL function. MSDN explains that it is to set the socket for low level mode, how low level operation method? In fact, this function is similar to the WSAIOCTL function in the API. The WSAIOCTL function is defined as follows: int WSAIOCTL (Socket S, // A specified socket dword dwioControlCode, / / control operand lpvoid lpvinbuffer, // pointing to the pointer DWORD cbinbuffer of the input data stream, // Enter the size of the data stream (word Number of: LPVOID LPVOUTBUFFER, / / Pointer DWORD CBOUTBUFFER, / / Output Data Stream (byte) LPDWORD LPCBBYTESRETURNED, / / Point to the real value of output byte stream LPWSAOVERLAPPED LPOVERLAPPED, // Point to one WSAOVERLAPPED structure lpwsaoverlapped_completion_routine lpcompletionRoutine // The routine that is executed when the operation is completed;
The IoControl function of the C # is unlike the WSAIOctL function, including only the control operation code, input byte stream, output byte stream, but these three parameters are already enough. We see a one-byte array in the function: byte [] in = new byte [4] {1, 0, 0, 0} In fact, it is a value of 1 DWORD or INT32, the same Byte [] OUT = New byte [4]; also, it is a value that is interested in the WSAiocTl function. The value of the parameter lpcbbytesreturned. Because it may have an error while setting the socket option, you need to deliver the error flag with a value: public bool errooroccurred {get {return error_occurred;}}
The following function is implemented by the data package:
// parse the received data packets, forming PacketArrivedEventArgs event data class object, and the event trigger 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 packetarrivedendArgs (); // New network packet information event
Fixed (Byte * fixed_buf = buf) {ipHeader * head = (ipHeader *) fixed_buf; // Put the data to IpHeader structure E.HeaderLength = (UINT) (Head-> ip_verlen & 0x0f) << 2; temp_protocol = Head-> ip_protocol; switch (temp_protocol) // Extract protocol type {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; // Extract the IP protocol version E.IPVERSION = TEMP_VERSION.TOSTOSTRING ();
// The following statements extract an additional object parameters temp_ip_srcaddr PacketArrivedEventArgs = 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 = * (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.MESSAGALENGTH = (uint) LEN - E.HEADERLENGTH;
E.ReceiveBuffer = BUF; // assigns the IP head in BUF to iPheaderBuffer Array.copy in PacketarriveDeventargs (buf, 0, E.ipheaderbuff, 0, (int) E.HeaderLength); // Put the package in BUF Content assignment to the MessageBuffer Array.copy in PacketarriveDeventargs; E.MESSAGELELENGTH, E.MessageLength;} // Raise Packetarrival Event Onpacketarrival (E);}
Everyone noticed that in the above function, we used the so-called unsafe code of the pointer. It can be seen that these original operations can also be programmed to programmakers in the C # middle pointers and shift operations. Declaring the PacketarriveDeventargs class object in a function to pass the information package information through the event through the event. The PacketarriveDeventargs class is the nested class in the RawSocket class, which inherits the system event (Event) class, encapsulated information contained in other data clauses of packets, ports, protocols. In the function of launching the received packet, we use the asynchronous operation method, the following functions open the interface of the asynchronous listening:
Public void run () // Start listening {ioasyncResult ar = socket.beginReceive (receive_buf_bytes, 0, len_receive_buf, socketflags.none, new asynccallback (callreceive),}
The Socket.BeginReceive function returns an asynchronous operation interface, and declares that the asynchronous callback function CallReceive is declared in the generation function based generation of this interface, and streams the received network data to Receive_BUF_BYTES, which can be used with an interface parameters with asynchronous operations. The asynchronous callback function constantly receives the packet:
private void CallReceive (IAsyncResult ar) // asynchronous callback {int received_bytes; received_bytes = socket.EndReceive (ar); Receive (receive_buf_bytes, received_bytes); if (KeepRunning) Run ();} This function ends when suspended or asynchronous read After receiving a new packet, this ensures that each packet can be detected by the program. By declaring the Agent's handle, communication:
Public Delegate Void PacketarriveDeventHandler (Object Sender, PacketarriveDeventArgs Args); // Event Handle: Packet arrival time trigger event public evenetarriveDeventhandler packetarrival; // declare time handle function
This allows the acquisition of packet information, and the asynchronous callback function can be used to improve the efficiency of the received data packet, and pass the package information to the outside through the proxy event. Since all packet information can be passed, you can achieve the analysis of the packet :) But RawSocket's task has not yet finished, and finally don't want to turn off the socket:
Public void shutdown () // Turn Raw Socket {if (socket! = null) {socket.shutdown (socketshutdown.both; socket.close ();}}
The RawSocket class introduces the information in the package by constructing the IP header, and implements the reception of the packet through the asynchronous callback function, and uses the time agent handle and the custom packet information event class to send the packet information to the information. Implement the monitoring of the network packet so that we can add some functions to the data package outside the external.