前言:这里只给出命令行版的扫描工具,后续可能是写一个独立的界面,或者是集成到其他工具上去。
一 扫描原理
其实原理非常简单,就是使用Socket去连接目标IP或者域名的指定端口,如果能够连上则说明该端口是打开的。反之,要是在连接超时之前都没有连上,则将该端口判断为关闭状态。下面我将分别说明两种基本的扫描方式:(1)扫描一个连续的端口段;(2)仅扫描一个指定的端口集合
二 使用多线程扫描目标主机一个段的端口开放情况
/** * 多线程扫描目标主机一个段的端口开放情况 * * @param ip * 待扫描IP或域名,eg:180.97.161.184 www.zifangsky.cn * @param startPort * 起始端口 * @param endPort * 结束端口 * @param threadNumber * 线程数 * @param timeout * 连接超时时间 * */ public void scanLargePorts(String ip, int startPort, int endPort, int threadNumber, int timeout) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < threadNumber; i++) { ScanMethod1 scanMethod1 = new ScanMethod1(ip, startPort, endPort, threadNumber, i, timeout); threadPool.execute(scanMethod1); } threadPool.shutdown(); // 每秒中查看一次是否已经扫描结束 while (true) { if (threadPool.isTerminated()) { System.out.println("扫描结束"); break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
然后是一个内部类ScanMethod1实现了Runnable接口:
/** * 扫描方式一:针对起始结束端口,进行逐个扫描 * * */ class ScanMethod1 implements Runnable { private String ip; // 目标IP private int startPort, endPort, threadNumber, serial, timeout; // 起始和结束端口,线程数,这是第几个线程,超时时间 /** * 初始化 * * @param ip * 待扫描IP或域名 * @param startPort * 起始端口 * @param endPort * 结束端口 * @param threadNumber * 线程数 * @param serial * 标记是第几个线程 * @param timeout * 连接超时时间 * */ public ScanMethod1(String ip, int startPort, int endPort, int threadNumber, int serial, int timeout) { this.ip = ip; this.startPort = startPort; this.endPort = endPort; this.threadNumber = threadNumber; this.serial = serial; this.timeout = timeout; } public void run() { int port = 0; try { InetAddress address = InetAddress.getByName(ip); Socket socket; SocketAddress socketAddress; for (port = startPort + serial; port <= endPort; port += threadNumber) { socket = new Socket(); socketAddress = new InetSocketAddress(address, port); try { socket.connect(socketAddress, timeout); // 超时时间 socket.close(); System.out.println("端口 " + port + " :开放"); } catch (IOException e) { // System.out.println("端口 " + port + " :关闭"); } } } catch (UnknownHostException e) { e.printStackTrace(); } } }
三 使用多线程扫描目标主机指定Set端口集合的开放情况
/** * 多线程扫描目标主机指定Set端口集合的开放情况 * * @param ip * 待扫描IP或域名,eg:180.97.161.184 www.zifangsky.cn * @param portSet * 待扫描的端口的Set集合 * @param threadNumber * 线程数 * @param timeout * 连接超时时间 * */ public void scanLargePorts(String ip, Set<Integer> portSet, int threadNumber, int timeout) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < threadNumber; i++) { ScanMethod2 scanMethod2 = new ScanMethod2(ip, portSet, threadNumber, i, timeout); threadPool.execute(scanMethod2); } threadPool.shutdown(); while (true) { if (threadPool.isTerminated()) { System.out.println("扫描结束"); break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
具体的线程内部类跟上面类似,代码如下:
/** * 扫描方式二:针对一个待扫描的端口的Set集合进行扫描 * * */ private class ScanMethod2 implements Runnable { private String ip; // 目标IP private Set<Integer> portSet; // 待扫描的端口的Set集合 private int threadNumber, serial, timeout; // 线程数,这是第几个线程,超时时间 public ScanMethod2(String ip, Set<Integer> portSet, int threadNumber, int serial, int timeout) { this.ip = ip; this.portSet = portSet; this.threadNumber = threadNumber; this.serial = serial; this.timeout = timeout; } public void run() { int port = 0; Integer[] ports = portSet.toArray(new Integer[portSet.size()]); // Set转数组 try { InetAddress address = InetAddress.getByName(ip); Socket socket; SocketAddress socketAddress; if (ports.length < 1) return; for (port = 0 + serial; port <= ports.length - 1; port += threadNumber) { socket = new Socket(); socketAddress = new InetSocketAddress(address, ports[port]); try { socket.connect(socketAddress, timeout); socket.close(); System.out.println("端口 " + ports[port] + " :开放"); } catch (IOException e) { // System.out.println("端口 " + ports[port] + " :关闭"); } } } catch (UnknownHostException e) { e.printStackTrace(); } } }
四 两种扫描方式的测试用例
public static void main(String[] args) { PortScanDemo portScanDemo = new PortScanDemo(); //方式1 // portScanDemo.scanLargePorts("ultra-book.co", 20, 10000, 5,800); // portScanDemo.scanLargePorts("180.97.161.184", 1, 100, 5); //方式2 Set<Integer> portSet = new LinkedHashSet<Integer>(); Integer[] ports = new Integer[] { 21, 22, 23, 25, 26, 69, 80, 110, 143, 443, 465, 995, 1080, 1158, 1433, 1521, 2100, 3128, 3306, 3389, 7001, 8080, 8081, 9080, 9090,43958}; portSet.addAll(Arrays.asList(ports)); portScanDemo.scanLargePorts("ultra-book.co", portSet, 5, 800); }
五 测试结果
注:1 超时时间是以毫秒为单位,其中要是扫描国内的IP可以把这个时间适当设置低一点,200~500左右。相反,要是扫描国外IP就需要把这个时间适当设置大一点,不然有可能把本来打开的端口也漏掉了
2 完整测试文件下载链接:http://pan.baidu.com/s/1ntTPx7V
(PS:欢迎大家访问我的个人博客网站:http://www.zifangsky.cn)
本文出自 “zifangsky的个人博客” 博客,请务必保留此出处http://983836259.blog.51cto.com/7311475/1727023
原文:http://983836259.blog.51cto.com/7311475/1727023