Qt6开发自签名证书的https代理服务器
目标:制作一个具备类似Fiddler、Burpsuit、Wireshark的https协议代理抓包功能,但是集成到自己的app内,这样无需修改系统代理设置,使用QWebengineview通过自建的代理服务器,即可实现https包的实时监测、注入等自定义功能。
实现:
一、https代理服务器
1.使用QSslSocket类收发https包;使用多线程,提升代理服务器的性能。
ProxyClientThread.h
#ifndef PROXYCLIENTTHREAD_H
#define PROXYCLIENTTHREAD_H#include <QObject>
#include <QTcpSocket>
#include <QNetworkProxy>
#include <QThread>
#include <QDebug>
#include <QSslSocket>
#include <QSslConfiguration>
#include <QFile>
#include <QSslKey>
#include <QByteArray>
#include <QtZlib/zlib.h>
#include <QRegularExpression>struct HTTPHDR{QString host;quint16 port;bool newReq;
};struct HTTPHDR2{quint8 CMD;QString CMDi;QString HOST;quint16 PORT;bool status;
};enum ClientConnectionState {InitialRequest,TlsHandshake,DataTransfer
};class ProxyClientThread : public QThread
{Q_OBJECTpublic:ProxyClientThread(qintptr sockDesc, QObject *parent = 0);~ProxyClientThread();void run();QByteArray LastResquest;private:QSslSocket clientSocket;QSslSocket serverSocket;QSslConfiguration sslConfig;int m_client_state=0;bool m_serverSocketConnected=false;QByteArray cNewReqData;QByteArray clientSockData;QByteArray serverSockData;void processClient();HTTPHDR2 processHeader(QByteArray hdr);bool loadCertificateAndKey();//HTTPHDR getHostInfo(QByteArray httpHeaderPartial);int pid;bool targetFound=false;//是否找到要注入的目标bool istargetHeader=true;//是否头部bool finishInject=false;//已完成注入QString cachedStr="";//缓存的内容;private slots:void clientSockReadyRead();void serverSockConnected();void clientSockDisconnected();void serverSockDisconnected();void serverSockReadyRead();void clientTlsHandOk();void serverSockError(QAbstractSocket::SocketError errorMsg);void clientSockError(QAbstractSocket::SocketError errorMsg);signals:void complete();
};#endif // PROXYCLIENTTHREAD_H
ProxyClientThread.cpp部分代码
#include "proxyclientthread.h"//#define DEBUG 1
QString keyFile="9291.0d30ab5b.js";
QString keyStr="}else e=await V.ImSdk.sendMessage({text:r,textExtra:a,referenceMessage:eQ";
QString injectStr=",window.MySendMsg=e";
ProxyClientThread::ProxyClientThread(qintptr sockDesc, QObject *parent) : QThread(parent)
{this->pid = sockDesc;//服务端连接connect (&this->serverSocket,SIGNAL(disconnected()),this,SLOT(serverSockDisconnected()));connect (&this->serverSocket,SIGNAL(readyRead()),this,SLOT(serverSockReadyRead()));connect (&this->serverSocket,SIGNAL(errorOccurred(QAbstractSocket::SocketError)),this,SLOT(serverSockError(QAbstractSocket::SocketError)));connect (&this->serverSocket,SIGNAL(connected()),this,SLOT(serverSockConnected()));this->serverSocket.setProxy(QNetworkProxy::NoProxy);//客户端m_client_state=InitialRequest;//客户端状态为初始化状态this->clientSocket.setSocketDescriptor(sockDesc);connect(&this->clientSocket, SIGNAL(disconnected()),this,SLOT(clientSockDisconnected()));connect(&this->clientSocket, SIGNAL(readyRead()),this,SLOT(clientSockReadyRead()),Qt::DirectConnection);connect(&this->clientSocket, SIGNAL(encrypted()), this, SLOT(clientTlsHandOk()));connect(&this->clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(clientSockError(QAbstractSocket::SocketError)));
}void ProxyClientThread::clientSockReadyRead()
{this->processClient();return;
}
void ProxyClientThread::processClient()
{HTTPHDR2 pHead;//recieved incoming client packetthis->clientSockData.clear();this->clientSockData = this->clientSocket.readAll();#ifdef DEBUGqDebug()<<this->pid<<"**收到客户端数据"<<this->clientSockData;#endif//查找匹配文件请求QString reqStr=QString(clientSockData);if(reqStr.contains("GET") and reqStr.contains(keyFile)){targetFound=true;qDebug()<<"找到要注入的文件--------------"<<reqStr;//修改请求头,不压缩reqStr.replace("Accept-Encoding: gzip, deflate, br","Accept-Encoding: identity");clientSockData=reqStr.toLocal8Bit();}if (this->serverSocket.state() == QAbstractSocket::ConnectedState){#ifdef DEBUGqDebug() <<this->pid<<": 4.2.向服务器发送请求:";//<<this->clientSockData;#endifserverSocket.write(clientSockData);return;}//处理 headerpHead = this->processHeader(clientSockData.mid(0,100));if (!pHead.status){this->LastResquest = this->clientSockData;return;}//process SSL/TLS Connection;if (pHead.CMD == 3){ //CONNECT类型if (serverSocket.state() == QAbstractSocket::UnconnectedState){#ifdef DEBUGqDebug() <<this->pid<<": 1.收到客户发起CONNECT连接" << pHead.CMD << pHead.HOST << pHead.PORT;#endifm_client_state=TlsHandshake;//握手状态serverSocket.connectToHostEncrypted(pHead.HOST, pHead.PORT);return;}}if (serverSocket.state() == QAbstractSocket::UnconnectedState){#ifdef DEBUGqDebug()<<"***连接服务器";#endifLastResquest=clientSockData;serverSocket.connectToHostEncrypted(pHead.HOST,pHead.PORT);return;}return;
}void ProxyClientThread::clientTlsHandOk(){//clientSockData = clientSocket.readAll();//读取客户端请求#ifdef DEBUGqDebug()<<this->pid<<": 4.<-- 已经和客户端ssl握手成功:"<<LastResquest;#endifserverSocket.write(LastResquest);}...}/** 加载自签名证书
*/
bool ProxyClientThread::loadCertificateAndKey() {QFile certFile(":/certs/server.crt");if (!certFile.open(QIODevice::ReadOnly)) {qWarning() << "Certificate file not found!";return false;}QSslCertificate cert(&certFile);QFile keyFile(":/certs/server.key");if (!keyFile.open(QIODevice::ReadOnly)) {qWarning() << "Private key file not found!";return false;}QSslKey key(&keyFile, QSsl::Rsa);sslConfig.setLocalCertificate(cert);sslConfig.setPrivateKey(key);sslConfig.setProtocol(QSsl::TlsV1_2);return true;
}
3.proxyserver.h
#ifndef PROXYSERVER_H
#define PROXYSERVER_H#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QTcpServer>
//#include <proxyclient.h>
#include <proxyclientthread.h>class proxyServer : public QTcpServer {Q_OBJECTpublic:explicit proxyServer(QObject* parent = nullptr) : QTcpServer(parent) {}protected:void incomingConnection(qintptr socketDescriptor) override {// 创建子线程并传递 socket 描述符ProxyClientThread* workerThread = new ProxyClientThread(socketDescriptor, this);// 启动子线程workerThread->run();}
};#endif // PROXYSERVER_H
代码的逻辑其实不难,按照代理服务器的连接过程补全相关代码就可以了。
二、QWebengineView部分
使用代理服务连接,该设置仅在app内有效,不影响其他应用。
设置QWebengineView的page忽略证书错误(因为是自签名证书),不处理的话无法访问https页面。
// 配置 QWebEngineView 使用代理
QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8787);
QNetworkProxy::setApplicationProxy(proxy);//忽略证书错误
connect(webPage,SIGNAL(certificateError(QWebEngineCertificateError)),this,SLOT(on_certerror(QWebEngineCertificateError)));void xxxx::on_certerror(QWebEngineCertificateError certerror){auto mutableError = const_cast<QWebEngineCertificateError&>(certerror);mutableError.acceptCertificate();qDebug()<<"忽略证书错误。";if(certerror.type()==QWebEngineCertificateError::CertificateAuthorityInvalid){auto error=const_cast<QWebEngineCertificateError&>(certerror);qDebug()<<"忽略证书错误。";error.acceptCertificate();}
}
经过验证,这个方案可行,可以在代理服务器端修改客户端发起的请求,也可以修改服务器端返回的任何数据(已解密过的)后再返回给客户端,但是前提是要做好对应的处理工作,比如Content-length记得要修改。
相关文章:

Qt6开发自签名证书的https代理服务器
目标:制作一个具备类似Fiddler、Burpsuit、Wireshark的https协议代理抓包功能,但是集成到自己的app内,这样无需修改系统代理设置,使用QWebengineview通过自建的代理服务器,即可实现https包的实时监测、注入等自定义功能…...

HarmonyOS:多线程并发-Worker
Worker主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与宿主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞宿主线程的运行。具体接口信息及使用方法详情请见Worker…...

小程序IOS安全区域优化:safe-area-inset-bottom
ios下边有一个小黑线,位于底部的元素会被黑线阻挡 safe-area-inset-bottom 一 用法及作用: IOS全面屏底部有小黑线,位于底部的元素会被黑线阻挡,可以使用以下样式: .model{padding-bottom: constant(safe-area-ins…...
C++ 中多态性在实际项目中的应用场景
C中的多态性是面向对象编程中的一个核心概念,它允许我们在使用基类指针或引用的情况下,调用派生类对象的特定方法。这种特性在实际项目中有着广泛的应用场景,具体包括但不限于以下几个方面: 1.图形图像处理: 在图形图…...
prettier配置
配置 Prettier 在 VSCode 中自动格式化代码的教程 1. 安装 Prettier VSCode 插件 打开 VSCode。点击左侧活动栏的扩展市场图标(或按 Ctrl+Shift+X)。在搜索栏中输入 Prettier - Code formatter。找到插件并点击 Install 安装它。2. 配置 VSCode 设置 确保 VSCode 配置正确,…...

【基于OpenEuler国产操作系统大数据实验环境搭建】
大数据实验环境搭建 一、实验简介1.1 实验内容1.2 环境及其资源规划 二、实验目的三、实验过程3.1 安装虚拟机软件及操作系统3.2 创建安装目录(在主节点上操作)3.2 安装JDK及基本设置(所有节点都需要操作)3.3 安装Hadoop3.4 安装Z…...
期末软件经济学
文章目录 前言复习策略复习名词解释简答题第一章 ppt后记 前言 最近白天都在忙正事,晚上锻炼一下,然后处理一些杂事,现在是晚上十点多,还有一些时间复习一下期末考试。复习到十一点。 复习策略 感觉比较简单,直接刷…...

滑动窗口算法专题
滑动窗口简介 滑动窗口就是利用单调性,配合同向双指针来优化暴力枚举的一种算法。 该算法主要有四个步骤 1. 先进进窗口 2. 判断条件,后续根据条件来判断是出窗口还是进窗口 3. 出窗口 4.更新结果,更新结果这个步骤是不确定的,…...

基于Java的世界时区自动计算及时间生成方法
目录 前言 一、zoneinfo简介 1、zoneinfo是什么 2、zoneinfo有什么 二、在Java中进行时区转换 1、Java与zoneInfo 2、Java展示zoneInfo实例 3、Java获取时区ID 三、Java通过经纬度获取时区 1、通过经度求解偏移 2、通过偏移量计算时间 3、统一的处理算法 四、总结 …...

Excel + Notepad + CMD 命令行批量修改文件名
注意:该方式为直接修改原文件的文件名,不会生成新文件 新建Excel文件 A列:固定为 renB列:原文件名称C列:修改后保存的名称B列、C列,需要带文件后缀,为txt文件就是.txt结尾,为png图片…...

OpenGL 几何着色器高级应用
几何着色器高级应用 概念回顾 几何着色器(Geometry Shader)是 OpenGL 管线中的可选着色器阶段,位于顶点着色器(Vertex Shader) 和光栅化阶段 之间。 其核心功能是基于输入的图元(如点、线或三角形),生成新的图元,或对输入的图元进行修改。 几何着色器的执行是以图元…...

【Unity基础】Unity 2D实现拖拽功能的10种方法
方法1. 基于 Update 循环的拖拽方法 (DragDrop2D) 代码概述 using System.Collections; using System.Collections.Generic; using UnityEngine;public class DragDrop2D : MonoBehaviour {bool isDraggable;bool isDragging;Collider2D objectCollider;void Start(){objectC…...

duxapp中兼容多端的 BoxShadow 阴影组件
由于RN 安卓端对阴影的支持不太完善,使用这个组件可以实现阴影效果 在RN端是使用 react-native-fast-shadow 实现的 示例 import { BoxShadow, Text } from /duxui<BoxShadow><Text>这是内容</Text> </BoxShadow>Props 继承自Taro的View…...
服务器---centos上安装docker并使用docker配置jenkins
要在 Docker 中安装 Jenkins 并进行管理,可以按照以下步骤操作: 1. 安装 Docker 首先,确保你的系统已经安装了 Docker。如果尚未安装,可以使用以下命令进行安装: 在 CentOS 上安装 Docker sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://…...

Linux系统操作03|chmod、vim
上文: Linux系统操作02|基本命令-CSDN博客 目录 六、chmod:给文件设置权限 1、字母法 2、数字法(用的最多) 七、vim:代码编写和文本编辑 1、启动和退出 1️⃣启动 2️⃣退出 2、vim基本操作 六、chmod&#x…...

数据库同步中间件DBSyncer安装配置及使用
1、介绍 DBSyncer(英[dbsɪŋkɜː],美[dbsɪŋkɜː 简称dbs)是一款开源的数据同步中间件,提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景。支持上传插件自定义同步转换业务…...

虚幻5描边轮廓材质
很多游戏内都有这种描边效果,挺实用也挺好看的,简单复刻一下 效果演示: Linethickness可以控制轮廓线条的粗细 这样连完,然后放到网格体细节的覆层材质上即可 可以自己更改粗细大小和颜色...
ISP帳戶會記錄什麼資訊?
許多用戶並不知道ISP會記錄有關線上活動的大量資訊。從流覽歷史記錄到數據使用情況,ISP經常收集和保留用戶數據,引發一系列隱私問題。 ISP 記錄哪些數據? ISP可以根據其隱私政策記錄各種類型的資訊。常見的記錄數據包括: 1.流覽…...
Facebook如何避免因IP变动而封号?实用指南
随着Facebook在个人社交与商业推广中的广泛应用,越来越多的用户面临因“IP变动”而被封号的问题。尤其是跨境电商、广告运营者和多账号管理用户,这种情况可能严重影响正常使用和业务发展。那么,如何避免因IP变动导致的封号问题?本…...

EXCEL数据清洗的几个功能总结备忘
目录 0 参考教材 1 用EXCEL进行数据清洗的几个功能 2 删除重复值: 3 找到缺失值等 4 大小写转换 5 类型转化 6 识别空格 0 参考教材 精通EXCEL数据统计与分析,中国,李宗璋用EXCEL学统计学,日EXCEL统计分析与决策&#x…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...

tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...

基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...