多线程端口扫描器的实现(java)
一、要求:
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);
-
}
-
}
-
}
-
}
-
}
-
}
-
}
-
}
三、实现功能的界面截图
地址:
地址
(界面过于丑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;
-
}
原理:根据每个线程扫描端口号的个数,从而对端口号进行分段,每个线程执行一段。