Java Swing 子域名扫描工具 - 多线程、暂停、停止功能
以下是一个示例代码,实现了基本的子域名扫描工具功能:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class SubdomainScanner extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private static final int MAX_THREADS = 200;
private static final int MAX_RETRIES = 3;
private static final int TIMEOUT = 5000;
private JTextField urlField;
private JButton startButton;
private JButton pauseButton;
private JButton stopButton;
private JButton importButton;
private JLabel statusLabel;
private JLabel countLabel;
private JList<String> resultList;
private JScrollPane resultScrollPane;
private ThreadGroup threadGroup;
private List<ScannerThread> threads;
private List<String> subdomains;
private List<String> results;
private int totalCount;
private int processedCount;
public SubdomainScanner() {
super('Subdomain Scanner');
// Initialize UI components
JPanel urlPanel = new JPanel(new BorderLayout());
urlPanel.setBorder(BorderFactory.createTitledBorder('Target URL'));
urlField = new JTextField('example.com');
urlPanel.add(urlField, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
startButton = new JButton('Start');
startButton.addActionListener(this);
buttonPanel.add(startButton);
pauseButton = new JButton('Pause');
pauseButton.addActionListener(this);
pauseButton.setEnabled(false);
buttonPanel.add(pauseButton);
stopButton = new JButton('Stop');
stopButton.addActionListener(this);
stopButton.setEnabled(false);
buttonPanel.add(stopButton);
importButton = new JButton('Import');
importButton.addActionListener(this);
buttonPanel.add(importButton);
JPanel statusPanel = new JPanel(new BorderLayout());
statusPanel.setBorder(BorderFactory.createTitledBorder('Status'));
statusLabel = new JLabel('Ready', SwingConstants.CENTER);
statusLabel.setFont(statusLabel.getFont().deriveFont(Font.BOLD));
statusPanel.add(statusLabel, BorderLayout.CENTER);
countLabel = new JLabel('Processed: 0 / Total: 0');
statusPanel.add(countLabel, BorderLayout.SOUTH);
JPanel resultPanel = new JPanel(new BorderLayout());
resultPanel.setBorder(BorderFactory.createTitledBorder('Results'));
resultList = new JList<>();
resultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
resultScrollPane = new JScrollPane(resultList);
resultScrollPane.setBorder(new LineBorder(Color.GRAY));
resultPanel.add(resultScrollPane, BorderLayout.CENTER);
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
mainPanel.add(urlPanel, BorderLayout.NORTH);
mainPanel.add(buttonPanel, BorderLayout.CENTER);
mainPanel.add(statusPanel, BorderLayout.SOUTH);
mainPanel.add(resultPanel, BorderLayout.EAST);
add(mainPanel);
// Initialize variables
threadGroup = new ThreadGroup('ScannerThreads');
threads = new ArrayList<>();
subdomains = new ArrayList<>();
results = new ArrayList<>();
totalCount = 0;
processedCount = 0;
// Set window properties
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == startButton) {
startButton.setEnabled(false);
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
importButton.setEnabled(false);
statusLabel.setText('Scanning');
countLabel.setText('Processed: 0 / Total: 0');
threads.clear();
results.clear();
totalCount = 0;
processedCount = 0;
loadSubdomains();
startThreads();
} else if (e.getSource() == pauseButton) {
pauseThreads();
} else if (e.getSource() == stopButton) {
stopThreads();
} else if (e.getSource() == importButton) {
importSubdomains();
}
}
private void loadSubdomains() {
String url = urlField.getText().trim().toLowerCase();
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'http://' + url;
}
try {
InetAddress.getByName(url);
} catch (UnknownHostException ex) {
JOptionPane.showMessageDialog(this, 'Invalid URL', 'Error', JOptionPane.ERROR_MESSAGE);
return;
}
subdomains.clear();
try {
File file = new File('subdomains.txt');
if (file.exists() && file.isFile()) {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim().toLowerCase();
if (!line.isEmpty() && !subdomains.contains(line)) {
subdomains.add(line);
}
}
reader.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
if (subdomains.isEmpty()) {
subdomains.add('www');
}
Collections.shuffle(subdomains);
totalCount = subdomains.size();
countLabel.setText('Processed: 0 / Total: ' + totalCount);
}
private void startThreads() {
int numThreads = Math.min(subdomains.size(), MAX_THREADS);
for (int i = 0; i < numThreads; i++) {
ScannerThread thread = new ScannerThread(i);
threads.add(thread);
thread.start();
}
}
private void pauseThreads() {
for (ScannerThread thread : threads) {
thread.pauseThread();
}
statusLabel.setText('Paused');
startButton.setEnabled(true);
pauseButton.setEnabled(false);
stopButton.setEnabled(true);
importButton.setEnabled(true);
}
private void stopThreads() {
for (ScannerThread thread : threads) {
thread.stopThread();
}
statusLabel.setText('Stopped');
startButton.setEnabled(true);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
importButton.setEnabled(true);
}
private void importSubdomains() {
JFileChooser chooser = new JFileChooser();
int result = chooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
subdomains.clear();
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim().toLowerCase();
if (!line.isEmpty() && !subdomains.contains(line)) {
subdomains.add(line);
}
}
reader.close();
} catch (IOException ex) {
ex.printStackTrace();
}
if (subdomains.isEmpty()) {
subdomains.add('www');
}
Collections.shuffle(subdomains);
totalCount = subdomains.size();
countLabel.setText('Processed: 0 / Total: ' + totalCount);
}
}
private synchronized void addResult(String result) {
results.add(result);
resultList.setListData(results.toArray(new String[0]));
}
private synchronized void updateCount() {
processedCount++;
countLabel.setText('Processed: ' + processedCount + ' / Total: ' + totalCount);
if (processedCount == totalCount) {
statusLabel.setText('Done');
startButton.setEnabled(true);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
importButton.setEnabled(true);
}
}
private class ScannerThread extends Thread {
private int index;
private boolean running;
private boolean paused;
public ScannerThread(int index) {
super(threadGroup, 'ScannerThread-' + index);
this.index = index;
this.running = true;
this.paused = false;
}
public void pauseThread() {
paused = true;
}
public void resumeThread() {
paused = false;
}
public void stopThread() {
running = false;
interrupt();
}
@Override
public void run() {
while (running && !subdomains.isEmpty()) {
if (!paused) {
String subdomain = subdomains.remove(0);
String url = subdomain + '.' + urlField.getText().trim().toLowerCase();
try {
InetAddress.getByName(url);
addResult(url);
} catch (UnknownHostException ex) {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
Thread.sleep(TIMEOUT);
InetAddress.getByName(url);
addResult(url);
break;
} catch (UnknownHostException | InterruptedException e) {
// Ignore and retry
}
}
} finally {
updateCount();
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// Ignore and continue
}
}
}
}
}
public static void main(String[] args) {
new SubdomainScanner();
}
}
该代码使用了 Java Swing 构建了一个基本的 UI 界面,其中包含了一个文本框用于输入目标域名,一个按钮用于开始扫描,一个按钮用于暂停扫描,一个按钮用于停止扫描,一个按钮用于导入子域名字典,一个标签用于显示扫描状态,一个标签用于显示已处理/总数信息,以及一个列表框用于显示扫描结果。
扫描过程中使用了多线程,每个线程负责处理一个子域名。每个线程在处理子域名时,会先尝试进行 DNS 解析,如果解析成功则将该子域名添加到结果列表中,否则会进行最多三次的重试。每次重试之间会有 5 秒的等待时间。在处理完一个子域名后,该线程会更新已处理/总数信息,并检查是否还有未处理的子域名,如果有则继续处理,否则该线程就结束。
在 UI 界面中,点击 Start 按钮会开始扫描,点击 Pause 按钮会暂停扫描,点击 Stop 按钮会停止扫描,点击 Import 按钮会导入子域名字典。在扫描过程中,扫描状态会实时更新,已处理/总数信息会实时更新,结果列表会实时显示。在扫描完毕后,所有按钮会恢复到初始状态,并且结果列表会显示所有扫描到的子域名。
原文地址: https://www.cveoy.top/t/topic/lDwN 著作权归作者所有。请勿转载和采集!