[英]Fastest way to scan ports with 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
,因为当我降低 go 时,它没有足够的时间来测试端口。
它运行良好,但是从 0 扫描到 65535 需要太多时间。有没有其他方法可以在不到 5 分钟的时间内从 0 扫描到 65535?
如果 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
}
然后,在第一个片段ScanResult
Boolean
更改为ScanResult
,并返回new ScanResult(port, true)
或new ScanResult(port, false)
而不是true
或false
编辑:实际上,我只是注意到:在这种特殊情况下,您不需要 ScanResult 类来保存结果 + 端口,并且仍然知道哪个端口是打开的。 由于您将期货添加到有序的List 中,并且稍后按照添加它们的相同顺序处理它们,因此您可以拥有一个计数器,您可以在每次迭代时递增该计数器以了解您正在处理的端口. 但是,嘿,这只是为了完整和精确。 永远不要尝试这样做,这太可怕了,我对想到这一点感到非常惭愧......使用 ScanResult 对象更干净,代码更易于阅读和维护,并且允许您以后,例如,使用CompletionService
来改进扫描器。
除了并行扫描之外,您还可以使用更高级的端口扫描技术,例如此处解释的(TCP SYN 和 TCP FIN 扫描): http : //nmap.org/nmap_doc.html 。 可以在此处找到实现的 VB 代码: http : //h.ackack.net/spoon-worlds-fastest-port-scanner.html
但是,为了使用这些技术,您需要使用原始 TCP/IP 套接字。 您应该为此使用RockSaw库。
代码示例的灵感来自“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;
}
}
}
如果您决定使用 Nmap 选项并想继续使用 Java,您应该查看SourceForge.net上的Nmap4j 。
这是一个简单的 API,允许您将 Nmap 集成到 Java 应用程序中。
我编写了自己的异步 portscanner java 服务,它可以像 Nmap 一样通过 TCP-SYN-Scan 扫描端口。 它还支持 IMCP ping 扫描并且可以以非常高的吞吐量工作(取决于网络可以承受的情况):
https://github.com/subes/invesdwin-webproxy
它在内部使用 java 绑定 pcap 并通过 JMS/AMQP 公开其服务。 如果您不介意它具有 root 权限,您也可以直接在您的应用程序中使用它。
不,这里最快的方法是使用动态创建的线程方法
Executors.newCachedThreadPool();
通过这种方式,它使用线程直到所有线程都被占用,然后当所有线程都被占用并且有一个新任务时,它将打开一个新线程并在其上执行新任务。
这是我的代码片段(归功于 Jack 和 Bruno Reis)
我还添加了搜索您输入的任何 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;
}
}
}
我可能迟到了,但是您可以通过使用 NIO2 单线程执行以下操作来进行批量端口扫描。 通过使用单个线程跟踪 NIO2 代码,我能够扫描给定端口的所有主机。 请尝试合理的超时时间,并确保您有大的文件描述符进行处理
public static List<HostTarget> getRechabilityStatus(String...hosts,final int port, final int bulkDevicesPingTimeoutinMS) throws Exception {
List<AsynchronousSocketChannel> channels = new ArrayList<>(hosts.length);
try {
List<CompletableFuture<HostTarget>> all = new ArrayList<>(hosts.length);
List<HostTarget> allHosts = new ArrayList(hosts.length);
for (String host : hosts) {
InetSocketAddress address = new InetSocketAddress(host, port);
HostTarget target = new HostTarget();
target.setIpAddress(host);
allHosts.add(target);
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
channels.add(client);
final CompletableFuture<HostTarget> targetFuture = new CompletableFuture<>();
all.add(targetFuture);
client.connect(address, target, new CompletionHandler<Void, HostTarget>() {
@Override
public void completed(Void result, HostTarget attachment) {
attachment.setIsReachable(true);
targetFuture.complete(attachment);
}
@Override
public void failed(Throwable exc, HostTarget attachment) {
attachment.setIsReachable(false);
attachment.errrorMessage = exc.getMessage();
targetFuture.complete(attachment);
}
});
}
try {
if(bulkDevicesPingTimeoutinMS > 0) {
CompletableFuture.allOf(all.toArray(new CompletableFuture[]{})).get(bulkDevicesPingTimeoutinMS, TimeUnit.MILLISECONDS);
}else{
// wait for all future to be complete 1000 scan is taking 7 seconds.
CompletableFuture.allOf(all.toArray(new CompletableFuture[]{})).join();
}
} catch (Exception timeoutException) {
// ignore
}
return allHosts;
}finally {
for(AsynchronousSocketChannel channel : channels){
try{
channel.close();
}catch (Exception e){
if(LOGGER.isDebugEnabled()) {
LOGGER.error("Erorr while closing socket",e);
}
}
}
}
static class HostTarget {
String ipAddress;
Boolean isReachable;
public String getIpAddress() {
return ipAddress;
}
public Boolean getIsReachable() {
return isReachable;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public void setIsReachable(Boolean isReachable) {
this.isReachable = isReachable;
}
}
受到你们所有人的启发,但仅此代码确实有效!
class 端口扫描器
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
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;
public class PortScaner {
public static void main(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++)
futures.add(portIsOpen(es, ip, port, timeout));
es.shutdown();
int openPorts = 0;
for (final Future<ScanResult> f : futures)
if (f.get().isOpen()) {
openPorts++;
System.out.println(f.get());
}
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);
}
}
});
}
}
class 扫描结果
public final class ScanResult {
private final int port;
private final boolean isOpen;
public ScanResult(int port, boolean isOpen) {
super();
this.port = port;
this.isOpen = isOpen;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* @return the isOpen
*/
public boolean isOpen() {
return isOpen;
}
@Override
public String toString() {
return "ScanResult [port=" + port + ", isOpen=" + isOpen + "]";
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.