下面是一个使用Qt和C++实现的简单端口扫描程序的示例。这个程序将使用TCP connect方法进行端口扫描,并利用多线程技术来提高扫描效率。
1. 创建Qt项目
首先,创建一个新的Qt项目。你可以使用Qt Creator来创建一个Qt Widgets应用程序。
2. 添加必要的头文件
在你的主窗口类文件(例如mainwindow.h
)中,添加必要的头文件:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include <QTcpSocket>
#include <QQueue>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class PortScanner : public QThread
{
Q_OBJECT
public:
explicit PortScanner(const QString &ip, int startPort, int endPort, QObject *parent = nullptr);
void run() override;
signals:
void portOpen(int port);
void portClosed(int port);
void scanningFinished();
private:
QString ip;
int startPort;
int endPort;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_scanButton_clicked();
void onPortOpen(int port);
void onPortClosed(int port);
void onScanningFinished();
private:
Ui::MainWindow *ui;
QQueue<PortScanner*> scanners;
int totalPorts;
int scannedPorts;
};
#endif // MAINWINDOW_H
3. 实现PortScanner类
在mainwindow.cpp
中实现PortScanner
类:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTcpSocket>
#include <QThread>
#include <QDebug>
PortScanner::PortScanner(const QString &ip, int startPort, int endPort, QObject *parent)
: QThread(parent), ip(ip), startPort(startPort), endPort(endPort)
{
}
void PortScanner::run()
{
for (int port = startPort; port <= endPort; ++port) {
QTcpSocket socket;
socket.connectToHost(ip, port);
if (socket.waitForConnected(1000)) {
emit portOpen(port);
socket.disconnectFromHost();
} else {
emit portClosed(port);
}
}
emit scanningFinished();
}
4. 实现MainWindow类
继续在mainwindow.cpp
中实现MainWindow
类:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
scannedPorts = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_scanButton_clicked()
{
QString ip = ui->ipLineEdit->text();
int startPort = ui->startPortSpinbox->value();
int endPort = ui->endPortSpinbox->value();
int threadCount = ui->threadCountSpinbox->value();
totalPorts = endPort - startPort + 1;
scannedPorts = 0;
ui->resultTextBrowser->clear();
int portsPerThread = totalPorts / threadCount;
int remainingPorts = totalPorts % threadCount;
int currentPort = startPort;
for (int i = 0; i < threadCount; ++i) {
int end = currentPort + portsPerThread - 1;
if (remainingPorts > 0) {
end += 1;
remainingPorts -= 1;
}
PortScanner *scanner = new PortScanner(ip, currentPort, end);
connect(scanner, &PortScanner::portOpen, this, &MainWindow::onPortOpen);
connect(scanner, &PortScanner::portClosed, this, &MainWindow::onPortClosed);
connect(scanner, &PortScanner::scanningFinished, this, &MainWindow::onScanningFinished);
scanners.enqueue(scanner);
scanner->start();
currentPort = end + 1;
}
}
void MainWindow::onPortOpen(int port)
{
ui->resultTextBrowser->append("Port " + QString::number(port) + " is open.");
}
void MainWindow::onPortClosed(int port)
{
// Optionally, you can log closed ports here
}
void MainWindow::onScanningFinished()
{
if (!scanners.isEmpty()) {
delete scanners.dequeue();
}
scannedPorts++;
if (scannedPorts >= totalPorts) {
ui->resultTextBrowser->append("Scanning finished.");
}
}
5. 设计UI
在Qt Designer中设计主窗口UI,添加以下控件:
QLineEdit
:用于输入目标IP地址QSpinBox
:用于输入起始端口和结束端口QSpinBox
:用于输入线程数量QPushButton
:用于开始扫描QTextBrowser
:用于显示扫描结果
确保在mainwindow.ui
中正确设置了这些控件的ObjectName,例如:
ipLineEdit
startPortSpinbox
endPortSpinbox
threadCountSpinbox
scanButton
resultTextBrowser
6. 编译和运行
编译并运行你的Qt项目。输入目标IP地址、端口范围和线程数量,然后点击“扫描”按钮开始端口扫描。
这个示例实现了基本的端口扫描功能,使用TCP connect方法和多线程技术来提高扫描效率。你可以根据需要进一步扩展和优化这个程序,例如添加更多的扫描方法(如TCP SYN和TCP FIN扫描),增加用户界面的友好性,或者改进错误处理机制。
完整代码
为了方便起见,以下是完整的代码示例。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include <QTcpSocket>
#include <QQueue>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class PortScanner : public QThread
{
Q_OBJECT
public:
explicit PortScanner(const QString &ip, int startPort, int endPort, QObject *parent = nullptr);
void run() override;
signals:
void portOpen(int port);
void portClosed(int port);
void scanningFinished();
private:
QString ip;
int startPort;
int endPort;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_scanButton_clicked();
void onPortOpen(int port);
void onPortClosed(int port);
void onScanningFinished();
private:
Ui::MainWindow *ui;
QQueue<PortScanner*> scanners;
int totalPorts;
int scannedPorts;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTcpSocket>
#include <QThread>
#include <QDebug>
PortScanner::PortScanner(const QString &ip, int startPort, int endPort, QObject *parent)
: QThread(parent), ip(ip), startPort(startPort), endPort(endPort)
{
}
void PortScanner::run()
{
for (int port = startPort; port <= endPort; ++port) {
QTcpSocket socket;
socket.connectToHost(ip, port);
if (socket.waitForConnected(1000)) {
emit portOpen(port);
socket.disconnectFromHost();
} else {
emit portClosed(port);
}
}
emit scanningFinished();
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
scannedPorts = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_scanButton_clicked()
{
QString ip = ui->ipLineEdit->text();
int startPort = ui->startPortSpinbox->value();
int endPort = ui->endPortSpinbox->value();
int threadCount = ui->threadCountSpinbox->value();
totalPorts = endPort - startPort + 1;
scannedPorts = 0;
ui->resultTextBrowser->clear();
int portsPerThread = totalPorts / threadCount;
int remainingPorts = totalPorts % threadCount;
int currentPort = startPort;
for (int i = 0; i < threadCount; ++i) {
int end = currentPort + portsPerThread - 1;
if (remainingPorts > 0) {
end += 1;
remainingPorts -= 1;
}
PortScanner *scanner = new PortScanner(ip, currentPort, end);
connect(scanner, &PortScanner::portOpen, this, &MainWindow::onPortOpen);
connect(scanner, &PortScanner::portClosed, this, &MainWindow::onPortClosed);
connect(scanner, &PortScanner::scanningFinished, this, &MainWindow::onScanningFinished);
scanners.enqueue(scanner);
scanner->start();
currentPort = end + 1;
}
}
void MainWindow::onPortOpen(int port)
{
ui->resultTextBrowser->append("Port " + QString::number(port) + " is open.");
}
void MainWindow::onPortClosed(int port)
{
// Optionally, you can log closed ports here
}
void MainWindow::onScanningFinished()
{
if (!scanners.isEmpty()) {
PortScanner *scanner = scanners.dequeue();
scanner->deleteLater();
}
scannedPorts++;
if (scannedPorts >= totalPorts) {
ui->resultTextBrowser->append("Scanning finished.");
}
}
mainwindow.ui
你需要在Qt Designer中设计主窗口的UI,包含以下控件:
QLineEdit
:ipLineEdit
,用于输入IP地址QSpinBox
:startPortSpinbox
,用于输入起始端口QSpinBox
:endPortSpinbox
,用于输入结束端口QSpinBox
:threadCountSpinbox
,用于输入线程数量QPushButton
:scanButton
,用于开始扫描QTextBrowser
:resultTextBrowser
,用于显示扫描结果
确保正确设置这些控件的ObjectName,以便在代码中正确引用它们。
运行项目
创建一个新的Qt Widgets应用程序项目。
将上述代码分别放入相应的头文件和源文件中。
在Qt Designer中设计主窗口UI,并确保ObjectName与代码中一致。
编译并运行项目。
现在,你可以输入目标IP地址、端口范围和线程数量,然后点击“扫描”按钮开始端口扫描。扫描结果将显示在resultTextBrowser
中。
注意事项
合法性:确保你只有在获得授权的情况下才能对目标主机进行端口扫描,否则可能违反法律法规。
性能:多线程扫描可以提高扫描速度,但过多的线程可能会导致性能下降或被目标主机封禁。请根据实际情况调整线程数量。
错误处理:当前实现中,对于连接超时或错误情况,统一视为端口关闭。你可以根据需要增加更详细的错误处理和日志记录。
扫描方法:示例中仅实现了TCP connect扫描,你可以进一步扩展,实现其他类型的扫描,如TCP SYN扫描等。但需要注意,某些扫描方法可能需要管理员权限或使用原始套接字,这在某些操作系统上可能受限。
进一步改进
添加进度条:显示扫描进度,使用户了解扫描的完成情况。
支持多种扫描方法:如TCP SYN扫描、UDP扫描等,并提供相应的选项供用户选择。
增加目标主机解析:支持输入主机名,并自动解析为IP地址。
结果导出:允许用户将扫描结果导出为文件,如文本文件或CSV文件。
图形化展示:使用图表等方式展示开放端口的情况。
增加扫描速度控制:允许用户设置扫描延迟,以减少对目标主机的影响。
集成Nmap等现有工具:通过调用外部程序如Nmap来进行更复杂的扫描,并解析其输出结果。
通过这些改进,可以使端口扫描程序更加功能丰富和用户友好。