Implementation of network packet monitoring with raw socket programming under C #

Source: Internet
Author: User
Tags bind bool copy header set socket socket tostring
Programming | Network talk about socket programming, we may think of QQ and IE, yes. There are many network tools such as Peer-to-peer, NetMeeting and other applications implemented in the application layer, but also with the socket to achieve. The socket is a network programming interface that is implemented at the network application level, and Windows Sockets includes a set of system components that take advantage of Microsoft Windows message-driven features. The socket Specification version 1.1 was released in January 1993 and is widely used in windows9x operating systems that subsequently appear. The socket Specification version 2.2 (which is Winsock2.2 on the Windows platform, also known as Winsock2) was released in May 1996 and Windows NT 5.0 and later Windows systems Support Winsock2, in Winsock2 , it supports the original socket of multiple transfer protocols, overlapping I/O model, service quality control and so on.

This article introduces some of the Windows Sockets programming of the original socket (raw socket) implemented in C #, and the network packet monitoring technology implemented on this basis. Compared with Winsock1, Winsock2 is the most obvious support for the raw socket socket type, the use of raw sockets, the network card can be set to promiscuous mode, in this mode, we can receive IP packets on the network, of course, including the purpose is not the IP packet, Through the original socket, we can also more easily control the various protocols under Windows, but also to the network at the bottom of the transmission mechanism control.

In the example of this article, I am in Nbyte. The Basicclass namespace implements the Rawsocket class, which contains our core technology for implementing packet monitoring. Before implementing this class, you need to write an IP header structure to temporarily store information about the network envelope:


[StructLayout (LAYOUTKIND.EXPLICIT)]
public struct Ipheader
{
[FieldOffset (0)] public byte Ip_verlen; I4 bit Header length + 4-bit IP version number
[FieldOffset (1)] public byte Ip_tos; 8-bit Service type TOS
[FieldOffset (2)] public ushort Ip_totallength; 16-bit Packet total length (bytes)
[FieldOffset (4)] public ushort ip_id; 16-bit identification
[FieldOffset (6)] public ushort Ip_offset; 3-bit Sign
[FieldOffset (8)] public byte Ip_ttl; 8-bit live time TTL
[FieldOffset (9)] public byte Ip_protocol; 8-bit protocol (TCP, UDP, ICMP, ETC.)
[FieldOffset] public ushort ip_checksum; 16-bit IP header checksum
[FieldOffset ()] public uint ip_srcaddr; 32-bit Source IP address
[FieldOffset] public uint ip_destaddr; 32-bit Destination IP address
}

In this way, when each packet arrives, the data in the packet can be converted into a Ipheader object by force type conversion.
The following is the beginning of the Rawsocket class, which begins by defining several parameters, including:
private bool error_occurred; Whether the socket generated an error while receiving the package
public bool keeprunning; Whether to proceed
private static int len_receive_buf; The length of the resulting data stream
byte [] receive_buf_bytes; Bytes Received
Private socket socket = NULL; Declaring sockets
There is also a constant:
const int Sio_rcvall = unchecked ((int) 0x98000001);//listening for all packets

The sio_rcvall here is to instruct the Rawsocket to receive all packets, to be used in a later Iocontrl function, and to initialize some variable parameters in the following constructor:

Public Rawsocket ()//constructors
{
Error_occurred=false;
Len_receive_buf = 4096;
Receive_buf_bytes = new BYTE[LEN_RECEIVE_BUF];
}

The following function implements the creation of the rawsocket and binds it to the endpoint (ipendpoint: native IP and Port):
public void Createandbindsocket (string IP)//Establish and bind sockets
{
Socket = new Socket (addressfamily.internetwork, Sockettype.raw, Protocoltype.ip);
Socket. Blocking = false; Socket non-blocking state
Socket. Bind (New IPEndPoint (Ipaddress.parse (IP), 0)); Binding sockets

if (SetSocketOption () ==false) error_occurred=true;
}
In which, in the creation socket of a socket = new socket (addressfamily.internetwork, Sockettype.raw, Protocoltype.ip), there are 3 parameters:

The first argument is to set the address family, which is described on MSDN as "Specify the addressing scheme for the socket instance to resolve the address", and when binding to endpoints (IPEndPoint), you need to use internetwork members, that is, the IP version 4 address format, This is also one of the addressing schemes (addressfamily) used in most socket programming today.

The second parameter sets the type of socket that we use for the raw type, SocketType is an enumerated data type, and the raw socket type supports access to the underlying transport protocol. By using Sockettype.raw, you can communicate not only with Transmission Control Protocol (TCP) and User Datagram Protocol (UDP), but also by using Internet messaging (ICMP) and Network Group Management Protocol (IGMP). When you send, your application must provide a complete IP header. The received datagram keeps its IP headers and options unchanged when it returns.

The third parameter sets the protocol type, and the Socket class notifies the Windows Socket API of the requested protocol using the ProtocolType enumeration data type. 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 setsocketoption in the socket class, where we define the IO control function setsocketoption , it is defined as follows:

private bool SetSocketOption ()//SET 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 operation mode, accept all packets, this step is the key, you must set the socket raw and IP level to be available Sio_rcvall
int ret_code = socket. IOControl (Sio_rcvall, in, out);
Ret_code = out[0] + out[1] + out[2] + out[3];//to synthesize 4 8-bit bytes into a 32-bit integer
if (ret_code!= 0) Ret_value = false;
}
catch (SocketException)
{
Ret_value = false;
}
return ret_value;
}

Where the socket option must be set to include the IP header, 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 in Windows we cannot use the receive function to receive data on RAW sockets because all IP packets are handed over to the system core first. And then transferred to the user program, when sending a Raws socket packet (such as SYN), the core does not know, and there is no record of the data being sent or connected, so when the remote host responds, the core of the system discards all of the packets, which can not reach the application. Therefore, you cannot simply use the receive function to receive these datagrams. To achieve the purpose of receiving data, a sniffer must be used to receive all the packets that pass and then sift through them to stay in line with what we need. You can set up Sio_rcvall to receive packets on all networks. Next, let's introduce the IOControl function. MSDN explains what it says is setting sockets for low-level operation 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 opcode
LPVOID Lpvinbuffer,//pointer to input data stream
DWORD Cbinbuffer,//input data stream size (bytes)
LPVOID Lpvoutbuffer,//pointer to output data stream
DWORD cbOutBuffer,//output data stream size (in bytes)
Lpdword lpcbbytesreturned,//point to actual value of output byte throttling number
lpwsaoverlapped lpoverlapped,//point to a wsaoverlapped structure
Lpwsaoverlapped_completion_routine lpcompletionroutine//point to routines that are executed when the operation completes
);

C # 's IOControl function is not as complex as the WSAIOCTL function, which includes only the control operation code, input byte throttling, output byte throttling three parameters, but these three parameters are sufficient. We see a byte array defined in the function: Byte []in = new Byte[4]{1, 0, 0, 0} Actually it is a DWORD or Int32 with a value of 1, same byte []out = new byte[4]; Also, it rounded up an int, as WSA The value pointed to by the parameter lpcbbytesreturned in the IOCTL function.
Because errors may occur when you set socket options, you need to pass the error flag with a value:

public bool ErrorOccurred
{
Get
{
return error_occurred;
}
}

The following function implements the receipt of the packet:

Resolves received packets, Forms Packetarrivedeventargs event data class objects, and raises packetarrival events
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;//to stream data into a 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 IP protocol version
E.ipversion = temp_version. ToString ();

The following statement extracts additional parameters from the Packetarrivedeventargs object
TEMP_IP_SRCADDR = 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;
Assign the IP header in the BUF to the Ipheaderbuffer in the Packetarrivedeventargs
Array.copy (buf,0,e.ipheaderbuffer,0, (int) e.headerlength);
The contents of the package in the BUF are assigned to the Messagebuffer in the Packetarrivedeventargs
Array.copy (buf, (int) e.headerlength,e.messagebuffer,0, (int) e.messagelength);
}
Raise Packetarrival Event
Onpacketarrival (e);
}

It is noted that, in the above function, we use the pointer this so-called unsafe code, visible in C # pointers and shift operations these original operations can also give programmers the convenience of programming. Declare the Packetarrivedeventargs class object in a function so that the packet information is passed through events through the Onpacketarrival (e) function. Where the Packetarrivedeventargs class is a nested class in the Rawsocket class, it inherits the system event class and encapsulates the information contained in other packet headers, such as IP, port, protocol, and so on. In the function that initiates the receive packet, we use the asynchronous operation method, the following function opens the asynchronous listener interface:

public void Run ()//start listening
{
IAsyncResult AR = socket. BeginReceive (receive_buf_bytes, 0, Len_receive_buf, Socketflags.none, New AsyncCallback (Callreceive), this);
}

The Socket.beginreceive function returns the interface of an asynchronous operation and declares the asynchronous callback function callreceive in the generation function BeginReceive of the interface, and spreads the received network data to Receive_buf_bytes, This allows an asynchronous callback function with an interface parameter with an asynchronous operation to receive packets continuously:

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 receives a new packet when it hangs or ends an asynchronous read, which guarantees that each packet can be detected by the program.
The following statements are implemented to communicate with the outside world by declaring a proxy event handle:

public delegate void Packetarrivedeventhandler (Object sender, Packetarrivedeventargs args);
Event handle: Raising an event when a package arrives
Public event Packetarrivedeventhandler packetarrival;//declaration time handle function

This can achieve the acquisition of packet information, the use of asynchronous callback function, can improve the efficiency of receiving packets, and through the agent event to pass packet information to the outside world. Since all the packet information can be passed out, you can achieve the analysis of the packet: But Rawsocket task is not finished, and finally do not look at the closing socket AH:

public void Shutdown ()//close RAW socket
{
if (socket!= null)
{
Socket. Shutdown (Socketshutdown.both);
Socket. Close ();
}
}

The above introduces the Rawsocket class obtains the information in the packet through constructs the IP header, and realizes the packet receiving by the asynchronous callback function, and uses the time proxy handle and the custom packet information event class to send the packet information, thus realizes the network packet surveillance, This allows us to add some functions externally to analyze the packet.




Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.