[C#] 네트워크 스니퍼 및 분석기 프로그램 - 2부

2022. 12. 16. 19:26프로그래밍

728x90

https://www.codeproject.com/Articles/5345381/Network-Sniffer-and-Analyzer-Program-Part-2

 

Network Sniffer and Analyzer Program - Part 2

Network Sniffer and Analyzer Program written in C# .NET 6.0 Windows Form (Sharppcap, PacketDotNet)

www.codeproject.com

 
 

C# .NET 6.0 Windows Form(Sharppcap, PacketDotNet)으로 만들어 보는 네트워크 스니퍼 및 분석기 프로그램

 

이 프로그램으로 DNS 패킷을 캡처하고 분석할 수 있습니다. 또한 네트워크에서 다른 사용자 패킷을 스니핑할 수 있도록 ARP를 스푸핑할 수 있습니다. 이 프로그램은 먼저 네트워크를 스캔하여 네트워크에 연결된 장치를 찾은 다음, 검색 된 장치 중 하나를 선택하여 해당 장치에 대한 수신 및 발신 패킷을 스니핑할 수 있습니다.

 

소개

이 기사의 첫 번째 부분은 네트워크 스니퍼 및 분석기 프로그램 - 1부 링크에서 읽을 수 있습니다.

 

사용 가능한 모든 장치를 얻기 위한 네트워크 스캔하기

이 부분에서는 우리는 네트워크에서 사용 가능한 모든 장치와 mac 주소, 호스트 이름, ipv4 및 ipv6과 같은 정보를 얻는 방법을 배울 것입니다.

 

실제로 장치가 네트워크에 연결되어 있는지 여부를 확인하는 방법에는 다음 두 가지가 있습니다:

 

첫 번째는 대상 IP를 핑한 다음 응답을 기다리는 것입니다. 특정 timeout 전에 응답을 받으면 사용 가능하지만 응답을 받지 못하면 사용할 수 없다는 뜻입니다.

 

이 방법의 단점은 수신 echo 요청이 대상 장치를 ping할 수 있도록 대상 장치에서 허용해야 한다는 것입니다.

firewall > Advanced > Settings. check Allow incoming echo request.
 

다른 방법은 Dns.GetHostEntryAsync(hostNameOrAddress)를 적용해 보는 것입니다.

대상 IP에서 이 메서드는 대상 IP에 대한 모든 정보를 가져오거나 IP가 네트워크에 없는 경우 오류 또는 timeout 오류를 반환 할 것입니다.

 

다음 코드에서 나는 우아한 방식으로 수행하는 방법을 보여 줄 것입니다:

public class PingDeviceCompletedEventArgs : EventArgs
{
	public PingDeviceStatus Status;

	public string IP;
}

public enum PingDeviceStatus
{
	Pending,
	Completed,
	InvalidHost,
	Timeout
}

public delegate void PingDeviceCompletedEventHandler
	   (Object sender, PingDeviceCompletedEventArgs e);

public class PingDevice
{
	public event PingDeviceCompletedEventHandler PingCompleted;
	public PingDevice()  { }

	public async void SendAsync
		   (string hostNameOrAddress, int millisecond_time_out)
	{            
			new Thread(async delegate ()
			{
				PingDeviceCompletedEventArgs args = 
						  new PingDeviceCompletedEventArgs();
				PingDeviceCompletedEventHandler handler = this.PingCompleted;
				try
				{
					args.Status = PingDeviceStatus.Pending;
					var result = Task.Run(() => 
					Dns.GetHostEntryAsync(hostNameOrAddress)).Wait
						(millisecond_time_out);
					if (!result)
					{
						args.Status = PingDeviceStatus.Timeout;
						args.IP = null;
						if (handler != null)
							handler(this, args);
					}
					else
					{
						args.Status = PingDeviceStatus.Completed;
						args.IP = hostNameOrAddress;
						if (handler != null)
							handler(this, args);
					}
				}
				catch (Exception ex)
				{
					args.Status = PingDeviceStatus.InvalidHost;
					args.IP = null;
					if (handler != null)
						handler(this, args);
				}

			}).Start();
		}

		public async void SendAsync(string hostNameOrAddress)
		{
			{
				new Thread(async delegate ()
				{
					PingDeviceCompletedEventArgs args = 
							  new PingDeviceCompletedEventArgs();
					PingDeviceCompletedEventHandler handler = this.PingCompleted;
					try
					{
						args.Status = PingDeviceStatus.Pending;
						var result = await Dns.GetHostEntryAsync(hostNameOrAddress);
					   
						if (result == null)
						{
							args.Status = PingDeviceStatus.Timeout;
							args.IP = null;
							if (handler != null)
								handler(this, args);
						}
						else
						{
							args.Status = PingDeviceStatus.Completed;
							args.IP = hostNameOrAddress;
							if (handler != null)
								handler(this, args);
						}
					}
					catch (Exception ex)
					{
						args.Status = PingDeviceStatus.InvalidHost;
						args.IP = null;
						if (handler != null)
							handler(this, args);
					}
				}).Start();
			}
		}
}
 

PingDevice 클래스는 "SendAsych"라는 두 개의 비동기 함수를 가지고 있습니다. 두 번째에서는 Dns.GetHostEntryAsync(hostNameOrAddress) 함수를 호출하고 'await' 키워드를 설정합니다. 이 단어는 이벤트의 트리거링을 구성하여 대상 함수가 실행될 때까지 실행 중인 스레드가 다른 작업을 수행하지 못하도록 합니다. 최종적으로 Dns.GetHostEntryAsync는 대상 장치를 찾아 해당 정보를 반환하거나 잘못된 호스트 상태라면 오류를 발생시키고 Null을 반환 할 것입니다 .

 

두 메서드의 차이점은 첫 번째 메서드에서는 시간 제한을 수동으로 설정해야 하고

두 번째 메서드는 네트워크에서 사용 가능한 장치를 가져오는 기본 방법을 정의한 후 자동으로 설정된다는 것입니다. ,,PingDeviceCompletedEventArgs" 클래스를 사용하여 호스트 이름, mac 주소 및 ipv6 주소와 같은 발견된 장치에 대한 모든 정보를 가져옵니다:

public class PingDeviceCompletedEventArgs : EventArgs
{
   public PingDeviceStatus Status;

   public string IP;

   public string Host => (IP != null) ? GetHostName(IP) : null;

   public List<string> Ipv6 => (IP != null) ? getIPV6Addr(IP) : null;

   public string MAC => (IP != null) ? getMACAddresse(IP) : null;

   List<string> getIPV6Addr(string ipv4)
   {
	   try
	   {
		   IPHostEntry ipEntry = System.Net.Dns.GetHostEntry(ipv4);
		   IPAddress[] addr = ipEntry.AddressList;
		   List<string> foundIPs = new List<string>();
		   foreach (IPAddress iPAddress in addr)
		   {
			   if (iPAddress.AddressFamily ==
				   System.Net.Sockets.AddressFamily.InterNetworkV6)
			   {
				   foundIPs.Add(iPAddress.ToString());
			   }
		   }
		   return foundIPs;
	   }
	   catch (Exception ex) { return null; }

	   return null;
   }

   string GetHostName(string ipAddress)
   {
	   try
	   {
		   IPHostEntry entry = Dns.GetHostEntry(ipAddress);
		   if (entry != null)
		   {
			   return entry.HostName;
		   }
	   }
	   catch (SocketException)
	   {
		   // MessageBox.Show(e.Message.ToString());
	   }

	   return null;
   }

   [DllImport("iphlpapi.dll", ExactSpelling = true)]
   public static extern int SendARP(int DestIP, int SrcIP,
		  [Out] byte[] MacAddr, ref int MacLen);

   public string getMACAddresse(string Ipaddress)
   {
	   IPAddress address = IPAddress.Parse(Ipaddress);
	   try
	   {
		   byte[] MACByte = new byte[6];
		   int MACLength = MACByte.Length;
		   SendARP((int)address.Address, 0, MACByte, ref MACLength);
		   string MACSSTR = BitConverter.ToString(MACByte, 0, 6);
		   if (MACSSTR != "00-00-00-00-00-00")
			   return PhysicalAddress.Parse(MACSSTR).ToString();
	   }
	   catch (Exception ex) { return "not detected"; }
	   return "not detected";
   }
}
 

 

코드 사용해 보기

특정 시간 timeout timeout,으로 단일 장치를 ping하려면 다음 코드를 사용할 수 있습니다:

public void Ping_Device(string host, int time_out)
{
    Thread thread = new Thread(() =>
      {
          try
          {
              PingDevice ping = new PingDevice();
              ping.PingCompleted +=
              new PingDeviceCompletedEventHandler(Ping_Completed);
              ping.SendAsync(host, time_out);
          }
          catch
          {

          }
      });

    thread.Start();

 //   Ping_Threads.Add(thread);
}
 

자동 timeout을 사용해서 하나의 장치에 ping을 한다면 다음 코드를 사용할 수 있습니다:

void Ping_Device(string host)
{
   Thread thread = new Thread(() =>
   {
	   try
	   {
		   PingDevice ping = new PingDevice();
		   ping.PingCompleted +=
		   new PingDeviceCompletedEventHandler(Ping_Completed);
		   ping.SendAsync(host);
	   }
	   catch
	   {

	   }
   });

   thread.Start();

   //   Ping_Threads.Add(thread);
}
 

시간이 부적절하거나 필요하지 않은 경우 Ping_Device 함수을 처리하기 위해 사용 가능한 모든 장치를 반환하지 않는 수동 timeout을 사용하는 것과 달리 장치를 제외하지 않고 네트워크에서 사용 가능한 모든 장치를 반환하기 때문에 자동 timeout를 사용하는 것이 좋습니다.

결과적으로 다음 코드와 같이 ,,PingDeviceCompletedEventArgs" Eventhandler를 사용합니다 :

void Ping_Completed(object sender, PingDeviceCompletedEventArgs e)
{
   if (e.IP != null)
   {
	   string ip = (string)e.IP;
	   if (e.Status == PingDeviceStatus.Completed)
	   {
		// to do
	   }
   }
   else
   {
	   // to do
   }
}
 

마지막으로, 우리는 IP 클래스를 기반으로 가능한 모든 Ips를 ping 할 것이며

여기에서 클래스 A와 B의 경우에는 짧은 시간에 많은 수의 객체를 메모리에 올려 놓을 것이기 때문에 리소스가 매우 소모되고 많은 시간이 걸리고 결국 메모리 부족 예외를 뺕을 것이 예상 되므로,  클래스 A 및 B와 함께 이 함수를 사용하지 않는 것을 추천 합니다:

void Ping_ALL_Devices(string Gateway)
{
   string[] array = Gateway.Split('.');

   IPClass iPClass = getIPClass(Gateway);

   if (iPClass == IPClass.D)
	   MessageBox.Show("Cannot Ping Multicast Address");

   if (iPClass == IPClass.E)
	   MessageBox.Show("Cannot Ping Address From Class E");

   if (iPClass == IPClass.A)
   {
	   for (int a = 1; a < 255; a++)
	   {
		   for (int b = 1; b < 255; b++)
		   {
			   for (int c = 1; c < 255; c++)
			   {
				   string ping_var = array[0] + "." + a + "." + b + "." + c;
				   Ping_Device(ping_var);
			   }
		   }
	   }
   }

   if (iPClass == IPClass.B)
   {
	   for (int a = 1; a < 255; a++)
	   {
		   for (int b = 1; b < 255; b++)
		   {
			   string ping_var = array[0] + "." +
					  array[1] + "." + a + "." + b;
			   Ping_Device(ping_var);
		   }
	   }
   }

   if (iPClass == IPClass.C)
   {
	   for (int a = 1; a < 255; a++)
	   {
		   string ping_var = array[0] + "." +
				  array[1] + "." + array[2] + "." + a;
		   Ping_Device(ping_var);
	   }
   }
}
 

History

  • 28th October, 2022: Initial version

 

License

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

이상.

 

728x90