Network package monitoring with C # Raw Socket

xiaoxiao2021-03-06  50

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, first define several parameters, including:

Private bool error_occurred; // Sockets Whether to generate error public bool keeprunning when receiving packets; // Continue to make private static int LEN_RECEIVE_BUF; // The length of the data stream is received byte [] receive_buf_bytes; // received bytes Private socket socket = null; // The declaration socket also has a constant: const Int Sio_rcvall = unchecked ((int) 0x98000001); // Listening to all packets here SiO_rcvall is indicating all packets, in the future In the IOCONTRL function, in the following constructor, the initialization of some variable parameters is implemented:

public RawSocket () // constructor {error_occurred = false; len_receive_buf = 4096; receive_buf_bytes = new byte [len_receive_buf];} implement the following function creates RawSocket, and with it the endpoint: tie (the IPEndPoint local IP and port) set:

public void CreateAndBindSocket (string IP) // create and bind a socket {socket = new Socket (AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP); socket.Blocking = false; // set unblocked socket 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); 3 parameters: The first parameter is to set the address family, the description on the MSDN is "Specify the address instance to resolve the address", when To bind the socket to the endpoint (IpendPoint), you need to use the InternetWork member, which is the address format of the IP version 4, which is also a addressing scheme (addressFamily). 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 to use Sio_rcvallint Ret_code = Socket.iocontrol ( SiO_RCVALL, IN, OUT; RET_CODE = OUT [0] OUT [1] OUT [2] OUT [3]; // Synthesate 4 8-bit bytes A 32-bit integer IF (RET_CODE! = 0) RET_VALUE = false;} cat_value = false;} Return Ret_Value;} where the socket must be set to contain the IP cladding, otherwise the iPheter 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 input data stream DWORD cbinbuffer, // Enter the size of the data stream (byte) LPVOID LPVOUTBUFFER // DWORD cbOutBuffer pointer to the output data stream, // size of the output data stream (bytes) LPDWORD lpcbBytesReturned, // points to the number of real-valued output stream of bytes LPWSAOVERLAPPED lpOverlapped, // points to a structure WSAOVERLAPPED LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine / / Point to the routine when the operation is completed); the IoControl function of the C # is not as complex as the WSAIOctL function, which includes only the control operation code, input byte stream, output byte stream, but these three parameters have 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. Because an error may occur when setting the socket option, you need to deliver the error flag with a value: public bool erroroccot {get {return error_occurred;}} The following functions are received:

// 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 PacketArrivedEventArgs (); // 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) // extraction 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 IP protocol version E.IPVERSION = TEMP_VERSION.TOSTRING (); // The following statement extraction other parameters of the object 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.MessageLength = (uint) len - e.HeaderLength; e. ReceiveBuffer = BUF; // assigns the IP head in BUF to iPheaderBuffrarray.copy in PacketarriveDeventargs (Buf, 0, E.PHEADERBuffer, 0, (int) E.HEADERLENGTH);

// Assign the content in the package in PacketarriveDeventargs (buf, (int) E.HeaderLength, E.MESSAGELENGTH);} // Triginal Packetarrival Event Onpacketarrival (e Everyone noticed that in the above functions, we used the so-called unsafe code of the pointer, and these original operations can also be programmed to programmakers in the C # in the C #. 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 starting the received packet, we use the asynchronous operation method. The following functions open the interface of asynchronous listening: public void run () // Start listening {ioasyncResult ar = socket.beginReceive (Receive_BUF_BYTES, 0, LEN_RECEIVE_BUF, SocketFlags .None, new asyncCallback (callReceive), this);} Socket.BeginReceive function returns an asynchronous operation interface, and declares that the asynchronous callback function CallReceive is declared in the generated function of this interface, and streams the received network data to Receive_buf_bytes, which can be continually received by an asynchronous callback function with an interface parameter with an asynchronous operation:

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 handler: raise an event public event PacketArrivedEventHandler PacketArrival packet arrives; // declaration handler function of time so that you can achieve access to the information packets, using asynchronous callback function, you can Improve the efficiency of receiving the 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 () // Turns Raw Socket {if (socket! = null) {socket.shutdown (socketshutdown.both); socket.close ();}} The RawSocket class has been introduced by constructing IP header. Information, and through the asynchronous callback function realize the reception of the packet, and use the time agent handle and the custom packet information event class to send the packet information to the monitoring of the network packet, so that we can be outside Add some functions to analyze the packet.

转载请注明原文地址:https://www.9cbs.com/read-82926.html

New Post(0)