Najszybszy sposób skanowania portów za pomocą Javy

Zrobiłem bardzo prosty skaner portów, ale działa zbyt wolno, więc szukam sposobu, aby przyspieszyć skanowanie. Oto Mój kod:

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;
        }
    }

Ten kod sprawdza, czy określony port jest otwarty na określonym ip. Dla timeout używam minimalnej wartości 200, ponieważ gdy idę niżej nie ma wystarczająco dużo czasu, aby przetestować port. Dobrze to działa dobrze, ale zajmuje zbyt dużo skanowania od 0 do 65535. Czy jest jakiś inny sposób, który mógłby przeskanować od 0 do 65535 w mniej niż 5 minut?

Author: Jørgen R, 2012-07-18

6 answers

Jeśli potrzebujesz 200ms dla każdego z 65536 portów (w najgorszym przypadku firewall blokuje wszystko, co sprawia, że osiągasz limit czasu dla każdego portu), matematyka jest dość prosta: potrzebujesz 13K sekund, czyli około 3 godzin i pół.

Masz 2 (niewyłączne) opcje, aby przyspieszyć:

  • zmniejsz limit czasu
  • paralelizuj swój kod

Ponieważ operacja jest związana We / Wy (w przeciwieństwie do CPU bound - czyli spędzasz czas czekając możesz użyć wielu, wielu wątków. Spróbuj zacząć od 20. Dzielili między siebie 3 i pół godziny, więc maksymalny oczekiwany czas to około 10 minut. Pamiętaj tylko, że spowoduje to presję po drugiej stronie, tj. skanowany host zobaczy ogromną aktywność sieciową z "nieuzasadnionymi" lub "dziwnymi" wzorcami, dzięki czemu skanowanie jest niezwykle łatwe do wykrycia.

Najprostszym sposobem (tj. z minimalnymi zmianami) jest użyj ExecutorService i przyszłych API:

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;
        }
      }
   });
}

Wtedy możesz zrobić coś w stylu:

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)");
}

Jeśli chcesz wiedzieć które porty są otwarte (a nie tylko ile, jak w powyższym przykładzie), musisz zmienić typ zwracanej funkcji na Future<SomethingElse>, Gdzie SomethingElse będzie trzymać port i wynik skanowania, coś w stylu:

public final class ScanResult {
  private final int port;
  private final boolean isOpen;
  // constructor
  // getters
}

Następnie zmień Boolean na ScanResult w pierwszym fragmencie i zwróć new ScanResult(port, true) lub new ScanResult(port, false) zamiast tylko true lub false

EDIT: właściwie, właśnie zauważyłem: w tym konkretnym przypadku, nie potrzebujesz klasy ScanResult do przechowywania result + port i nadal wiesz, który port jest otwarty. Ponieważ dodajesz kontrakty futures do listy , która jest uporządkowana, a później przetwarzasz je w tej samej kolejności, w jakiej je dodałeś , możesz mieć licznik, który zwiększasz przy każdej iteracji, aby wiedzieć, z którym portem masz do czynienia. Ale, hej, to ma być kompletne i precyzyjne. nie nigdy nie próbowałem tego robić , to straszne, wstydzę się głównie, że o tym pomyślałem... Korzystanie z obiektu ScanResult jest znacznie czystsze , kod jest znacznie łatwiejszy do odczytania i utrzymania, i pozwala później, na przykład, użyć CompletionService do ulepszenia skanera.

 61
Author: Bruno Reis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-18 22:02:17

Oprócz równoległego skanowania, możesz użyć bardziej zaawansowanych technik skanowania portów, takich jak te (TCP SYN i TCP FIN scanning) wyjaśnione tutaj: http://nmap.org/nmap_doc.html . Kod VB implementacji można znaleźć tutaj: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html

Aby jednak użyć tych technik, musisz użyć surowych gniazd TCP / IP. W tym celu należy użyć biblioteki RockSaw.

 4
Author: Hakan Serce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-18 17:49:40

Przykład kodu jest inspirowany przez "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;
    }

}
}
 3
Author: Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-20 13:51:06

Dla Androida wypróbuj tę niesamowitą bibliotekę

Https://github.com/stealthcopter/AndroidNetworkTools

 2
Author: Bala Vishnu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-21 00:29:36

Jeśli zdecydujesz się użyć opcji Nmap i chcesz kontynuować korzystanie z Javy, powinieneś spojrzeć na Nmap4j na SourceForge.net (http://nmap4j.sourceforge.net). jest to proste API, które pozwala na integrację Nmap z aplikacją java.

--Jon

 1
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 19:35:48

Napisałem własną asynchroniczną usługę portscanner java, która może skanować porty przez TCP-SYN-Scan, tak jak robi to Nmap. Obsługuje również skanowanie ping IMCP i może pracować z bardzo wysoką przepustowością (w zależności od tego, co sieć może utrzymać):

Https://github.com/subes/invesdwin-webproxy

Wewnętrznie używa wiązania java pcap i udostępnia swoje usługi poprzez JMS / AMQP. Chociaż możesz go również używać bezpośrednio w aplikacji, Jeśli nie masz nic przeciwko temu, że ma uprawnienia roota.

 1
Author: subes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-09 19:17:56