使用Java开发多线程端口扫描工具

  1. 云栖社区>
  2. 博客>
  3. 正文

使用Java开发多线程端口扫描工具

技术小阿哥 2017-11-27 14:59:00 浏览846
展开阅读全文

一 扫描原理

其实原理非常简单,就是使用Socket去连接目标IP或者域名的指定端口,如果能够连上则说明该端口是打开的。反之,要是在连接超时之前都没有连上,则将该端口判断为关闭状态。下面我将分别说明两种基本的扫描方式:(1)扫描一个连续的端口段;(2)仅扫描一个指定的端口集合

二 使用多线程扫描目标主机一个段的端口开放情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
     * 多线程扫描目标主机一个段的端口开放情况
     
     * @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接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
     * 扫描方式一:针对起始结束端口,进行逐个扫描
     
     * */
    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端口集合的开放情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
     * 多线程扫描目标主机指定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();
            }
        }
    }

具体的线程内部类跟上面类似,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
     * 扫描方式二:针对一个待扫描的端口的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();
            }
 
        }
 
    }

四 两种扫描方式的测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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[] { 21222325266980110143,
                44346599510801158143315212100312833063389,
                70018080808190809090,43958};
        portSet.addAll(Arrays.asList(ports));
        portScanDemo.scanLargePorts("ultra-book.co", portSet, 5800);
 
    }

五 测试结果

wKioL1Z4oyXB_3cYAAFDqJgRSFQ285.png

wKioL1Z4oyWxTyktAABNvxk7sL4968.png

注:1 超时时间是以毫秒为单位,其中要是扫描国内的IP可以把这个时间适当设置低一点,200~500左右。相反,要是扫描国外IP就需要把这个时间适当设置大一点,不然有可能把本来打开的端口也漏掉了



本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1727023,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
技术小阿哥
+ 关注