多线程批量探测目标IP段的服务器类型(内网也可用)

简介:

一 原理解释

这里所说的服务器类型是指像Apache,tomcat,nginx,IIS这种。其中原理用到了HTTP Header的Responses,这里面有项叫“Server”的参数就包涵我们所需要的信息。下面是Responses的部分截图:

wKiom1ZszVDikU_EAABUcdoWcOw314.png

(PS:更多相关可自行百度“HTTP Header”)

因此,我们想要做一个多线程批量探测的软件,思路有两种:1)根据别人提供的接口然后我们去调用获取(比如:http://api.builtwith.com 这个我以后可能会写);(2)针对每个IP我们发送Get请求,然后去获取响应头文件中的Server参数

PS:文末我会放出打包好的有GUI界面的jar文件以及完整源代码

二 项目结构

这里我选择了第二种方式,自己动手做一个,虽然获取到的信息没有用接口获取的来的全。下面是整个完整小项目的目录结构:

wKioL1ZszYvSZ9IAAAAhxUJ3qCI708.png


三 核心代码

在这里核心代码在ServerTypeDemo.java这个文件中,主要是通过对指定IP以及端口发出Get请求,然后获取响应包中的“Server”,最后将结果写入文件。代码如下:

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
package  action;
 
import  java.io.BufferedWriter;
import  java.io.IOException;
import  java.net.HttpURLConnection;
import  java.net.MalformedURLException;
import  java.net.URL;
import  java.util.List;
import  java.util.Map;
 
public  class  ServerTypeDemo{ 
     /**
      * 获取到的服务器类型写入txt
      * @param ip IP
      * @param port 提交端口
      * @param writer 写入流
     
      * @return null
      * */
     public  static  void  savaData(String ip,String port,BufferedWriter writer){
         try  {
             URL url =  new  URL( "http://"  + ip +  ":"  + port);
             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
             connection.setRequestMethod( "GET" );
             connection.setConnectTimeout( 1000 );   //毫秒
             connection.setReadTimeout( 1000 );
             Map<String, List<String>> map = connection.getHeaderFields();   //获取HTTP Header Responses
             
             List<String> server = map.get( "Server" );   //关键点
             if (server ==  null ){
                 return ;
             }
             else {  
                 //写入文件
                 for (String tmp : server){
                     writer.write(ip +  ":"  + port +  "       "  + tmp);
                     writer.newLine();                  
                 }
                 writer.flush();
                 connection.disconnect();
             }
         
         catch  (MalformedURLException e) {        
             e.printStackTrace();
         catch  (IOException e) {          
             e.printStackTrace();
         }
 
     }
     
}


四 一个陋界

wKiom1ZszdSxEenNAAB0fFaCOSw024.png

从左到右分别填:起始IP,结束IP(PS:在这里两个IP不在一个C段也行,比如:192.168.1.1~192.168.255.255),线程数,最后点击开始进行扫描,待全部线程结束后会给出提示信息。界面相关代码如下:

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package  view;
 
import  java.awt.Dimension;
import  java.awt.FlowLayout;
import  java.awt.Font;
import  java.awt.Toolkit;
import  java.awt.event.ActionEvent;
import  java.awt.event.ActionListener;
import  java.text.Format;
import  java.text.SimpleDateFormat;
import  java.util.Date;
import  java.util.concurrent.ExecutorService;
import  java.util.concurrent.Executors;
 
import  javax.swing.JButton;
import  javax.swing.JFrame;
import  javax.swing.JMenu;
import  javax.swing.JMenuBar;
import  javax.swing.JMenuItem;
import  javax.swing.JOptionPane;
import  javax.swing.JPanel;
import  javax.swing.JTextField;
 
import  util.IPTraverse;
 
import  action.MyThread;
 
public  class  MainView  extends  JFrame  implements  ActionListener {
     /**
      * 此程序是为了批量探测目标IP段的服务器类型(Apache,tomcat,nginx,IIS。。。) 其中用到了线程池,可以自定义扫描线程数量
      * (PS:只做了一个简陋的界面 O(∩_∩)O~)
     
      * @author zifangsky
      * @blog http://www.zifangsky.cn
      * @version V1.0.0
      * @date 2015-12-9
      * */
     private  static  final  long  serialVersionUID = 1L;
     private  JPanel mainJPanel;
     private  JTextField start, end, threadNum;  // 起始IP,结束IP,线程值
     private  JButton submit;
 
     private  JMenuBar jMenuBar;
     private  JMenu help;  // 帮助
     private  JMenuItem author, contact, version, readme;  // 作者,邮箱,版本号,使用说明
 
     private  Font font =  new  Font( "宋体" , Font.LAYOUT_NO_LIMIT_CONTEXT,  16 );
 
     public  MainView() {
         super ( "批量判断服务器类型(Apache,tomcat,nginx。。。) by zifangsky" );
         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
         screenSize = Toolkit.getDefaultToolkit().getScreenSize();  // 屏幕大小
         setPreferredSize( new  Dimension( 600 300 ));
         int  frameWidth =  this .getPreferredSize().width;  // 界面宽度
         int  frameHeight =  this .getPreferredSize().height;  // 界面高度
         setSize(frameWidth, frameHeight);
         setLocation((screenSize.width - frameWidth) /  2 ,
                 (screenSize.height - frameHeight) /  2 );
 
         mainJPanel =  new  JPanel();
         start =  new  JTextField( "192.168.1.1" 12 );
         end =  new  JTextField( "192.168.1.254" 12 );
         threadNum =  new  JTextField( "5" 8 );
         submit =  new  JButton( "开始" );
         submit.setFont(font);
 
         jMenuBar =  new  JMenuBar();
         help =  new  JMenu( "帮助" );
         help.setFont(font);
         author =  new  JMenuItem( "作者" );
         author.setFont(font);
         contact =  new  JMenuItem( "联系方式" );
         contact.setFont(font);
         version =  new  JMenuItem( "版本" );
         version.setFont(font);
         readme =  new  JMenuItem( "使用说明" );
         readme.setFont(font);
 
         mainJPanel.setLayout( new  FlowLayout(FlowLayout.CENTER,  15 40 ));
         mainJPanel.add(start);
         mainJPanel.add(end);
         mainJPanel.add(threadNum);
         mainJPanel.add(submit);
 
         jMenuBar.add(help);
         help.add(author);
         help.add(contact);
         help.add(version);
         help.add(readme);
 
         add(mainJPanel);
         setJMenuBar(jMenuBar);
         setVisible( true );
         setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
 
         submit.addActionListener( this );
         author.addActionListener( this );
         contact.addActionListener( this );
         version.addActionListener( this );
         readme.addActionListener( this );
     }
 
     public  static  void  main(String[] args) {
         new  MainView();
 
     }


五 处理点击事件

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
58
59
60
     public  void  actionPerformed(ActionEvent e) {
         if  (e.getSource() == submit) {
             Date date =  new  Date();
             Format format =  new  SimpleDateFormat( "HH_mm_ss" );
             // 结果存储的文件名
             String fileName = format.format(date) +  ".txt" ;
 
             String startIP = start.getText();
             String endIP = end.getText();
             long  sips = IPTraverse.ipToLong(startIP);
             long  eips = IPTraverse.ipToLong(endIP);
 
             int  threadNumber = Integer.valueOf(threadNum.getText());
 
             // 多线程,线程池
             ExecutorService eService = Executors.newFixedThreadPool( 50 );
             for  ( int  i =  0 ; i < threadNumber; i++) {
                 MyThread myThread =  new  MyThread(sips, eips, i, threadNumber,
                         fileName);
                 eService.execute(myThread);
             }
             eService.shutdown();
             while  ( true ) {
                 //判断是否全部线程都已经执行结束了
                 if  (eService.isTerminated()) {
                     JOptionPane.showMessageDialog( this "全部扫描结束" "提示:" ,
                             JOptionPane.INFORMATION_MESSAGE);
                     break ;
                 }
                 try  {
                     Thread.sleep( 1000 );
                 catch  (InterruptedException e1) {
                     e1.printStackTrace();
                 }
 
             }
 
         else  if  (e.getSource() == author) {
             JOptionPane.showMessageDialog( this "zifangsky" "作者:" ,
                     JOptionPane.INFORMATION_MESSAGE);
         else  if  (e.getSource() == contact) {
             JOptionPane.showMessageDialog( this ,
                     "邮箱:admin@zifangsky.cn\n博客:http://www.zifangsky.cn" ,
                     "联系方式:" , JOptionPane.INFORMATION_MESSAGE);
         else  if  (e.getSource() == version) {
             JOptionPane.showMessageDialog( this "v1.0.0" "版本号:" ,
                     JOptionPane.INFORMATION_MESSAGE);
         else  if  (e.getSource() == readme) {
             JOptionPane
                     .showMessageDialog(
                             this ,
                             "我只做了一个简陋的图像化界面,默认只扫描80和8080端口,从左到右分别填起始ip(比如:192.168.0.1)\n;"  +
                             "结束ip(比如:192.168.250.250);线程数目(别太大,不然有的结果就漏掉了)\n"  +
                             "还有就是执行的结果会保存在当前目录下的一个txt文件中" ,
                             "使用说明:" , JOptionPane.INFORMATION_MESSAGE);
         }
 
     }
 
}

在这里,由于单线程的扫描速度很慢,因此我使用了多线程。同时又为了判断全部线程是否都已经执行完毕,我又将这些线程放在了一个线程池里,通过eService.isTerminated()这个方法来判断任务是否全部执行完毕。

其实,这里还有一个关键点,如何快速的遍历一个IP段之间的每个IP?最开始我使用了笨方法(PS:四层for循环依次判断),后来度娘了一下,找到了一个不错的IP工具类,可以将IP在long和String之间相互转化。因此转化成long时,一层for循环就可以遍历了,需要String类型时再将它转化回去就可以了

IPTraverse.java

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
package  util;
 
public  class  IPTraverse {
     /**
      * 将127.0.0.1形式的IP地址转换成十进制整数
      * @param strIp
      * @return 整数
      * */
     public  static  long  ipToLong(String strIp) {  
         long [] ip =  new  long [ 4 ];  
         // 先找到IP地址字符串中.的位置  
         int  position1 = strIp.indexOf( "." );  
         int  position2 = strIp.indexOf( "." , position1 +  1 );  
         int  position3 = strIp.indexOf( "." , position2 +  1 );  
         // 将每个.之间的字符串转换成整型  
         ip[ 0 ] = Long.parseLong(strIp.substring( 0 , position1));  
         ip[ 1 ] = Long.parseLong(strIp.substring(position1 +  1 , position2));  
         ip[ 2 ] = Long.parseLong(strIp.substring(position2 +  1 , position3));  
         ip[ 3 ] = Long.parseLong(strIp.substring(position3 +  1 ));  
         return  (ip[ 0 ] <<  24 ) + (ip[ 1 ] <<  16 ) + (ip[ 2 ] <<  8 ) + ip[ 3 ];  
    
     
     /**
      * 将十进制整数形式转换成127.0.0.1形式的ip地址  
      * @param longIp 整数型IP
      * @return 字符串型IP
      * */
     public  static  String longToIP( long  longIp) {  
         StringBuffer sb =  new  StringBuffer( "" );  
         // 直接右移24位  
         sb.append(String.valueOf((longIp >>>  24 )));  
         sb.append( "." );  
         // 将高8位置0,然后右移16位  
         sb.append(String.valueOf((longIp &  0x00FFFFFF ) >>>  16 ));  
         sb.append( "." );  
         // 将高16位置0,然后右移8位  
         sb.append(String.valueOf((longIp &  0x0000FFFF ) >>>  8 ));  
         sb.append( "." );  
         // 将高24位置0  
         sb.append(String.valueOf((longIp &  0x000000FF )));  
         return  sb.toString();  
     }  
}


六 多线程批量扫描

先将IP转化成long型数据,然后根据线程数量将这一连续的IP段均匀分给每个线程执行,再通过调用ServerTypeDemo.java这个核心类发出Get请求,最后是将获取到的信息写入到文件中

(PS:关于多线程处理IP段的原理不太理解的可以看我写的这篇文章:http://www.zifangsky.cn/2015/12/多线程循环批量处理以及多线程操作文件写入相关/

MyThread.java:

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
58
59
package  action;
 
import  java.io.BufferedWriter;
import  java.io.File;
import  java.io.FileWriter;
import  java.io.IOException;
 
import  util.IPTraverse;
 
public  class  MyThread  implements  Runnable {
     private  long  sips;  // 起始IP转化的数组
     private  long  eips;  // 结束IP转化的数组
     private  int  i;  // 第几个线程
     private  int  threadNum;  // 总共创建了几个线程
     private  String fileName;
 
     /**
      * 根据输入的数据多线程批量扫描一个IP段的服务器类型(Apache,tomcat,nginx,IIS。。。)
     
      * @param sips
      *            起始IP转化的整数
      * @param eips
      *            结束IP转化的整数
      * @param i
      *            这是第几个线程
      * @param fileName
      *            结果所保存的文件名
      * @param threadNumber
      *            扫描的线程数
     
      * @return null
      * */
     public  MyThread( long  sips,  long  eips,  int  i,  int  threadNum, String fileName) {
         this .sips = sips;
         this .eips = eips;
         this .i = i;
         this .threadNum = threadNum;
         this .fileName = fileName;
     }
 
     public  void  run() {
         try  {
             BufferedWriter writer =  new  BufferedWriter( new  FileWriter( new  File(fileName), true ));
             // 遍历每个IP
             for  ( long  step = sips + i; step <= eips; step = step + threadNum) {
                 String tempIP = IPTraverse.longToIP(step);
//              System.out.println(tempIP);
                 //这里只扫描了80和8080端口
                 ServerTypeDemo.savaData(tempIP,  "80" , writer);
                 ServerTypeDemo.savaData(tempIP,  "8080" , writer);
             }
             writer.close();
         catch  (IOException e) {
             e.printStackTrace();
         }
         
 
     }
}


七 测试

我随便找了一个IP段进行测试,结果如下:

wKiom1ZszmWyrrjrAAEoAbwgg1E026.png

好了,文章到此结束。



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

相关文章
|
1天前
|
安全 Linux 网络安全
Windows搭建Emby媒体库服务器,无公网IP远程访问本地影音文件
Windows搭建Emby媒体库服务器,无公网IP远程访问本地影音文件
12 0
|
2天前
|
网络协议 安全
ensp中nat server 公网访问内网服务器
ensp中nat server 公网访问内网服务器
|
8天前
|
数据安全/隐私保护 Windows
使用Serv-U FTP服务器共享文件,实现无公网IP环境下远程访问-2
使用Serv-U FTP服务器共享文件,实现无公网IP环境下远程访问
|
8天前
|
存储 网络协议 文件存储
使用Serv-U FTP服务器共享文件,实现无公网IP环境下远程访问-1
使用Serv-U FTP服务器共享文件,实现无公网IP环境下远程访问
|
8天前
|
缓存 网络安全 开发工具
Git服务器报错:host key for (ip地址) has changed and you have requested strict checking
Git服务器报错:host key for (ip地址) has changed and you have requested strict checking
|
9天前
|
存储 数据处理 数据库
|
10天前
|
弹性计算 Shell Apache
某时间段访问apache 服务器的请求IP
【4月更文挑战第29天】
16 2
|
13天前
|
监控 Linux 网络安全
Linux服务器如何查询连接服务器的IP
【4月更文挑战第17天】Linux服务器如何查询连接服务器的IP
14 1
|
19天前
|
安全 Java Linux
如何实现无公网IP及服务器实现公网环境企业微信网页应用开发调试
如何实现无公网IP及服务器实现公网环境企业微信网页应用开发调试
|
19天前
|
Linux 网络安全 文件存储
本地部署Jellyfin影音服务器并实现远程访问内网影音库
本地部署Jellyfin影音服务器并实现远程访问内网影音库