一 介绍
这一篇文章是紧接着上一篇文章(http://www.zifangsky.cn/2015/12/使用java开发多线程端口扫描工具/)写的,端口扫描的原理不用多少,我在上一篇文章中已经说过了,至于目的大家都懂得。在这一篇文章里,我主要是对端口扫描工具的继续完善,以及写出一个比较直观的图形界面出来,以方便我们测试使用。界面如下:
这个工具主要是实现了以下几点功能:(1)两种扫描方式,一种是只扫描常见端口,另一种是设置一个起始和结束端口,依次探测。当然,原理很简单,用for循环就可以了;(2)输入一个域名或者IP后,可以自定义扫描线程,这一点是非常有用的,因为有的服务器是装了360,云锁或者安全狗的,扫描线程过大或许会被封IP。
注:文末我会给出简易的可执行jar文件的下载链接
二 代码实现
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class PortScan extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private GridBagLayout gridbag;
private GridBagConstraints constraints;
private JPanel mainJPanel,panel1,panel1_1,panel1_2,panel2,panel2_2,panel3,panel4,panel5;
private ButtonGroup buttonGroup; //扫描方式组
private JRadioButton scanType1,scanType2; //扫描方式1,扫描方式2
private JLabel startJLabel,endJLabel,threadNum; //起始端口和结束端口 线程数
private JLabel progressJLabel,resultJLabel; //进度和结果
private JTextField customPorts,startPort,endPort,customDomain,customThreadNum; //常见端口,起始和结束端口;自定义域名,自定义线程数
private JButton beginJButton; //开始扫描
private JScrollPane progressPane,resultPane; //进度面板和结果面板
private JTextArea progressJtJTextArea,resultJTextArea; //同上
private JMenuBar jMenuBar;
private JMenu help;
private JMenuItem author,contact,version,readme;
private Font menuFont = new Font("宋体", Font.LAYOUT_NO_LIMIT_CONTEXT, 14); //菜单字体
private Font contentFont = new Font("宋体", Font.LAYOUT_NO_LIMIT_CONTEXT, 16); //正文字体
public PortScan(){
super("多线程端口扫描工具");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setPreferredSize(new Dimension(900, 600));
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();
panel1 = new JPanel();
panel1_1 = new JPanel();
panel1_2 = new JPanel();
panel2 = new JPanel();
panel2_2 = new JPanel();
panel3 = new JPanel();
panel4 = new JPanel();
panel5 = new JPanel();
buttonGroup = new ButtonGroup();
scanType1 = new JRadioButton("扫描常见端口:");
scanType2 = new JRadioButton("扫描一个连续段的端口:");
startJLabel = new JLabel("起始端口:");
endJLabel = new JLabel("结束端口:");
threadNum = new JLabel("线程:");
progressJLabel = new JLabel("扫描进度");
resultJLabel = new JLabel("扫描结果");
customPorts = new JTextField("21,22,23,25,26,69,80,110," +
"143,443,465,1080,1158,1433,1521,2100,3306," +
"3389,7001,8080,8081,8888,9080,9090,43958");
startPort = new JTextField("20", 10);
endPort = new JTextField("9000", 10);
customDomain = new JTextField("www.zifangsky.cn", 25);
customThreadNum = new JTextField("5", 5);
beginJButton = new JButton("开始扫描");
progressPane = new JScrollPane();
resultPane = new JScrollPane();
progressJtJTextArea = new JTextArea(18, 20);
resultJTextArea = new JTextArea(18, 20);
//布局
buttonGroup.add(scanType1);
buttonGroup.add(scanType2);
scanType1.setSelected(true);
gridbag = new GridBagLayout();
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.BOTH;
mainJPanel.setLayout(gridbag);
constraints.gridwidth = 0;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
gridbag.setConstraints(scanType1, constraints);
scanType1.setFont(contentFont);
mainJPanel.add(scanType1);
gridbag.setConstraints(customPorts, constraints);
customPorts.setFont(contentFont);
mainJPanel.add(customPorts);
gridbag.setConstraints(scanType2, constraints);
scanType2.setFont(contentFont);
mainJPanel.add(scanType2);
gridbag.setConstraints(panel1, constraints);
mainJPanel.add(panel1);
gridbag.setConstraints(panel2, constraints);
mainJPanel.add(panel2);
constraints.weighty = 1;
gridbag.setConstraints(panel3, constraints);
mainJPanel.add(panel3);
panel1.setLayout(new FlowLayout(FlowLayout.LEFT,30,5));
panel1.add(panel1_1);
panel1.add(panel1_2);
panel1_1.setLayout(new FlowLayout(FlowLayout.CENTER,0,5));
startJLabel.setFont(contentFont);
panel1_1.add(startJLabel);
startPort.setFont(contentFont);
panel1_1.add(startPort);
panel1_2.setLayout(new FlowLayout(FlowLayout.CENTER,0,5));
endJLabel.setFont(contentFont);
panel1_2.add(endJLabel);
endPort.setFont(contentFont);
panel1_2.add(endPort);
panel2.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 5));
customDomain.setFont(contentFont);
panel2.add(customDomain);
panel2.add(panel2_2);
panel2_2.setLayout(new FlowLayout());
threadNum.setFont(contentFont);
panel2_2.add(threadNum);
customThreadNum.setFont(contentFont);
panel2_2.add(customThreadNum);
beginJButton.setFont(contentFont);
panel2.add(beginJButton);
panel3.setLayout(new GridLayout(1, 2));
panel3.add(panel4);
panel3.add(panel5);
panel4.setLayout(new BorderLayout());
progressJLabel.setFont(contentFont);
progressJLabel.setHorizontalAlignment(JLabel.CENTER);
panel4.add(progressJLabel,BorderLayout.NORTH);
panel4.add(progressPane,BorderLayout.CENTER);
progressJtJTextArea.setFont(contentFont);
progressPane.setViewportView(progressJtJTextArea);
progressJtJTextArea.setEditable(false);
progressJtJTextArea.setLineWrap(true);
progressJtJTextArea.setWrapStyleWord(true);
panel5.setLayout(new BorderLayout());
resultJLabel.setFont(contentFont);
resultJLabel.setHorizontalAlignment(JLabel.CENTER);
panel5.add(resultJLabel,BorderLayout.NORTH);
panel5.add(resultPane,BorderLayout.CENTER);
resultJTextArea.setFont(contentFont);
resultPane.setViewportView(resultJTextArea);
resultJTextArea.setEditable(false);
resultJTextArea.setLineWrap(true);
resultJTextArea.setWrapStyleWord(true);
//菜单
jMenuBar = new JMenuBar();
help = new JMenu("帮助");
author = new JMenuItem("作者");
contact = new JMenuItem("联系方式");
version = new JMenuItem("版本号");
readme = new JMenuItem("说明");
help.setFont(menuFont);
jMenuBar.add(help);
author.setFont(menuFont);
help.add(author);
contact.setFont(menuFont);
help.add(contact);
version.setFont(menuFont);
help.add(version);
readme.setFont(menuFont);
help.add(readme);
add(mainJPanel);
setJMenuBar(jMenuBar);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
beginJButton.addActionListener(this);
author.addActionListener(this);
contact.addActionListener(this);
version.addActionListener(this);
readme.addActionListener(this);
}
/**
* 点击事件,根据选择的不同扫描方式,开启不同的线程开始扫描
* */
public void actionPerformed(ActionEvent e) {
if(e.getSource() == beginJButton){
progressJtJTextArea.setText("");
resultJTextArea.setText("");
String domain = getDomainString(customDomain.getText().trim());
int threadNumber = Integer.parseInt(customThreadNum.getText().trim());
if(domain == null)
return ;
if(scanType1.isSelected()){
String[] portsString = customPorts.getText().split(",");
//端口转化为int型
int[] ports = new int[portsString.length];
for(int i=0;i<portsString.length;i++)
ports[i] = Integer.parseInt(portsString[i].trim());
//线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < threadNumber; i++) {
ScanThread1 scanThread1 = new ScanThread1(domain, ports,
threadNumber, i, 800);
threadPool.execute(scanThread1);
}
threadPool.shutdown();
}
else if(scanType2.isSelected()){
int startPortInt = Integer.parseInt(startPort.getText().trim());
int endPortInt = Integer.parseInt(endPort.getText().trim());
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < threadNumber; i++) {
ScanThread2 scanThread2 = new ScanThread2(domain, startPortInt,endPortInt,
threadNumber, i, 800);
threadPool.execute(scanThread2);
}
threadPool.shutdown();
}
}
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, "多线程端口扫描工具,两个扫描方式任你选择,你值得拥有!!!","说明:",JOptionPane.INFORMATION_MESSAGE);
}
}
class ScanThread1 implements Runnable{
private String domain;
private int[] ports; // 待扫描的端口的Set集合
private int threadNumber, serial, timeout; // 线程数,这是第几个线程,超时时间
public ScanThread1(String domain,int[] ports, int threadNumber, int serial,
int timeout) {
this.domain = domain;
this.ports = ports;
this.threadNumber = threadNumber;
this.serial = serial;
this.timeout = timeout;
}
public void run() {
int port = 0;
try {
InetAddress address = InetAddress.getByName(domain);
Socket socket;
SocketAddress socketAddress;
if (ports.length < 1)
return;
for (port = 0 + serial; port <= ports.length - 1; port += threadNumber) {
SwingUtilities.invokeLater(new Progre***unnable( ports[port])); //更新界面
socket = new Socket();
socketAddress = new InetSocketAddress(address, ports[port]);
try {
socket.connect(socketAddress, timeout);
socket.close();
SwingUtilities.invokeLater(new ResultRunnable( ports[port])); //更新界面
} catch (IOException e) {
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
class ScanThread2 implements Runnable{
private String domain;
private int startPort = 20,endPort = 100; // 待扫描的端口的Set集合
private int threadNumber, serial, timeout; // 线程数,这是第几个线程,超时时间
public ScanThread2(String domain, int startPort, int endPort,
int threadNumber, int serial, int timeout) {
this.domain = domain;
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(domain);
Socket socket;
SocketAddress socketAddress;
for (port = startPort + serial; port <= endPort; port += threadNumber) {
SwingUtilities.invokeLater(new Progre***unnable(port)); //更新界面
socket = new Socket();
socketAddress = new InetSocketAddress(address, port);
try {
socket.connect(socketAddress, timeout); // 超时时间
socket.close();
SwingUtilities.invokeLater(new ResultRunnable(port)); //更新界面
} catch (IOException e) {
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
/**
* 由EDT调用来更新界面的线程
* */
class Progre***unnable implements Runnable{
private int currentPort = 0;
public Progre***unnable(int currentPort) {
this.currentPort = currentPort;
}
public void run() {
progressJtJTextArea.setEditable(true);
progressJtJTextArea.append("正在扫描端口:" + currentPort + "\n");
progressJtJTextArea.setEditable(false);
//设置显示最新内容
progressJtJTextArea.selectAll();
progressJtJTextArea.setCaretPosition(progressJtJTextArea.getSelectionEnd());
}
}
/**
* 同上
* */
class ResultRunnable implements Runnable{
private int currentPort = 0;
public ResultRunnable(int currentPort) {
this.currentPort = currentPort;
}
public void run() {
resultJTextArea.setEditable(true);
resultJTextArea.append("端口:" + currentPort + " 开放\n");
resultJTextArea.setEditable(false);
resultJTextArea.selectAll();
resultJTextArea.setCaretPosition(resultJTextArea.getSelectionEnd());
}
}
/**
* 根据输入的字符串提取出其中的域名字符串或者IP字符串,如:www.zifangsky.cn
*
* @param str 输入的包含域名的字符串
* @return 域名或IP字符串
* */
public static String getDomainString(String str){
String reg = "[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+";
Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(str);
if(matcher.find()){
return matcher.group();
}
return "";
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new PortScan();
}
});
}
}
三 测试效果
随便找了一个网站进行测试,效果如下:
附:打包好的可执行jar包:链接:http://down.51cto.com/data/2147925
特别申明:给出测试软件以及源代码仅仅只是为了技术交流,请勿用作非法用途,否则后果自负!
(PS:如果没有下载豆的话,可以看我个人博客上的这篇文章,文末有百度云的下载链接,传送门:http://www.zifangsky.cn/2016/01/使用java开发多线程端口扫描工具(二)/)
转载于:https://blog.51cto.com/983836259/1733295