版权声明:此文章转载自infocool
原文链接:http://www.infocool.net/kb/Python/201607/168002.html
如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com
这是我大三下学期课程设计的题目,没有想象中的那么难。
一、要求:
1.利用Socket通信机制实现一个多线程的端口扫描器。
2.设计要求:
2.1用户界面:用户可以输入IP地址或IP地址段;输入端口号或端口号范围;列表显示主机名、开放的端口及开放端口上相应的服务或恶意程序的名称;功能按钮。
2.2使用多线程机制对某一地址(段)的主机端口进行扫描;说明开放端口的类型(如UDP端口还是TCP端口);查询数据库,对开放的端口进行说明(如提供的服务或存在的风险)。
2.3有关端口与服务或恶意程序的映射关系保存为数据库表,以扫描出的开放端口号为关键字查询表,将端口的说明显示在界面的列表框中。
二、代码
功能实现类:
public class PortScanner{ public static void main(String[] args){ new EditorWin(); } } class EditorWin extends JFrame implements ActionListener { private JLabel startIp,endIp,l_startPort,l_endPort,l_portOfThread ,showResult ,empty,type ,status; private JTextField f_startIp,f_endIp,f_startPort,f_endPort,f_portOfThread ; private JScrollPane result ; private JComboBox comboBox ; private JButton startScanner,exitScanner ,clear,reset; private JPanel top,bottom ; private JTextArea message ; private String startIpStr ,endIpStr; private int startPort,endPort,portOfThread ,threadNum ; public EditorWin(){ this.setTitle("多线程端口扫描器") ; startIp = new JLabel("扫描的Ip") ; l_startPort = new JLabel("起始端口") ; l_endPort = new JLabel("结束端口") ; l_portOfThread = new JLabel("每个线程扫描端口数") ; status=new JLabel("未开始扫描") ; showResult = new JLabel("扫描结果") ; endIp = new JLabel("结束Ip"); empty = new JLabel(" ") ; type = new JLabel("选择扫描的类型") ; startScanner = new JButton("扫描"); exitScanner = new JButton("退出"); clear = new JButton("清空") ; reset = new JButton("重置") ; f_endIp = new JTextField(12) ; f_startIp = new JTextField(12) ; f_startPort = new JTextField(5) ; f_endPort = new JTextField(5) ; f_portOfThread = new JTextField(5) ; message = new JTextArea(20,20) ; result = new JScrollPane(message) ; result.setColumnHeaderView(showResult) ; comboBox = new JComboBox() ; comboBox.addItem("地址"); comboBox.addItem("地址段"); endIp.setVisible(false) ; f_endIp.setVisible(false) ; top = new JPanel() ; top.add(type); top.add(comboBox) ; top.add(startIp) ; top.add(f_startIp) ; top.add(endIp) ; top.add(f_endIp) ; top.add(l_startPort) ; top.add(f_startPort) ; top.add(l_endPort) ; top.add(f_endPort) ; top.add(l_portOfThread) ; top.add(f_portOfThread) ; bottom = new JPanel() ; bottom.add(status) ; bottom.add(empty) ; bottom.add(empty) ; bottom.add(empty) ; bottom.add(empty) ; bottom.add(empty) ; bottom.add(empty) ; bottom.add(startScanner) ; bottom.add(clear); bottom.add(reset); bottom.add(exitScanner) ; this.add(top,BorderLayout.NORTH); this.add(result,BorderLayout.CENTER) ; this.add(bottom,BorderLayout.SOUTH) ; comboBox.addActionListener(this) ; startScanner.addActionListener(this) ; exitScanner.addActionListener(this) ; clear.addActionListener(this) ; reset.addActionListener(this) ; setSize(1000, 500); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource()==startScanner){ //点击扫描按钮 //点击时刻 startIpStr = f_startIp.getText().trim() ; //得到输入的Ip if(checkIP(startIpStr)){ //判断是否为数字 try{ startPort = Integer.parseInt(f_startPort.getText().trim()) ; endPort = Integer.parseInt(f_endPort.getText().trim()) ; portOfThread =Integer.parseInt(f_portOfThread.getText().trim()) ; threadNum = (endPort-startPort)/portOfThread+1 ; //普安段端口号的范围 if(startPort<0||endPort>65535||startPort>endPort){ JOptionPane.showMessageDialog(this, "端口号范围:0~65535,并且最大端口号应大于最小端口号!") ; } else{ if(portOfThread>endPort-startPort||portOfThread<1){ JOptionPane.showMessageDialog(this, "每个线程扫描的端口数不能大于所有的端口数且不能小于1") ; }else{ if(((String) comboBox.getSelectedItem()).equals("地址")){ message.append("************************************************************"+"\n") ; message.append("正在扫描 "+startIpStr+" 每个线程扫描端口个数"+portOfThread+"\n"+"开启的线程数"+threadNum+"\n") ; message.append("开始端口 "+startPort+" 结束端口" +endPort+"\n") ; message.append("主机名:"+getHostname(startIpStr)+"\n"); message.append("开放的端口如下:"+"\n") ; for(int i = startPort;i <= endPort; i++) { if((i + portOfThread) <= endPort) { new Scan(i, i + portOfThread,startIpStr).start(); i += portOfThread; } else { new Scan(i, endPort,startIpStr).start(); i += portOfThread; } } }else{ endIpStr = f_endIp.getText() ; if(checkIP(endIpStr)){ //扫描Ip地址段 Set ipSet = new HashSet<Object>() ; int start = Integer.valueOf(startIpStr.split("\\.")[3]); int end = Integer.valueOf(endIpStr.split("\\.")[3]); String starts = startIpStr.split("\\.")[0]+"."+startIpStr.split("\\.")[1]+"."+startIpStr.split("\\.")[2]; //生成IP地址 for(int i = start;i<=end;i++){ ipSet.add(starts+"."+i) ; //地海段的每个地址存入集合 } for (Object str : ipSet) { new ScanIp(str.toString()).start() ; } }else{ JOptionPane.showMessageDialog(this, "请输入正确的Ip地址") ; } } } } } catch(NumberFormatException e1){ JOptionPane.showMessageDialog(this, "错误的端口号或端口号和线程数必须为整数") ; } } else{ JOptionPane.showMessageDialog(this, "请输入正确的Ip地址") ; } } else if(e.getSource()==reset){ f_startIp.setText("") ; f_startPort.setText("") ; f_endPort.setText("") ; f_portOfThread.setText("") ; } else if(e.getSource()==clear){ message.setText("") ; System.out.println((String) comboBox.getSelectedItem()); } else if(e.getSource()==exitScanner){ System.exit(1); }else if(e.getSource()==comboBox){ String type=(String) comboBox.getSelectedItem(); if(type.equals("地址")){ endIp.setVisible(false) ; f_endIp.setVisible(false) ; startIp.setText("扫描的Ip") ; }else{ endIp.setVisible(true) ; f_endIp.setVisible(true) ; startIp.setText("开始Ip") ; } } } //扫描端口地址的线程 class Scan extends Thread{ int maxPort, minPort; String Ip; Scan(int minPort, int maxPort,String Ip){ this.minPort=minPort ; this.maxPort=maxPort ; this.Ip=Ip; } @SuppressWarnings("unchecked") public void run() { Socket socket = null ; for(int i = minPort;i<maxPort;i++){ try { socket=new Socket(Ip, i); findInfoByPort(i ,Ip);//通过端口号调用数据库信息 message.append("\n"); socket.close(); } catch (Exception e) { message.append(""); } status.setText("正在扫描"+i) ; } status.setText("扫描结束") ; } } //扫描Ip地址段查看合法Ip的线程 class ScanIp extends Thread{ String Ip ; ScanIp(String Ip ){ this.Ip = Ip ; } public synchronized void run(){ try { for(int i = startPort;i <= endPort; i++) { //扫描开放的Ip InetAddress.getByName(Ip); if((i + portOfThread) <= endPort) { new Scan(i, i + portOfThread,Ip).start(); i += portOfThread; } else { new Scan(i, endPort,Ip).start(); i += portOfThread; } } } catch (Exception e) { System.out.println(Ip+"\n"); } } } //根据端口号,查询数据库中端口号的相应信息并显示在文本域之中 synchronized void findInfoByPort(int port,String Ip){ message.append("-----------------------"+"Ip"+Ip+"的"+"端口号"+port+"------------------------------------"+"\n"); Connection conn ; PreparedStatement pst ; ResultSet rs ; conn = JdbcUtils.getConnection() ;//与数据库建立连接,获取Connection对象 String sql = "Select * from ports where port ="+port; try { pst = conn.prepareStatement(sql) ; rs = pst.executeQuery() ; String totalStr = null ; while(rs.next()){ String server = rs.getString("server"); String info = rs.getString("info") ; message.append("端口信息:"+server+"\n") ; message.append("端口说明:"+info+"\n") ; totalStr = totalStr+server ; } } catch (Exception e) { e.printStackTrace(); } } // 判断输入的IP是否合法 private boolean checkIP(String str) { Pattern pattern = Pattern .compile("^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]" + "|[*])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]|[*])$"); return pattern.matcher(str).matches(); } //根据Ip获得主机名、 public static synchronized String getHostname(String host){ InetAddress addr ; try { addr = InetAddress.getByName(host); return addr.getHostName(); } catch (UnknownHostException e) { return "网络不通或您输入的信息无法构造InetAddress对象!"; } } } 数据库工具类 package portScanner; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public final class JdbcUtils { private static String url = "jdbc:mysql://localhost:3306/portInfo?useUnicode=true&characterEncoding=utf8"; private static String user = "root"; private static String psw = "root"; private static Connection conn; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 获取数据库的连接 * @return conn */ public static Connection getConnection() { if(null == conn) { try { conn = DriverManager.getConnection(url, user, psw); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } return conn; } /** * 释放资源 * @param conn * @param pstmt * @param rs */ public static void closeResources(Connection conn,PreparedStatement pstmt,ResultSet rs) { if(null != rs) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { if(null != pstmt) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { if(null != conn) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } } } } } }
三、实现功能的界面截图
地址:
多线程端口扫描器的实现(java)
地址
多线程端口扫描器的实现(java)
(界面过于丑T-T)
四、多线程扫描端口算法的说明:
for(int i = startPort;i <= endPort; i++) { //扫描开放的Ip InetAddress.getByName(Ip); if((i + portOfThread) <= endPort) { new Scan(i, i + portOfThread,Ip).start(); i += portOfThread; } else { new Scan(i, endPort,Ip).start(); i += portOfThread; }
原理:根据每个线程扫描端口号的个数,从而对端口号进行分段,每个线程执行一段。