我有带有前端 GUI 的 ARP 扫描仪。
它工作得很好,直到我实现了实时显示何时找到主机。 当它在网络上找到活动主机时,它会添加包含 ip 地址和 mac 地址等信息的行,但由于某种原因它开始返回空行。以前没有实时返回时它可以工作。
这是我的后端代码:
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Text.RegularExpressions;
using System.Windows.Input;
using System.Runtime.InteropServices;
using System.Net;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;
using System.ComponentModel;
// TODO - jak navýšit čas na odpověď od stanice? Občas možná nestihne přijít odpověd, dle jiného skeneru jsou ip adresy např. dostupné
// TODO - navýšit rychlost skenování - použit více paralel tasků
// TODO - naimplementovaet log4net
namespace NetVision.Pages.NetworkTools
{
public partial class NetworkScan : Page
{
private ObservableCollection<string> Subnets { get; set; }
private ObservableCollection<ScanResult> ScanResults { get; set; }
// Regex pattern for subnet validation with CIDR notation
private static readonly Regex SubnetRegex = new Regex(
@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$",
RegexOptions.Compiled);
private int _totalHosts;
private int _scannedHosts;
public NetworkScan()
{
InitializeComponent();
cmbTypeScan.SelectedIndex = 0;
Subnets = new ObservableCollection<string>();
ScanResults = new ObservableCollection<ScanResult>();
lstSubnets.ItemsSource = Subnets;
dgScanResults.ItemsSource = ScanResults;
}
private void UpdateScanDescription()
{
if (txtScanDescription != null)
{
switch (cmbTypeScan.SelectedIndex)
{
case 0:
txtScanDescription.Text = "ARP scan works only on broadcast domain!";
break;
case 1:
txtScanDescription.Text = "Scans TCP/UDP opened ports.";
break;
case 2:
txtScanDescription.Text = "Scans TCP opened ports.";
break;
case 3:
txtScanDescription.Text = "Scans UDP opened ports.";
break;
case 4:
txtScanDescription.Text = "Uses ICMP echo requests to detect hosts.";
break;
default:
txtScanDescription.Text = "";
break;
}
}
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
UpdateScanDescription();
}
private void txtSubnet_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
AddSubnet();
e.Handled = true; // Prevents the ding sound
}
}
private void btnAddSubnet_Click(object sender, RoutedEventArgs e)
{
AddSubnet();
}
private void AddSubnet()
{
string subnet = txtSubnet.Text.Trim();
if (!string.IsNullOrWhiteSpace(subnet))
{
if (SubnetRegex.IsMatch(subnet))
{
Subnets.Add(subnet);
txtSubnet.Clear();
HideError();
}
else
{
ShowError("Invalid subnet format. Please use CIDR notation (e.g., 192.168.1.0/24 or 10.0.0.0/8).");
}
}
}
private void HideError()
{
txtErrorMessage.Text = string.Empty;
txtErrorMessage.Visibility = Visibility.Collapsed;
}
private void ShowError(string message)
{
txtErrorMessage.Text = message;
txtErrorMessage.Visibility = Visibility.Visible;
}
private void btnDeleteSubnet_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button && button.DataContext is string subnet)
{
Subnets.Remove(subnet);
}
}
private async void btnStartScan_ClickAsync(object sender, RoutedEventArgs e)
{
if (Subnets.Count > 0)
{
btnStartScan.IsEnabled = false;
btnStopScan.IsEnabled = true;
ScanResults.Clear();
// Reset progress
_scannedHosts = 0;
_totalHosts = 0;
progressGrid.Visibility = Visibility.Collapsed;
if (cmbTypeScan.Text == "ARP Scan")
{
await PerformArpScan();
}
btnStartScan.IsEnabled = true;
btnStopScan.IsEnabled = false;
}
else
{
txtErrorMessage.Text = "You need to add at least one subnet to scan.";
txtErrorMessage.Visibility = Visibility.Visible;
}
}
[DllImport("iphlpapi.dll", ExactSpelling = true)]
private static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);
private static uint macAddrLen = (uint)new byte[6].Length;
private async Task PerformArpScan()
{
var allHosts = new ConcurrentBag<IPAddress>();
foreach (var subnet in Subnets)
{
var hosts = GenerateIPRange(subnet);
foreach (var host in hosts)
{
allHosts.Add(host);
}
}
var results = new ConcurrentBag<ScanResult>();
_totalHosts = allHosts.Count;
_scannedHosts = 0;
await Dispatcher.InvokeAsync(() =>
{
progressGrid.Visibility = Visibility.Visible;
scanProgressBar.Value = 0;
scanProgressBar.Maximum = _totalHosts;
UpdateProgressText();
ScanResults.Clear();
});
await Task.Run(() =>
{
Parallel.ForEach(allHosts, new ParallelOptions { MaxDegreeOfParallelism = 50 }, ip =>
{
var result = SendArpRequest(ip);
if (result != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
ScanResults.Add(result);
});
}
Interlocked.Increment(ref _scannedHosts);
Application.Current.Dispatcher.Invoke(() =>
{
scanProgressBar.Value = _scannedHosts;
UpdateProgressText();
});
});
});
await Dispatcher.InvokeAsync(() =>
{
progressGrid.Visibility = Visibility.Collapsed;
});
}
private void UpdateProgressText()
{
progressText.Text = $"Scanning: {_scannedHosts}/{_totalHosts} hosts";
}
public static IEnumerable<IPAddress> GenerateIPRange(string cidrNotation)
{
string[] parts = cidrNotation.Split('/');
if (parts.Length != 2)
{
throw new ArgumentException("Invalid CIDR notation");
}
if (!IPAddress.TryParse(parts[0], out IPAddress ipAddress))
{
throw new ArgumentException("Invalid IP address");
}
if (!int.TryParse(parts[1], out int subnetBits) || subnetBits < 0 || subnetBits > 32)
{
throw new ArgumentException("Invalid subnet mask");
}
uint ipCount = (uint)(1 << (32 - subnetBits));
byte[] ipBytes = ipAddress.GetAddressBytes();
uint startIP = (uint)((ipBytes[0] << 24) | (ipBytes[1] << 16) | (ipBytes[2] << 8) | ipBytes[3]);
for (uint i = 0; i < ipCount; i++)
{
uint currentIP = startIP + i;
byte[] currentIPBytes = new byte[4]
{
(byte)(currentIP >> 24),
(byte)(currentIP >> 16),
(byte)(currentIP >> 8),
(byte)currentIP
};
yield return new IPAddress(currentIPBytes);
}
}
private ScanResult SendArpRequest(IPAddress ipAddress)
{
byte[] macAddr = new byte[6];
uint macAddrLen = (uint)macAddr.Length;
try
{
// Convert IP address to bytes in the correct order
byte[] ipBytes = ipAddress.GetAddressBytes();
int ipInteger = BitConverter.ToInt32(ipBytes, 0);
int result = SendARP(ipInteger, 0, macAddr, ref macAddrLen);
if (result == 0)
{
// Create MAC address string
string macAddressString = BitConverter.ToString(macAddr, 0, (int)macAddrLen).Replace("-", ":");
// Create and return result immediately
var scanResult = new ScanResult
{
IpAddress = ipAddress.ToString(),
MacAddress = macAddressString,
Status = "Online",
OpenPorts = "N/A"
};
// Debug print to verify data
Console.WriteLine($"Found device - IP: {ipAddress}, MAC: {macAddressString}");
return scanResult;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error scanning {ipAddress}: {ex.Message}");
}
return null;
}
private void cmbTypeScan_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateScanDescription();
}
private void btnStopScan_Click(object sender, RoutedEventArgs e)
{
//TODO - implement logic to stop scan
btnStartScan.IsEnabled = true;
btnStopScan.IsEnabled = false;
progressGrid.Visibility = Visibility.Collapsed;
}
}
public class ScanResult : INotifyPropertyChanged
{
private string _ipAddress;
private string _macAddress;
private string _status;
private string _openPorts;
public string IpAddress
{
get => _ipAddress;
set
{
_ipAddress = value;
OnPropertyChanged(nameof(IpAddress));
}
}
public string MacAddress
{
get => _macAddress;
set
{
_macAddress = value;
OnPropertyChanged(nameof(MacAddress));
}
}
public string Status
{
get => _status;
set
{
_status = value;
OnPropertyChanged(nameof(Status));
}
}
public string OpenPorts
{
get => _openPorts;
set
{
_openPorts = value;
OnPropertyChanged(nameof(OpenPorts));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
还有扫描仪的前端:
<Page x:Class="NetVision.Pages.NetworkTools.NetworkScan"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:NetVision.Pages.NetworkTools"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="1000"
Title="Network Scan"
Loaded="Page_Loaded">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Subnet Input -->
<Grid Grid.Row="0" Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Subnet:" Grid.Column="0" VerticalAlignment="Center" Foreground="White" Margin="0,0,10,0"/>
<TextBox x:Name="txtSubnet" Grid.Column="1" Margin="0,0,10,0" KeyDown="txtSubnet_KeyDown"/>
<Button x:Name="btnAddSubnet" Grid.Column="2" Content="Add Subnet" Style="{StaticResource BaseButtonStyle}" Click="btnAddSubnet_Click"/>
<!-- Error Message -->
<TextBlock x:Name="txtErrorMessage" Grid.Column="3" Foreground="Red" VerticalAlignment="Center" Margin="10,0,0,0" TextWrapping="Wrap"/>
</Grid>
<!-- Scan Controls -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
<Button x:Name="btnStartScan" Content="Start Scan" Style="{StaticResource BaseButtonStyle}" Click="btnStartScan_ClickAsync" Margin="0,0,10,0"/>
<Button x:Name="btnStopScan" Content="Stop Scan" Style="{StaticResource BaseButtonStyle}" Click="btnStopScan_Click" IsEnabled="False"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="20,0,0,0">
<TextBlock Text="Scan Type:" Foreground="White" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox x:Name="cmbTypeScan" Width="120" SelectedIndex="0" SelectionChanged="cmbTypeScan_SelectionChanged">
<ComboBoxItem Content="ARP Scan"/>
<ComboBoxItem Content="TCP/UDP Scan"/>
<ComboBoxItem Content="TCP Scan"/>
<ComboBoxItem Content="UDP Scan"/>
<ComboBoxItem Content="PING Scan"/>
</ComboBox>
</StackPanel>
</StackPanel>
<!-- Scan Description -->
<TextBlock Grid.Row="3" x:Name="txtScanDescription" Foreground="White" VerticalAlignment="Top" Margin="0,0,0,10" TextWrapping="Wrap"/>
<!-- Scan Results -->
<Grid Grid.Row="3" Margin="0,30,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Subnet List -->
<Border Grid.Column="0" Style="{StaticResource CardStyle}" Margin="0,0,10,0">
<StackPanel>
<TextBlock Text="Subnets to Scan" Style="{StaticResource CardTitleStyle}"/>
<ListBox x:Name="lstSubnets" Background="Transparent" Foreground="White">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="❌"
Background="Transparent" BorderThickness="0"
Foreground="Red" Padding="5,0"
Click="btnDeleteSubnet_Click"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
<!-- Scan Results Table -->
<Border Grid.Column="1" Style="{StaticResource CardStyle}" Margin="0,0,0,0">
<StackPanel>
<TextBlock Text="Scan Results" Style="{StaticResource CardTitleStyle}"/>
<Grid x:Name="progressGrid" Visibility="Collapsed" Margin="0,0,0,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ProgressBar x:Name="scanProgressBar" Height="20" Value="0"/>
<TextBlock x:Name="progressText" Grid.Row="1" Foreground="White"
HorizontalAlignment="Center" Margin="0,5,0,0"
Text="Scanning: 0/0 hosts"/>
</Grid>
<DataGrid x:Name="dgScanResults"
AutoGenerateColumns="False"
IsReadOnly="True"
Background="Transparent"
BorderThickness="0"
Foreground="White">
<DataGrid.Columns>
<DataGridTextColumn Header="IP Address"
Binding="{Binding IpAddress}"
Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="MAC Address"
Binding="{Binding MacAddress}"
Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Status"
Binding="{Binding Status}"
Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Open Ports/Protocols"
Binding="{Binding OpenPorts}"
Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Border>
</Grid>
</Grid>
</Page>
这是一个视觉问题,您的文本在白色背景上是白色的您的数据在那里,但您看不到它......尝试更改背景将修复它
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Background" Value="#333"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</DataGridTextColumn.ElementStyle>