J'ai fait un scanner de port très simple, mais il fonctionne trop lentement, donc je cherche un moyen de le faire scanner plus rapidement. Voici mon code:
public boolean portIsOpen(String ip, int port, int timeout) {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return true;
} catch (Exception ex) {
return false;
}
}
Ce code teste si un port spécifique est ouvert sur une adresse IP spécifique. Pour le délai d'attente, j'ai utilisé une valeur minimale de 200
parce que quand je descends, je n'ai pas assez de temps pour tester le port.
Cela fonctionne bien, mais prend trop de temps pour numériser de 0 à 65535. Existe-t-il un autre moyen qui pourrait peut-être numériser de 0 à 65535 en moins de 5 minutes?
Si vous avez besoin de 200 ms pour chacun des 65536 ports (dans le pire des cas, un pare-feu bloque tout, ce qui vous fait atteindre votre délai d'expiration pour chaque port), le calcul est assez simple: vous avez besoin de 13 000 secondes, soit environ 3 heures et une moitié.
Vous avez 2 options (non exclusives) pour le rendre plus rapide:
Étant donné que l'opération est liée aux E/S (contrairement à l'UC - c'est-à-dire que vous passez du temps à attendre les E/S et non à effectuer d'énormes calculs), vous pouvez utiliser de nombreux threads. Essayez de commencer par 20. Ils diviseraient les 3 heures et demie entre eux, donc le temps maximum prévu est d'environ 10 minutes. N'oubliez pas que cela mettra la pression de l'autre côté, c'est-à-dire que l'hôte analysé verra une énorme activité réseau avec des motifs "déraisonnables" ou "étranges", ce qui rend l'analyse extrêmement facile à détecter.
Le moyen le plus simple (c'est-à-dire avec des modifications minimes) consiste à utiliser les API ExecutorService et Future:
public static Future<Boolean> portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) {
return es.submit(new Callable<Boolean>() {
@Override public Boolean call() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return true;
} catch (Exception ex) {
return false;
}
}
});
}
Ensuite, vous pouvez faire quelque chose comme:
public static void main(final String... args) {
final ExecutorService es = Executors.newFixedThreadPool(20);
final String ip = "127.0.0.1";
final int timeout = 200;
final List<Future<Boolean>> futures = new ArrayList<>();
for (int port = 1; port <= 65535; port++) {
futures.add(portIsOpen(es, ip, port, timeout));
}
es.shutdown();
int openPorts = 0;
for (final Future<Boolean> f : futures) {
if (f.get()) {
openPorts++;
}
}
System.out.println("There are " + openPorts + " open ports on Host " + ip + " (probed with a timeout of " + timeout + "ms)");
}
Si vous avez besoin de savoir quels ports sont ouverts (et pas seulement combien , comme dans l'exemple ci-dessus), vous devez modifier le type de retour de la fonction en Future<SomethingElse>
, où SomethingElse
contiendrait le port et le résultat de l'analyse, quelque chose comme:
public final class ScanResult {
private final int port;
private final boolean isOpen;
// constructor
// getters
}
Ensuite, remplacez Boolean
par ScanResult
dans le premier extrait, et retournez new ScanResult(port, true)
ou new ScanResult(port, false)
au lieu de simplement true
ou false
EDIT: En fait, je viens de remarquer: dans ce cas particulier, vous n'avez pas besoin de la classe ScanResult pour contenir result + port, et vous savez toujours quel port est ouvert. Puisque vous ajoutez les futures à un List, qui est ordonné, et, plus tard, vous traitez-les dans le même ordre que vous les avez ajoutés, vous pourriez avoir un compteur que vous incrémenteriez à chaque itération pour savoir à quel port vous avez affaire. Mais, hé, c'est juste pour être complet et précis. N'essayez jamais de faire ça, c'est horrible, j'ai surtout honte d'y avoir pensé ... L'utilisation de l'objet ScanResult est beaucoup plus propre, le code est plus facile à lire et à entretenir, et vous permet, plus tard, par exemple, d'utiliser un CompletionService
pour améliorer le scanner.
Outre la parallélisation du scan, vous pouvez utiliser des techniques de scan de port plus avancées comme celles (TCP SYN et TCP FIN scan) expliquées ici: http://nmap.org/nmap_doc .html . VB d'une implémentation peut être trouvé ici: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html
Cependant, pour utiliser ces techniques, vous devez utiliser des sockets TCP/IP bruts. Vous devez utiliser la bibliothèque RockSaw pour cela.
L'exemple de code est inspiré de "Bruno Reis"
class PortScanner {
public static void main(final String... args) throws InterruptedException, ExecutionException {
final ExecutorService es = Executors.newFixedThreadPool(20);
final String ip = "127.0.0.1";
final int timeout = 200;
final List<Future<ScanResult>> futures = new ArrayList<>();
for (int port = 1; port <= 65535; port++) {
// for (int port = 1; port <= 80; port++) {
futures.add(portIsOpen(es, ip, port, timeout));
}
es.awaitTermination(200L, TimeUnit.MILLISECONDS);
int openPorts = 0;
for (final Future<ScanResult> f : futures) {
if (f.get().isOpen()) {
openPorts++;
System.out.println(f.get().getPort());
}
}
System.out.println("There are " + openPorts + " open ports on Host " + ip + " (probed with a timeout of "
+ timeout + "ms)");
}
public static Future<ScanResult> portIsOpen(final ExecutorService es, final String ip, final int port,
final int timeout) {
return es.submit(new Callable<ScanResult>() {
@Override
public ScanResult call() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return new ScanResult(port, true);
} catch (Exception ex) {
return new ScanResult(port, false);
}
}
});
}
public static class ScanResult {
private int port;
private boolean isOpen;
public ScanResult(int port, boolean isOpen) {
super();
this.port = port;
this.isOpen = isOpen;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public boolean isOpen() {
return isOpen;
}
public void setOpen(boolean isOpen) {
this.isOpen = isOpen;
}
}
}
Si vous décidez d'utiliser l'option Nmap et que vous souhaitez continuer avec Java, vous devriez regarder Nmap4j sur SourceForge.net .
C'est une API simple qui vous permet d'intégrer Nmap dans une application Java.
J'ai écrit mon propre analyseur de port asynchrone Java service qui peut analyser les ports via TCP-SYN-Scan comme Nmap le fait. Il prend également en charge les analyses ping IMCP et peut fonctionner avec un débit très élevé (selon ce que le réseau peut supporter):
https://github.com/subes/invesdwin-webproxy
En interne, il utilise un Java pcap de liaison et expose ses services via JMS/AMQP. Bien que vous puissiez également l'utiliser directement dans votre application si cela ne vous dérange pas d'avoir des autorisations root.
Non, le moyen le plus rapide ici est d'utiliser la méthode de thread créée dynamiquement
Executors.newCachedThreadPool();
De cette façon, il utilise des threads jusqu'à ce qu'ils soient tous pris, puis lorsque tous sont pris et qu'il y a une nouvelle tâche, il ouvrira un nouveau thread et exécutera la nouvelle tâche dessus.
Voici mon extrait de code (Creds dû à Jack et Bruno Reis)
J'ai également ajouté la fonction pour rechercher n'importe quelle adresse IP que vous tapez pour une fonctionnalité supplémentaire et une facilité d'utilisation.
import Java.net.InetSocketAddress;
import Java.net.Socket;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.Scanner;
import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import Java.util.concurrent.TimeUnit;
class PortScanner {
public static void main(final String... args) throws InterruptedException, ExecutionException
{
final ExecutorService es = Executors.newCachedThreadPool();
System.out.print("Please input the ip address you would like to scan for open ports: ");
Scanner inputScanner = new Scanner(System.in);
final String ip = inputScanner.nextLine();
final int timeout = 200;
final List<Future<ScanResult>> futures = new ArrayList<>();
for (int port = 1; port <= 65535; port++) {
// for (int port = 1; port <= 80; port++) {
futures.add(portIsOpen(es, ip, port, timeout));
}
es.awaitTermination(200L, TimeUnit.MILLISECONDS);
int openPorts = 0;
for (final Future<ScanResult> f : futures) {
if (f.get().isOpen()) {
openPorts++;
System.out.println(f.get().getPort());
}
}
System.out.println("There are " + openPorts + " open ports on Host " + ip + " (probed with a timeout of "
+ timeout + "ms)");
}
public static Future<ScanResult> portIsOpen(final ExecutorService es, final String ip, final int port,
final int timeout)
{
return es.submit(new Callable<ScanResult>() {
@Override
public ScanResult call() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return new ScanResult(port, true);
} catch (Exception ex) {
return new ScanResult(port, false);
}
}
});
}
public static class ScanResult {
private int port;
private boolean isOpen;
public ScanResult(int port, boolean isOpen) {
super();
this.port = port;
this.isOpen = isOpen;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public boolean isOpen() {
return isOpen;
}
public void setOpen(boolean isOpen) {
this.isOpen = isOpen;
}
}
}