使用Java扫描端口的最快方法

时间:2012-07-18 17:32:46

标签: java sockets ip port-scanning

我制作了非常简单的端口扫描程序,但运行速度太慢,所以我正在寻找方法让它扫描得更快。这是我的代码:

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?

7 个答案:

答案 0 :(得分:63)

如果65536端口中的每一个都需要200ms(在最坏的情况下,防火墙会阻止所有内容,从而使你的每个端口都达到超时),数学很简单:你需要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
}

然后,在第一个代码段中将Boolean更改为ScanResult,然后返回new ScanResult(port, true)new ScanResult(port, false),而不只是truefalse < / p>

编辑:实际上,我刚注意到:在这种特殊情况下,您不需要ScanResult类来保存结果+端口,并且仍然知道哪个端口是打开的。由于您将期货添加到列表已订购),稍后您按照添加它们的相同顺序处理它们,您可以拥有一个计数器,您可以在每次迭代时递增,以了解您正在处理的端口。但是,嘿,这只是完整而准确的。 不要尝试那样做,这太可怕了,我为此感到羞耻,我想到了这个...... 使用ScanResult对象更清晰,代码是方便阅读和维护,并允许您稍后使用CompletionService来改进扫描仪。

答案 1 :(得分:4)

除了并行化扫描之外,您还可以使用更高级的端口扫描技术,如此处所述的(TCP SYN和TCP FIN扫描):http://nmap.org/nmap_doc.html。可以在此处找到实现的VB代码:http://h.ackack.net/spoon-worlds-fastest-port-scanner.html

但是,要使用这些技术,您需要使用原始TCP / IP套接字。您应该使用RockSaw库。

答案 2 :(得分:3)

代码示例的灵感来自“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 :(得分:3)

对于Android,请尝试使用此Awesome Library

https://github.com/stealthcopter/AndroidNetworkTools

答案 4 :(得分:2)

如果您决定使用Nmap选项并希望继续使用Java,则应该在SourceForge.net(http://nmap4j.sourceforge.net)上查看Nmap4j。这是一个简单的API,允许您将Nmap集成到Java应用程序中。

- 乔恩

答案 5 :(得分:1)

我编写了自己的异步portscanner java服务,可以像Nmap一样通过TCP-SYN-Scan扫描端口。它还支持IMCP ping扫描,并且可以以非常高的吞吐量工作(取决于网络可以支持的内容):

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

在内部,它使用java绑定pcap并通过JMS / AMQP公开其服务。虽然您也可以直接在应用程序中使用它,但如果您不介意它具有root权限。

答案 6 :(得分:0)

不是,这里最快的方法是使用动态创建的线程方法

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)");
 es.shutdown();
    }



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

    }
    }