网络嗅探器和分析程序 - 第二部分






4.81/5 (6投票s)
使用 C# .NET 6.0 Windows Form (Sharppcap, PacketDotNet) 编写的网络嗅探器和分析程序
引言
您可以阅读本文的第一部分,请访问以下链接:网络嗅探器和分析程序 - 第一部分。
扫描网络以获取所有可用设备
在本部分中,我们将学习如何获取网络上所有可用设备及其信息,例如 MAC 地址、主机名、IPv4 和 IPv6。
实际上,有两种方法可以确定设备是否连接到网络
第一种方法是 ping 目标 IP,然后等待其响应,并在特定超时时间内等待。如果收到响应,则表示可用;如果没有收到响应,则表示不可用。
这种方法的缺点是目标设备必须允许传入的回显请求,才能 ping 目标设备。
firewall > Advanced > Settings. check Allow incoming echo request.
另一种方法是应用 Dns.GetHostEntryAsync(hostNameOrAddress)
。
在目标 IP 上,此方法将获取有关目标 IP 的所有信息,或者在 IP 不在网络中时返回错误或超时错误。
在下面的代码中,我将向您展示如何以优雅的方式执行此操作
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
,状态为 Invalid Host。两种方法之间的区别在于,在第一种方法中,我们必须手动设置超时时间,而在第二种方法中,超时时间将自动设置。现在,在定义获取网络中可用设备的主要方法之后,我们将增强我们的 ,,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";
}
Using the Code
要使用特定超时时间 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);
}
要使用自动超时时间 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
函数的结果,我们将使用 ,,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 类 ping 所有可能的 IP。在这里,我要指出 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);
}
}
}
历史
- 2022 年 10 月 28 日:初始版本