web-dev-qa-db-ja.com

Javaでポートをスキャンする最速の方法

非常に単純なポートスキャナーを作成しましたが、実行速度が遅すぎるため、より高速にスキャンする方法を探しています。ここに私のコードがあります:

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

このコードは、特定のポートが特定のIPで開いているかどうかをテストします。タイムアウトには、200の最小値を使用しました。これは、低くするとポートをテストするのに十分な時間がないためです。

うまく動作しますが、0〜65535でスキャンするには時間がかかります。5分未満で0〜65535をスキャンできる他の方法はありますか?

29
Rohit Malish

65536個のポートごとに200ミリ秒が必要な場合(最悪の場合、ファイアウォールがすべてをブロックしているため、すべてのポートでタイムアウトになります)、計算は非常に簡単です:13k秒、または約3時間必要です半分。

より高速にするための2つの(非排他的)オプションがあります。

  • タイムアウトを減らす
  • コードを並列化する

操作はI/Oバウンドであるため(CPUバウンドとは対照的に、つまり、巨大な計算が完了するのではなく、I/Oを待つことに時間を費やします)、多数のスレッドを使用できます。 20から始めてみてください。3時間半を分割しますしたがって、最大予想時間は約10分です。これは反対側に圧力をかけることを覚えておいてください。つまり、スキャンされたホストは「不合理な」または「奇妙な」パターンで巨大なネットワークアクティビティを見るため、スキャンを非常に簡単に検出できます。

最も簡単な方法(つまり、最小限の変更)は、ExecutorServiceおよびFuture 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;
        }
      }
   });
}
_

次に、次のようなことができます:

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

どのポートが開いているか(上記の例のようにいくつだけでなく)を知る必要がある場合、関数の戻り値の型を次のように変更する必要があります。 _Future<SomethingElse>_、ここでSomethingElseはポートとスキャンの結果を保持します:

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

次に、最初のスニペットでBooleanScanResultに変更し、trueまたはfalseの代わりにnew ScanResult(port, true)またはnew ScanResult(port, false)を返します。

編集:実際、私はちょうど気づいた:この特定のケースでは、結果とポートを保持するためにScanResultクラスを必要とせず、まだ開いているポートを知っています。先物をList、つまりorderedに追加し、後で追加したのと同じ順序で処理、繰り返しごとにインクリメントするカウンターを使用して、処理しているポートを知ることができます。しかし、ちょっと、これは完全で正確であることです。 これをやろうとしないでください、それは恐ろしいです、私はこれについて考えたことをほとんど恥ずかしく思います... ScanResultオブジェクトを使用する方がはるかにきれいです、コードは読みやすく保守しやすい方法で、たとえば、CompletionServiceを使用してスキャナーを改善できます。

64
Bruno Reis

スキャンの並列化とは別に、ここで説明されているもの(TCP SYNおよびTCP FINスキャン)などのより高度なポートスキャン技術を使用できます。 http://nmap.org/nmap_doc .html 。VB実装のコードはここにあります: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html

ただし、これらの手法を使用するには、生のTCP/IPソケットを使用する必要があります。これには RockSaw ライブラリを使用する必要があります。

4
Hakan Serce

コードサンプルは「ブルーノレイス」に触発されています

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
Jack

Nmapオプションを使用し、Javaを継続したい場合は、 Nmap4j on SourceForge.net を参照してください。

これは、Nmap=をJavaアプリに統合できるシンプルなAPIです。

2
Jon

私は独自の非同期portscannerを作成しましたJava TCP-SYN-Scanを介してポートをスキャンできるサービスはNmapのようになります。IMCPpingスキャンもサポートします。非常に高いスループット(ネットワークが維持できるものに応じて):

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

内部的には、Javaバインディングpcapを使用し、JMS/AMQPを介してサービスを公開します。ただし、root権限を持つことを気にしない場合は、アプリケーションで直接使用することもできます。

1
subes

いや、ここで最速の方法は、動的に作成されたスレッドメソッドを使用することです

Executors.newCachedThreadPool();

この方法では、すべてのスレッドが取得されるまでスレッドを使用し、すべてのスレッドが取得されて新しいタスクが発生すると、新しいスレッドを開いて新しいタスクを実行します。

これが私のコードスニペットです(ジャックとブルーノレイスによるレッド)

また、追加した機能と使いやすさのために、入力したIPアドレスを検索する機能を追加しました。

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

    }
    }
0
thatsmellything