306 lines
9.9 KiB
C#
306 lines
9.9 KiB
C#
using CommandLine;
|
|
using System;
|
|
using System.CodeDom;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
namespace SharpScan
|
|
{
|
|
|
|
|
|
class Program
|
|
{
|
|
public class Options
|
|
{
|
|
[Option('h', "hosts", Required = true, Separator = ',', HelpText = "Hosts to scan, coma separated. CIDR accepted")]
|
|
public IEnumerable<string> Hosts { get; set; }
|
|
|
|
[Option('p', "ports", Required = false, Separator = ',', HelpText = "TCP port to scan, coma separated")]
|
|
public IEnumerable<int> Ports { get; set; }
|
|
|
|
[Option('t', "timeout", Required = false, Default = 500, HelpText = "Timeout in milliseconds to check a port")]
|
|
public int Timeout { get; set; }
|
|
|
|
[Option('d', "delay", Required = false, Default = 0, HelpText = "Delay in milliseconds between 2 scan request")]
|
|
public int Delay { get; set; }
|
|
|
|
[Option('j', "jittler", Required = false, Default = 0, HelpText = "Jittler to apply to delay, in percent, new evalutation for each request")]
|
|
public int Jittler { get; set; }
|
|
|
|
[Option('r', "randomize", Required = false, Default = false, HelpText = "Randomize hosts/ports scan order. Results will be printed in normal order")]
|
|
public bool Randomize { get; set; }
|
|
|
|
[Option('f', "top-ports", Required = false, Default = 0, HelpText = "Equivalent of nmap --top-ports option")]
|
|
public int TopPorts { get; set; }
|
|
}
|
|
|
|
|
|
public static List<string> GetIpsFromCidr(string cidr)
|
|
{
|
|
var ipList = new List<string>();
|
|
|
|
// Séparer l'adresse IP et le masque
|
|
var parts = cidr.Split('/');
|
|
var ipAddress = parts[0];
|
|
var subnetMask = int.Parse(parts[1]);
|
|
|
|
// Convertir l'adresse IP en un format numérique
|
|
var ip = IPAddress.Parse(ipAddress);
|
|
var ipBytes = ip.GetAddressBytes();
|
|
uint ipNumeric = BitConverter.ToUInt32(ipBytes.Reverse().ToArray(), 0);
|
|
|
|
// Calculer le nombre total d'adresses IP dans le réseau
|
|
uint totalAddresses = (uint)Math.Pow(2, 32 - subnetMask);
|
|
|
|
// Calculer l'adresse de broadcast en ajoutant le nombre total d'adresses moins un
|
|
uint broadcastIpNumeric = ipNumeric + totalAddresses - 1;
|
|
|
|
// Générer les adresses IP en excluant l'adresse du réseau et de broadcast
|
|
for (uint i = 1; i < totalAddresses - 1; i++)
|
|
{
|
|
uint currentIp = ipNumeric + i;
|
|
byte[] bytes = BitConverter.GetBytes(currentIp);
|
|
Array.Reverse(bytes); // Revertir l'ordre des octets pour obtenir une IP valide
|
|
ipList.Add(new IPAddress(bytes).ToString());
|
|
}
|
|
|
|
return ipList;
|
|
}
|
|
|
|
public static void Shuffle<T>(List<T> list)
|
|
{
|
|
Random rand = new Random();
|
|
int n = list.Count;
|
|
while (n > 1)
|
|
{
|
|
n--;
|
|
int k = rand.Next(n + 1); // Choisir un index aléatoire entre 0 et n
|
|
T value = list[k];
|
|
list[k] = list[n];
|
|
list[n] = value;
|
|
}
|
|
}
|
|
|
|
|
|
public static bool IsPortOpen(string hostname, int port, int timeoutMilliseconds)
|
|
{
|
|
using (var client = new TcpClient(AddressFamily.InterNetwork))
|
|
{
|
|
var connectDone = new ManualResetEvent(false);
|
|
bool connected = false;
|
|
Exception connectException = null;
|
|
|
|
Thread connectThread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
client.Connect(hostname, port);
|
|
connected = true;
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
connectException = ex;
|
|
connected = false;
|
|
}
|
|
finally
|
|
{
|
|
connectDone.Set();
|
|
}
|
|
});
|
|
|
|
connectThread.Start();
|
|
|
|
if (!connectDone.WaitOne(timeoutMilliseconds))
|
|
{
|
|
// Timeout
|
|
connectThread.Abort(); // On tente d'interrompre le thread de connexion.
|
|
return false;
|
|
}
|
|
|
|
if (connectException != null)
|
|
{
|
|
// La connexion a échoué.
|
|
return false;
|
|
}
|
|
|
|
return connected;
|
|
}
|
|
}
|
|
|
|
static void RunOptions(Options o)
|
|
{
|
|
List<(string address, int port)> cibles = new List<(string address, int port)>();
|
|
List<string> addresses = new List<string>();
|
|
List<(string address, int port)> results = new List<(string address, int port)>();
|
|
|
|
List<int> ports = new List<int>();
|
|
int timeout = 0;
|
|
bool randomize = false;
|
|
int delay = 0;
|
|
int jittler = 0;
|
|
int top_port = 0;
|
|
|
|
// Gestion des hosts
|
|
foreach (string host in o.Hosts)
|
|
{
|
|
if (host.Contains('/'))
|
|
{
|
|
addresses.AddRange(GetIpsFromCidr(host));
|
|
}
|
|
else
|
|
{
|
|
addresses.Add(host);
|
|
}
|
|
}
|
|
|
|
|
|
// Gestion des ports
|
|
if (o.Ports.Count() > 0)
|
|
{
|
|
if (o.Ports.Count() == 1 && o.Ports.First() == -1)
|
|
{
|
|
foreach (int port in Enumerable.Range(0, 65536))
|
|
{
|
|
ports.Add(port);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (int port in o.Ports)
|
|
{
|
|
ports.Add(port);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (o.TopPorts > 0)
|
|
{
|
|
if (o.TopPorts <= Utils.top_ports_list.Count())
|
|
{
|
|
ports = Utils.top_ports_list.GetRange(0, o.TopPorts);
|
|
}
|
|
else
|
|
{
|
|
ports = Utils.top_ports_list;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ports = Utils.top_ports_list.GetRange(0, 12);
|
|
}
|
|
}
|
|
|
|
|
|
timeout = o.Timeout;
|
|
randomize = o.Randomize;
|
|
delay = o.Delay;
|
|
jittler = o.Jittler;
|
|
top_port = o.TopPorts;
|
|
|
|
// Construction des cibles
|
|
foreach (string address in addresses)
|
|
{
|
|
foreach (int port in ports)
|
|
{
|
|
cibles.Add((address, port));
|
|
}
|
|
}
|
|
|
|
if (randomize)
|
|
{
|
|
Shuffle(cibles);
|
|
}
|
|
|
|
// Check wich ports are available
|
|
Random r;
|
|
int sleep_value;
|
|
Console.WriteLine($"There is {cibles.Count} requests to send !!!");
|
|
|
|
int cpt = 1;
|
|
Stopwatch chronometre = new Stopwatch();
|
|
chronometre.Start();
|
|
|
|
|
|
int fraction = cibles.Count / 10;
|
|
int cpt_fraction = 0;
|
|
|
|
// Estimation de la durée du scan
|
|
int total = 0;
|
|
total += ((delay + (jittler * delay / 100)) * cibles.Count) / 1000;
|
|
total += (timeout * cibles.Count) / 1000;
|
|
total += (2 * 100) / total; // On rajoute un chouilla pour le temps de traitement.
|
|
TimeSpan temps = TimeSpan.FromSeconds(total);
|
|
string total_formatee = temps.ToString(@"hh\:mm\:ss");
|
|
Console.WriteLine($"Duree estimée: {total_formatee}");
|
|
|
|
|
|
foreach ((string address, int port) in cibles)
|
|
{
|
|
if (cpt % fraction == 0)
|
|
{
|
|
cpt_fraction += 10;
|
|
Console.WriteLine($"Request {cpt}/{cibles.Count} ({cpt_fraction})%");
|
|
}
|
|
|
|
if (IsPortOpen(address, port, timeout))
|
|
{
|
|
results.Add((address, port));
|
|
}
|
|
|
|
// Sleep delay value
|
|
r = new Random();
|
|
sleep_value = r.Next(delay, delay + (jittler * delay / 100));
|
|
|
|
|
|
Thread.Sleep(sleep_value);
|
|
cpt += 1;
|
|
}
|
|
|
|
chronometre.Stop();
|
|
TimeSpan duree = chronometre.Elapsed;
|
|
string dureeFormatee = duree.ToString(@"hh\:mm\:ss"); // Format : heures:minutes
|
|
|
|
Console.WriteLine($"Durée (heures:minutes) : {dureeFormatee}");
|
|
|
|
|
|
// Affichage des résultats
|
|
results = results.OrderBy(tuple => IPAddress.Parse(tuple.address).GetAddressBytes().Select(b => (int)b).Aggregate(0, (acc, b) => acc * 256 + b)).ThenBy(tuple => tuple.port).ToList();
|
|
|
|
string current_host = "";
|
|
Console.WriteLine("\n ===[ SCAN RESULTS ]===");
|
|
foreach ((string address, int port) in results)
|
|
{
|
|
if (!(address == current_host))
|
|
{
|
|
Console.WriteLine($"\n{address}");
|
|
current_host = address;
|
|
}
|
|
Console.WriteLine($" {port} opened ({Utils.GetDesc(port)})");
|
|
}
|
|
|
|
}
|
|
static void HandleParseError(IEnumerable<Error> errs)
|
|
{
|
|
//handle errors
|
|
}
|
|
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
CommandLine.Parser.Default.ParseArguments<Options>(args)
|
|
.WithParsed(RunOptions)
|
|
.WithNotParsed(HandleParseError);
|
|
}
|
|
}
|
|
}
|
|
|