Click here to Skip to main content
Email Password   helpLost your password?
Sample Image - CSNetworkSniffer.jpg

Introduction

In this article, I will discuss the working of a simple network sniffer which can parse IP, TCP, UDP, and DNS packets.

Capturing the Packets

// For sniffing the socket to capture the packets 
// has to be a raw socket, with the address family
// being of type internetwork, and protocol being IP
mainSocket = newSocket(AddressFamily.InterNetwork, SocketType.Raw,
                       ProtocolType.IP);

// Bind the socket to the selected IP address
mainSocket.Bind(newIPEndPoint(IPAddress.Parse(cmbInterfaces.Text),0));

// Set the socket options
mainSocket.SetSocketOption(SocketOptionLevel.IP,  //Applies only to IP packets
                           SocketOptionName.HeaderIncluded, //Set the include header
                           true);                           //option to true

byte[] byTrue = newbyte[4]{1, 0, 0, 0};
byte[] byOut = newbyte[4];

//Socket.IOControl is analogous to the WSAIoctl method of Winsock 2
mainSocket.IOControl(IOControlCode.ReceiveAll,  //SIO_RCVALL of Winsock
                     byTrue, byOut);

//Start receiving the packets asynchronously
mainSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None,
                        newAsyncCallback(OnReceive), null);

For capturing the packets, we use a raw socket and bind it to the IP address. After setting the proper options for the socket, we then call the IOControl method on it. Notice that IOControl is analogous to the Winsock2WSAIoctl method. The IOControlCode.ReceiveAll implies that all incoming and outgoing packets on the particular interface be captured.

The second parameter passed to IOControl with IOControlCode.ReceiveAll should be TRUE so an array byTrue is created and passed to it (thanks to Leonid Molochniy for this). Next we start receiving all packets asynchronously.

Analysing the Packets

The IP datagram encapsulates the TCP and UDP packets. This further contains the data sent by the application layer protocols such as DNS, HTTP, FTP, SMTP, SIP, etc. Thus a TCP packet is received inside the IP datagram, like this:

                       
+-----------+------------+--------------------+
| IP header | TCP header |        Data        |  
+-----------+------------+--------------------+

So the first thing that we need to do is to parse the IP header. The stripped version of the IP header class is shown below, the comments describe the things as they happen.

public classIPHeader 
{ 
  //IP Header fields 
  private byte byVersionAndHeaderLength; // Eight bits for version and header 
                                         // length 
  private byte byDifferentiatedServices; // Eight bits for differentiated 
                                         // services
  private ushort usTotalLength;          // Sixteen bits for total length 
  private ushort usIdentification;       // Sixteen bits for identification
  private ushort usFlagsAndOffset;       // Eight bits for flags and frag. 
                                         // offset 
  private byte byTTL;                    // Eight bits for TTL (Time To Live) 
  private byte byProtocol;               // Eight bits for the underlying 
                                         // protocol 
  private short sChecksum;               // Sixteen bits for checksum of the 
                                         //  header 
  private uint uiSourceIPAddress;        // Thirty two bit source IP Address 
  private uint uiDestinationIPAddress;   // Thirty two bit destination IP Address 

  //End IP Header fields   
  private byte byHeaderLength;             //Header length 
  private byte[] byIPData = new byte[4096]; //Data carried by the datagram
  public IPHeader(byte[] byBuffer, int nReceived)
  {
    try
    {
    //Create MemoryStream out of the received bytes
    MemoryStream memoryStream = newMemoryStream(byBuffer, 0, nReceived);
    
    //Next we create a BinaryReader out of the MemoryStream
    BinaryReader binaryReader = newBinaryReader(memoryStream);

    //The first eight bits of the IP header contain the version and
    //header length so we read them
    byVersionAndHeaderLength = binaryReader.ReadByte();

    //The next eight bits contain the Differentiated services
    byDifferentiatedServices = binaryReader.ReadByte();
    
    //Next eight bits hold the total length of the datagram
    usTotalLength = 
             (ushort) IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //Next sixteen have the identification bytes
    usIdentification = 
              (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //Next sixteen bits contain the flags and fragmentation offset
    usFlagsAndOffset = 
              (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //Next eight bits have the TTL value
    byTTL = binaryReader.ReadByte();

    //Next eight represent the protocol encapsulated in the datagram
    byProtocol = binaryReader.ReadByte();

    //Next sixteen bits contain the checksum of the header
    sChecksum = IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //Next thirty two bits have the source IP address
    uiSourceIPAddress = (uint)(binaryReader.ReadInt32());

    //Next thirty two hold the destination IP address
    uiDestinationIPAddress = (uint)(binaryReader.ReadInt32());

    //Now we calculate the header length
    byHeaderLength = byVersionAndHeaderLength;

    //The last four bits of the version and header length field contain the
    //header length, we perform some simple binary arithmetic operations to
    //extract them
    byHeaderLength <<= 4;
    byHeaderLength >>= 4;

    //Multiply by four to get the exact header length
    byHeaderLength *= 4;

    //Copy the data carried by the datagram into another array so that
    //according to the protocol being carried in the IP datagram
    Array.Copy(byBuffer, 
               byHeaderLength, //start copying from the end of the header
               byIPData, 0, usTotalLength - byHeaderLength);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "MJsniff", MessageBoxButtons.OK, 
                        MessageBoxIcon.Error);
    }
 }
//Please see the attached codes for the properties…
}

Firstly, the class contains the data members corresponding to the fields of the IP header. Kindly see RFC 791 for a detailed explanation of the IP header and its fields. The constructor of the class takes the bytes received and creates a MemoryStream on the received bytes and then creates a BinaryReader to read the data from the MemoryStream byte-by-byte. Also note that the data received from the network is in big-endian form so we use the IPAddress.NetworkToHostOrder to correct the byte ordering. This has to be done for all non-byte data members.

The TCP, UDP headers are also parsed in an identical fashion, the only difference is that they are read from the point where the IP header ends. As an example of parsing the application layer protocols, the attached project can detect DNS packets as well.

References

Updates

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGood Stuff
milpool
1:35 11 Nov '09  
This is a good start for me
GeneralQuestion
Alireza_1362
2:04 4 Oct '09  
Hello ,And Thanks for your good program
i have a question from you :
why you use IPAddress.NetWorktoHostOrder() In your classess
please let me know
thanks again
GeneralTCP?
Member 6542899
16:56 10 Sep '09  
Hi,
how can i get working tcp with this Sniffer?
Exists there any workarounds for the vista/xp update?
thx
GeneralThanks, great project
CSharpDavid
12:30 9 Sep '09  
Learned enough to solve all my problems.

Ran nicely under windows 7 - turn the firewall off !!!
GeneralNice work!
alejandro29A
3:55 26 Aug '09  
short, elegant and well documented article Thumbs Up

Dios existe pero duerme...
Sus pesadillas son nuestra existencia.
(Ernesto Sabato)

QuestionCan I Get the Type of Message a Packet is having ( like email , Data or word documnet etc) from a Web application
AjitonCodeProject
21:11 16 Apr '09  
Can I Get the Type of Message a Packet is having ( like email , Data or word documnet etc) from a Web application

I Req a applicaction to be made which would be implemented througha web Site
& that Would do packet sniffing on network & will find which message is sent via
packet i.e email or word document

could you please Help me

Ajit
OMG
GeneralNot work at Vista
RhbdfzHerf
2:31 9 Apr '09  
The sniffer cornestone code

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Raw,ProtocolType.IP);



not work at Vista, throw excetion with smth like "policy prevent create a socket that way" under admin user acount, with turned off firewall and anti-virus software. (Netstat shows about 10 tcp and 20 udp legal connections ).
At the same time clean Win32 code can create, bind, connect sockets.

So there is my question :

is somewhere a C# sniffer which work with current version (with all updates) Vista in default configuration under admin?
GeneralHow to Sniff outgoing packets?
Member 1868326
6:43 19 Nov '08  
In the text you write that "The IOControlCode.ReceiveAll implies that all incoming and outgoing packets on the particular interface be captured."
I cant capture outgoing packets anyway, how can this be accomplished?

best regards
Freundschaft
GeneralLosing a lot of packets
Max.Brin
7:52 13 Nov '08  
Hi,

I am running this on a server which listens to a hub, wireshark sniffs around 10,000 packets for 2 seconds, and this sniffer is showing only 2-3 connection per 10 seconds.

Why am I losing so many packets?
Generaloutgoing packets through VPN
People313
6:04 12 Nov '08  
Hi,

the sniffer works good on LAN but when I connect to internet through VPN connection it captures only incoming packets. outgoing packets are are lost for some reason.

Is there any possible way to sniff outgoing packets from browser to internet when connecting through VPN?

Any kinds of ideas will be appreciated,
Thank you.
Jokeimplement
beck17
20:21 15 Oct '08  
Such good implement code . Thanks a lot

happy for coding

Generalplease help determine the packet size..
eladvini
16:01 16 Aug '08  
the ip total length is in byte or Kbyte?
how much do i need to add in order to get the real size(including all headers)??
GeneralThis is no good....
the pink jedi
5:10 13 May '08  
As of the latest Windows Vista updates, raw socket will only be able to sniff incoming UDP packets. Bye bye to winsocks and raw socket. All of you should give your thanks to steve gibson of Microsoft.
GeneralRe: This is no good....
Hitesh Sharma
6:37 15 May '08  
This article was mainly done to try stuff out and play with the raw protocol headers. It was never supposed to be a full proof solution to any of your problems.
QuestionRe: This is no good....
volch
4:10 18 Jul '08  
not good is not the point....it's sh....

However I just started working on a CDP (Cisco dicovery Package) sniffer and would like to do it in C#

Any good idea how to do that, And be sure it works also in the future without loading other dlls...?
GeneralRe: This is no good....
Member 2818058
12:44 21 Oct '08  
It is true that limitations have been put on raw sockets in Windows XP SP2 and Vista in the name of security, but to say that raw sockets can now only sniff incoming UDP packets is patently not true. You can look here for further information.

When you sniff with a raw socket, the data is always returned to you as a datagram. For TCP, this simply means that the streaming TCP session is packetized. At that point, you are responsible for reconstituting the stream, but to say that you can't sniff TCP or other IP traffic is simply wrong.
GeneralRe: This is no good....
firexware
10:26 17 Jul '09  
steve gibson doesnt work for microsoft, he works for his own company. And he knows what he's talking about, if there was raw sockets in windows, we would have every botnet sockstressing random sites off the net so be thankful.
QuestionTo Alawi Alkaff and others (How can i read data from printer port before printing in C#)
Krishna Prasad RVS
5:39 6 May '08  
StringBuilder result = new StringBuilder();
for ( int i =0 ;i < byteData.Length ; i++)
result.Append(byteData[i]);
tcpNode.Nodes.Add("Data: " + result.ToString());


Hi,

I am trying to read the print data from printer port, could you pleae let me know where i need to add the above specified code snippet

I placed the code in TCP Header constructor and not getting exact data what i have given for printing, could you please let me know

This is very very ....... important to me

If possible could you please send me the source code

Thanks in Advance

--Krishna

Krishna Prasad RVS
Microsoft Certified Application Developer (MCAD)

QuestionHow can i read data from printer port before printing in C#
Krishna Prasad RVS
3:36 6 May '08  
Hi,

How can i read data (data sent to printer port from notepad or any other application) from printer port before printing based on printer port and ip adress (in case of network printer) or any other way in C#

This is very very urgent for me

Thanks in advance

--Krishna

Krishna Prasad RVS
Microsoft Certified Application Developer (MCAD)

QuestionA few questions...
gazda
5:35 31 Mar '08  
1) Is there a way to modify this code (maybe in socket options?) to really *capture* and not to sniff packets, so that data *doesnt* go "through" the program to it's destination?

2) Is there a way to sniff TCP and UDP packets only for a specific port number, so i don't have to parse all the packets?

I'm going to try and build a simple port forwarder, just so you understand the context... Thanks,

Bojan
GeneralRe: A few questions...
Hitesh Sharma
8:24 1 Apr '08  
1) No you can't do that. This is the job of a firewall.

2) Again, I can't think of a way to do that, sorry.

Thanks.
GeneralRe: A few questions...
gazda
7:33 2 Apr '08  
Thank you.
AnswerRe: A few questions...
Max.Brin
7:50 13 Nov '08  
2. mainSocket.Bind(newIPEndPoint(IPAddress.Parse(cmbInterfaces.Text),0));

Change the 0 to the desired port.
GeneralRe: A few questions...
di~v~inci
11:10 23 Apr '09  
Hi Max,

I have tried this but no worky. I will try work something out and post.
AnswerRe: A few questions...
Max.Brin
9:14 12 Oct '09  
Hi!

1. Sniffing by a certain filter can be easily done by changing the pcap filter.
I dont know nor remember if this specific project uses winpcap.
When you sniff you must use winpcap and not winsock because winsock absolutely sucks. It droppes like 90% of the packets.
When using winpcap you can define the desired filter, like "tcp port 80 and tcp port 110".

2. Actually "capturing" the packets is not possible with c#. You are trying to imitate a firewall's behaviour and this can only be done by writing a capture kernel driver with "c" language.


Last Updated 11 Feb 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010