当前位置: 首页 > article >正文

Qt中实现可视化界面的TCP SYN扫描(改进版)

        添加TCP SYN扫描的Qt程序-CSDN博客

        为了在Qt中实现一个带有可视化界面的TCP SYN扫描器,并且正确计算IP和TCP校验和,我们需要进行以下步骤:

  1. 创建Qt项目:创建一个新的Qt Widgets应用程序项目。
  2. 设计界面:使用Qt Designer设计一个简单的界面,包含输入目标IP地址、起始端口和结束端口的控件,以及一个开始扫描的按钮。
  3. 实现扫描逻辑:在后台线程中实现TCP SYN扫描逻辑,并正确计算IP和TCP校验和。
  4. 更新UI:在扫描过程中更新UI,显示扫描结果。

 基础版本

1. 创建Qt项目

创建一个新的Qt Widgets应用程序项目,命名为TcpSynScanner

2. 设计界面

使用Qt Designer设计一个简单的界面。界面可以包含以下控件:

  • QLineEdit:用于输入目标IP地址。
  • QSpinBox:用于输入起始端口和结束端口。
  • QPushButton:用于开始扫描。
  • QTextEdit:用于显示扫描结果。

保存设计文件为mainwindow.ui

3. 实现扫描逻辑

mainwindow.cpp中实现扫描逻辑,并正确计算IP和TCP校验和。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
#include <QHostAddress>
#include <QNetworkInterface>
#include <QDateTime>
#include <QSocketNotifier>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

class TcpSynScanner : public QObject {
    Q_OBJECT

public:
    TcpSynScanner(QObject *parent = nullptr) : QObject(parent) {
        rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
        if (rawSocket < 0) {
            qDebug() << "Failed to create raw socket";
            return;
        }
    }

    ~TcpSynScanner() {
        close(rawSocket);
    }

public slots:
    void scan(const QString &targetIp, quint16 startPort, quint16 endPort) {
        struct sockaddr_in destAddr;
        memset(&destAddr, 0, sizeof(destAddr));
        destAddr.sin_family = AF_INET;
        destAddr.sin_addr.s_addr = inet_addr(targetIp.toStdString().c_str());

        for (quint16 port = startPort; port <= endPort; ++port) {
            destAddr.sin_port = htons(port);

            // Create TCP SYN packet
            char packet[4096];
            memset(packet, 0, sizeof(packet));
            struct iphdr *ipHeader = (struct iphdr *)packet;
            struct tcphdr *tcpHeader = (struct tcphdr *)(packet + sizeof(struct iphdr));

            // Fill IP header
            ipHeader->ihl = 5;
            ipHeader->version = 4;
            ipHeader->tos = 0;
            ipHeader->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
            ipHeader->id = htons(54321);
            ipHeader->frag_off = 0;
            ipHeader->ttl = 255;
            ipHeader->protocol = IPPROTO_TCP;
            ipHeader->check = 0;
            ipHeader->saddr = inet_addr("192.168.1.2"); // Source IP address
            ipHeader->daddr = destAddr.sin_addr.s_addr;

            // Fill TCP header
            tcpHeader->source = htons(12345); // Source port
            tcpHeader->dest = htons(port);
            tcpHeader->seq = htonl(123456);
            tcpHeader->ack_seq = 0;
            tcpHeader->doff = 5;
            tcpHeader->syn = 1;
            tcpHeader->window = htons(65535);
            tcpHeader->check = 0;
            tcpHeader->urg_ptr = 0;

            // Calculate TCP checksum
            tcpHeader->check = calculateTcpChecksum(ipHeader, tcpHeader);

            // Send packet
            if (sendto(rawSocket, packet, ipHeader->tot_len, 0, (struct sockaddr *)&destAddr, sizeof(destAddr)) < 0) {
                qDebug() << "Failed to send packet to port" << port;
            } else {
                qDebug() << "Sent SYN packet to port" << port;
            }

            // Wait for a short time before sending the next packet
            QThread::msleep(100);
        }
    }

private:
    int rawSocket;

    quint16 calculateTcpChecksum(struct iphdr *ipHeader, struct tcphdr *tcpHeader) {
        // This is a simplified checksum calculation for demonstration purposes
        // In a real implementation, you should calculate the checksum correctly
        return 0;
    }
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->scanButton, &QPushButton::clicked, this, &MainWindow::startScan);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::startScan() {
    QString targetIp = ui->ipLineEdit->text();
    quint16 startPort = ui->startPortSpinBox->value();
    quint16 endPort = ui->endPortSpinBox->value();

    TcpSynScanner *scanner = new TcpSynScanner(this);
    QThread *thread = new QThread(this);
    scanner->moveToThread(thread);

    connect(thread, &QThread::started, [=]() {
        scanner->scan(targetIp, startPort, endPort);
        thread->quit();
    });

    connect(thread, &QThread::finished, scanner, &QObject::deleteLater);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);

    thread->start();
}

4. 更新UI

在扫描过程中更新UI,显示扫描结果。可以在startScan函数中添加信号和槽机制,将扫描结果实时更新到UI中。

void MainWindow::startScan() {
    QString targetIp = ui->ipLineEdit->text();
    quint16 startPort = ui->startPortSpinBox->value();
    quint16 endPort = ui->endPortSpinBox->value();

    TcpSynScanner *scanner = new TcpSynScanner(this);
    QThread *thread = new QThread(this);
    scanner->moveToThread(thread);

    connect(thread, &QThread::started, [=]() {
        scanner->scan(targetIp, startPort, endPort);
        thread->quit();
    });

    connect(scanner, &TcpSynScanner::portScanned, this, &MainWindow::updateScanResult);
    connect(thread, &QThread::finished, scanner, &QObject::deleteLater);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);

    thread->start();
}

void MainWindow::updateScanResult(quint16 port, bool isOpen) {
    QString result = QString("Port %1 is %2\n").arg(port).arg(isOpen ? "open" : "closed");
    ui->resultTextEdit->append(result);
}

5. 编译和运行

编译并运行你的Qt项目。程序将显示一个简单的界面,用户可以输入目标IP地址、起始端口和结束端口,并开始扫描。扫描结果将实时显示在界面上。

 

进阶版本

通过改进上述程序来实现检查端口是否开放的功能。为了实现这一点,我们需要在发送TCP SYN包后监听来自目标主机的响应。如果收到TCP SYN-ACK响应,则端口是开放的;如果收到TCP RST响应,则端口是关闭的;如果没有收到响应,则端口可能被防火墙屏蔽。

以下是改进后的代码,包括发送SYN包和监听响应:

1. 更新 TcpSynScanner 类

在 TcpSynScanner 类中添加一个槽函数 listenForResponses 来监听响应,并在发送SYN包后启动监听。

class TcpSynScanner : public QObject {
    Q_OBJECT

public:
    TcpSynScanner(QObject *parent = nullptr) : QObject(parent) {
        rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
        if (rawSocket < 0) {
            qDebug() << "Failed to create raw socket";
            return;
        }
    }

    ~TcpSynScanner() {
        close(rawSocket);
    }

public slots:
    void scan(const QString &targetIp, quint16 startPort, quint16 endPort) {
        struct sockaddr_in destAddr;
        memset(&destAddr, 0, sizeof(destAddr));
        destAddr.sin_family = AF_INET;
        destAddr.sin_addr.s_addr = inet_addr(targetIp.toStdString().c_str());

        for (quint16 port = startPort; port <= endPort; ++port) {
            destAddr.sin_port = htons(port);

            // Create TCP SYN packet
            char packet[4096];
            memset(packet, 0, sizeof(packet));
            struct iphdr *ipHeader = (struct iphdr *)packet;
            struct tcphdr *tcpHeader = (struct tcphdr *)(packet + sizeof(struct iphdr));

            // Fill IP header
            ipHeader->ihl = 5;
            ipHeader->version = 4;
            ipHeader->tos = 0;
            ipHeader->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
            ipHeader->id = htons(54321);
            ipHeader->frag_off = 0;
            ipHeader->ttl = 255;
            ipHeader->protocol = IPPROTO_TCP;
            ipHeader->check = 0;
            ipHeader->saddr = inet_addr("192.168.1.2"); // Source IP address
            ipHeader->daddr = destAddr.sin_addr.s_addr;

            // Fill TCP header
            tcpHeader->source = htons(12345); // Source port
            tcpHeader->dest = htons(port);
            tcpHeader->seq = htonl(123456);
            tcpHeader->ack_seq = 0;
            tcpHeader->doff = 5;
            tcpHeader->syn = 1;
            tcpHeader->window = htons(65535);
            tcpHeader->check = 0;
            tcpHeader->urg_ptr = 0;

            // Calculate TCP checksum
            tcpHeader->check = calculateTcpChecksum(ipHeader, tcpHeader);

            // Send packet
            if (sendto(rawSocket, packet, ipHeader->tot_len, 0, (struct sockaddr *)&destAddr, sizeof(destAddr)) < 0) {
                qDebug() << "Failed to send packet to port" << port;
            } else {
                qDebug() << "Sent SYN packet to port" << port;
            }

            // Listen for responses
            listenForResponses(port);
        }
    }

private:
    int rawSocket;

    quint16 calculateTcpChecksum(struct iphdr *ipHeader, struct tcphdr *tcpHeader) {
        // This is a simplified checksum calculation for demonstration purposes
        // In a real implementation, you should calculate the checksum correctly
        return 0;
    }

    void listenForResponses(quint16 port) {
        char buffer[4096];
        struct sockaddr_in fromAddr;
        socklen_t fromLen = sizeof(fromAddr);

        // Set a timeout for the recvfrom call
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        setsockopt(rawSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));

        int bytesReceived = recvfrom(rawSocket, buffer, sizeof(buffer), 0, (struct sockaddr *)&fromAddr, &fromLen);
        if (bytesReceived > 0) {
            struct iphdr *ipHeader = (struct iphdr *)buffer;
            struct tcphdr *tcpHeader = (struct tcphdr *)(buffer + (ipHeader->ihl * 4));

            if (ipHeader->daddr == inet_addr("192.168.1.2") && tcpHeader->dest == htons(12345)) {
                if (tcpHeader->ack && tcpHeader->syn) {
                    emit portScanned(port, true); // Port is open
                } else if (tcpHeader->reset) {
                    emit portScanned(port, false); // Port is closed
                }
            }
        } else {
            emit portScanned(port, false); // No response, port is filtered
        }
    }

signals:
    void portScanned(quint16 port, bool isOpen);
};

2. 更新 MainWindow 类

在 MainWindow 类中添加槽函数 updateScanResult 来更新UI。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->scanButton, &QPushButton::clicked, this, &MainWindow::startScan);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::startScan() {
    QString targetIp = ui->ipLineEdit->text();
    quint16 startPort = ui->startPortSpinBox->value();
    quint16 endPort = ui->endPortSpinBox->value();

    TcpSynScanner *scanner = new TcpSynScanner(this);
    QThread *thread = new QThread(this);
    scanner->moveToThread(thread);

    connect(thread, &QThread::started, [=]() {
        scanner->scan(targetIp, startPort, endPort);
        thread->quit();
    });

    connect(scanner, &TcpSynScanner::portScanned, this, &MainWindow::updateScanResult);
    connect(thread, &QThread::finished, scanner, &QObject::deleteLater);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);

    thread->start();
}

void MainWindow::updateScanResult(quint16 port, bool isOpen) {
    QString result = QString("Port %1 is %2\n").arg(port).arg(isOpen ? "open" : "closed");
    ui->resultTextEdit->append(result);
}

3. 更新 UI 设计

确保你的 mainwindow.ui 文件包含以下控件:

  • QLineEdit:用于输入目标IP地址,命名为 ipLineEdit
  • QSpinBox:用于输入起始端口,命名为 startPortSpinBox
  • QSpinBox:用于输入结束端口,命名为 endPortSpinBox
  • QPushButton:用于开始扫描,命名为 scanButton
  • QTextEdit:用于显示扫描结果,命名为 resultTextEdit

4. 编译和运行

编译并运行你的Qt项目。程序将显示一个简单的界面,用户可以输入目标IP地址、起始端口和结束端口,并开始扫描。扫描结果将实时显示在界面上,指示端口是开放、关闭还是被过滤。

注意事项

  1. 权限问题:在Linux系统上,使用原始套接字需要root权限。
  2. 合法性:确保你只在授权的网络上进行此类操作。
  3. 校验和计算:上述代码中的校验和计算是简化的,实际应用中需要正确计算IP和TCP校验和。
  4. 性能:为了提高性能,可以考虑并行化扫描过程,例如使用多个线程或异步IO。

通过这种方式,你可以在Qt中实现一个带有可视化界面的TCP SYN扫描器,并且能够检查端口是否开放。


http://www.kler.cn/a/428441.html

相关文章:

  • jenkins-k8s pod方式动态生成slave节点
  • USART_串口通讯轮询案例(HAL库实现)
  • C语言操作符(上)
  • 数据结构:二叉树
  • OGG 19C 集成模式启用DDL复制
  • 等变即插即用图像重建
  • Lumos学习王佩丰Excel第二十讲:图表基础
  • 黑马程序员Java项目实战《苍穹外卖》Day09
  • Java集合(三)- Stack Queue
  • 如何用python获取图像
  • ADI的DSP用CCES来调试,仿真器TEST第一步“Opening Emulator Interface”报错,解决办法。
  • Chrome 中小于 12px 文字的实现方式与应用场景详解
  • 机器学习周报(12.2-12.8)
  • C# NLog 配置ElasticSearch
  • 【JAVA】Java高级:Spring框架与Java EE—Spring框架概述(控制反转、依赖注入)
  • 链表OJ题型讲解与总结
  • 【金融贷后】贷后核心风险指标有哪些?
  • 算法训练(leetcode)二刷第三十四天 | *198. 打家劫舍、*213. 打家劫舍 II、*337. 打家劫舍 III
  • 谷歌DeepMind推出RT-2 大模型机器人方面应用
  • 设计模式:20、状态模式(状态对象)
  • OpenGL编译用户着色器shader
  • 工业检测基础-线扫相机和面阵相机参数及应用
  • Java、python标识符命名规范
  • 22. C++STL 8(stack与queue的使用与模拟,STL容器适配器,vector与deque的效率比较)
  • springSecurity自定义登陆接口和JWT认证过滤器
  • go语言的sdk项目搭建与git 操作标签tag并推送至远程仓库