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

flask_socketio+pyautogui实现的具有加密传输功能的极简远程桌面

flask_socketio+pyautogui实现的具有加密传输功能的极简远程桌面:

import base64
import pyautogui
from flask import Flask
import time
from flask_socketio import SocketIO, emit
from io import BytesIO
import pyperclip
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from Crypto.Random import get_random_bytes# 32字节AES密钥
global AES_KEY
AES_KEY='11111111112222222222333333333344'# 加密函数
def aes_encrypt(plain_text, key):"""AES加密"""key_bytes=key.encode('utf-8')iv=get_random_bytes(AES.block_size)# 初始化AES加密器cipher = AES.new(key_bytes, AES.MODE_CBC, iv)# 对明文进行填充plain_text_bytes=plain_text.encode('utf-8')padded_bytes=pad(plain_text_bytes,AES.block_size)cipher_text_bytes=cipher.encrypt(padded_bytes)# 合并IV和密文combined_bytes=iv+cipher_text_bytesencrypted_base64=base64.b64encode(combined_bytes).decode('utf-8')return encrypted_base64# 解密函数
def aes_decrypt(encrypted_base64, key):"""AES解密"""key_bytes=key.encode('utf-8')# 从base64编码中提取IV和密文combined_bytes=base64.b64decode(encrypted_base64)iv=combined_bytes[:AES.block_size]cipher_text_bytes=combined_bytes[AES.block_size:]# 初始化AES解密器cipher = AES.new(key_bytes, AES.MODE_CBC, iv)# 解密密文padded_bytes=cipher.decrypt(cipher_text_bytes)# 移除填充plain_text_bytes=unpad(padded_bytes,AES.block_size)plain_text=plain_text_bytes.decode('utf-8')return plain_text# original_text='Hello, World!'
# # 加密原始文本
# encrypted_text=aes_encrypt(original_text,AES_KEY)
# print('加密后的文本:', encrypted_text)# # 解密加密后的文本
# decrypted_text=aes_decrypt(encrypted_text,AES_KEY)
# print('解密后的文本:', decrypted_text)app=Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'socketio = SocketIO(app)@socketio.on('connect')
def handle_connect():"""客户端连接时触发"""print('客户端已连接')emit('server_response', {'data': '连接成功'})# 连接成功后,获取最新的远程屏幕#client_get_screen()@socketio.on('client_send_keyboard_input')
def client_send_keyboard_input(json):"""处理来自客户端的消息"""print('收到客户端消息:', json)# 模拟键盘输入client_input=json['data']print('客户端输入:', client_input)# typewrite只会输入字母,其他字符会被忽略pyautogui.typewrite(client_input)client_get_screen()@socketio.on('client_send_paste_key')
def client_send_paste_key(json):"""处理来自客户端的粘贴消息"""print('收到客户端粘贴消息:', json)# 模拟粘贴操作client_paste_text=json['data']print('客户端粘贴文本:', client_paste_text)# 先将文本复制到剪贴板pyperclip.copy(client_paste_text)# 模拟粘贴键pyautogui.hotkey('ctrl', 'v')client_get_screen()@socketio.on('client_get_screen')
def client_get_screen():"""处理获取远程屏幕请求"""print('收到获取远程屏幕请求')    # 循环刷新3次for i in range(5):# 延迟0.2秒time.sleep(0.2)screen = pyautogui.screenshot()# 将屏幕图片转换为base64编码buffered = BytesIO()screen.save(buffered, format="PNG")# 得到图片的base64编码screen_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')#print('原始图片base64编码:', screen_base64[:100])# 将图片base64编码进行aes加密global AES_KEYencrypted_screen_base64=aes_encrypt(screen_base64,AES_KEY)# 发送aes加密后的屏幕图片给所有客户端emit('remote_screen_update', {'data': encrypted_screen_base64},broadcast=False)#emit('remote_screen_update', {'data': screen_base64},broadcast=False)@socketio.on('client_click_screen')
def handle_client_click(json):"""处理来自客户端的点击事件"""print('收到客户端点击事件:', json)x, y = json['x'], json['y']# 映射到主机屏幕坐标screen_width, screen_height = pyautogui.size()x, y = int(x * screen_width), int(y * screen_height)pyautogui.moveTo(x, y)pyautogui.click(x, y)# emit('server_response', {'data': f'已点击屏幕位置: ({x}, {y})'},broadcast=False)# 点击鼠标事件后,获取最新的远程屏幕client_get_screen()@socketio.on('client_dbclick_screen')
def handle_client_dbclick(json):"""处理来自客户端的双击事件"""print('收到客户端双击事件:', json)x, y = json['x'], json['y']# 映射到主机屏幕坐标screen_width, screen_height = pyautogui.size()x, y = int(x * screen_width), int(y * screen_height)pyautogui.doubleClick(x, y)# 双击鼠标事件后,获取最新的远程屏幕client_get_screen()@socketio.on('client_send_enter_key')
def handle_client_send_enter_key():"""处理来自客户端的Enter键事件"""print('收到客户端Enter键事件')# 模拟键盘输入Enter键pyautogui.press('enter')client_get_screen()@socketio.on('client_send_delete_key')
def handle_client_send_delete_key():"""处理来自客户端的Delete键事件"""print('收到客户端Delete键事件')# 模拟键盘输入Delete键pyautogui.press('backspace')client_get_screen()@socketio.on('client_send_scroll_up_key')
def handle_client_send_scroll_up_key():"""处理来自客户端的上翻键事件"""print('收到客户端上翻键事件')# 模拟键盘输入上翻键pyautogui.scroll(100)client_get_screen()@socketio.on('client_send_scroll_down_key')
def handle_client_send_scroll_down_key():"""处理来自客户端的下翻键事件"""print('收到客户端下翻键事件')# 模拟键盘输入下翻键pyautogui.scroll(-100)client_get_screen()@app.route('/')
def index():"""渲染主页"""return '''<!DOCTYPE html><html><head><meta lang="zh-CN"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>远程桌面</title><style>body{background-color:black;}#telescreen {position: relative;background-color:skyblue;box-shadow: 0 0 1em rgba(0, 0, 0, 0.5);border:1em solid gray;border-radius: 1em;resize: both;overflow: auto;}#remote_screen {display: none;position: relative;top: 0;left: 0;width: 100%;}#control_panel{background-color: gray;padding: 1em;border-radius: 1em;                    }button,input{margin: 0.5em;padding: 0.5em 1em;border-radius: 0.5em;border: 1px solid #666;}input{width: 80%;}</style><script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script><script type="text/javascript">// 解密AES函数function decryptAES(ciphertext, key, options = {}) {try {// 设置默认选项const mode = options.mode || 'CBC';const padding = options.padding || 'Pkcs7';// 准备解密参数const decryptOptions = {padding: CryptoJS.pad[padding]};// 获取对应的加密模式const cryptoMode = CryptoJS.mode[mode];// 如果指定了IV,使用指定的IV;否则尝试从密文中提取IVif (options.iv) {decryptOptions.iv = CryptoJS.enc.Utf8.parse(options.iv);} else if (mode === 'CBC' || mode === 'CFB' || mode === 'OFB') {// 注意:这部分逻辑取决于加密时IV的存储方式// 有些实现会将IV附加在密文前面一起进行base64编码// 这里提供一个常见的处理方式作为示例const rawData = CryptoJS.enc.Base64.parse(ciphertext);const iv = CryptoJS.lib.WordArray.create(rawData.words.slice(0, 4), 16); // 前16字节作为IVconst actualCiphertext = CryptoJS.lib.WordArray.create(rawData.words.slice(4)); // 剩余部分作为实际密文decryptOptions.iv = iv;ciphertext = CryptoJS.enc.Base64.stringify(actualCiphertext);}// 确保密钥格式正确let keyWordArray;if (key.length === 16 || key.length === 24 || key.length === 32) {// 如果密钥长度符合AES标准,直接使用keyWordArray = CryptoJS.enc.Utf8.parse(key);} else {// 否则使用MD5哈希生成一个32字节的密钥console.warn('密钥长度不符合AES标准,将使用MD5哈希处理');keyWordArray = CryptoJS.MD5(key);}// 执行解密const decrypted = CryptoJS.AES.decrypt(ciphertext, keyWordArray, decryptOptions);// 将解密结果转换为字符串并返回return decrypted.toString(CryptoJS.enc.Utf8);} catch (error) {console.error('AES解密失败:', error);throw new Error('解密失败,请检查密文和密钥是否正确');}}// 连接到服务器document.addEventListener('DOMContentLoaded', () => {// 连接到服务器const socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);// 接收服务器消息socket.on('server_response', (data) => {console.log("收到服务器响应:", data);});// 接收远程屏幕socket.on('remote_screen_update', (data) => {console.log("收到远程屏幕:", data);// 解密远程屏幕数据let secret_key = document.getElementById('secret_key').value.trim();console.log('密钥:', secret_key);if (!secret_key) {console.log('请输入密钥');return;}let decryptedImageData = decryptAES(data.data, secret_key);console.log('解密后的远程屏幕数据:', decryptedImageData);if (!decryptedImageData) {console.log('密钥错误');return;}// 清除旧的远程屏幕图片while (document.getElementById('telescreen').firstChild) {document.getElementById('telescreen').removeChild(document.getElementById('telescreen').firstChild);}//创建新的远程屏幕图片const remote_screen = document.createElement('img');remote_screen.id = 'remote_screen';                        remote_screen.src=`data:image/png;base64,${decryptedImageData}`;remote_screen.style.display = 'block';//remote_screen.src=`data:image/png;base64,${data.data}`;remote_screen.alt = '远程屏幕';remote_screen.addEventListener('click', (e) => {const rect = e.target.getBoundingClientRect();const x = (e.clientX - rect.left) / rect.width;const y = (e.clientY - rect.top) / rect.height;console.log(`点击位置: (${x}, ${y})`);socket.emit('client_click_screen', {x, y});});remote_screen.addEventListener('dbclick', (e) => {const rect = e.target.getBoundingClientRect();const x = (e.clientX - rect.left) / rect.width;const y = (e.clientY - rect.top) / rect.height;console.log(`点击位置: (${x}, ${y})`);socket.emit('client_dbclick_screen', {x, y});});document.getElementById('telescreen').appendChild(remote_screen);});// 绑定获取屏幕按钮事件document.getElementById('get_screen').addEventListener('click', () => {socket.emit('client_get_screen');});// 绑定发送按钮事件document.getElementById('send_button').addEventListener('click', (e) => {const input = document.getElementById('message_input');socket.emit('client_send_keyboard_input', {data: input.value});input.value = '';});// 绑定粘贴按钮事件document.getElementById('paste_button').addEventListener('click', (e) => {const input = document.getElementById('message_input');socket.emit('client_send_paste_key', {data: input.value});input.value = '';});// 绑定Enter键事件document.getElementById('enter_button').addEventListener('click', (e) => {socket.emit('client_send_enter_key');});// 绑定Delete键事件document.getElementById('delete_button').addEventListener('click', (e) => {socket.emit('client_send_delete_key');});// 绑定上翻键事件document.getElementById('scroll_up_button').addEventListener('click', (e) => {socket.emit('client_send_scroll_up_key');});// 绑定下翻键事件document.getElementById('scroll_down_button').addEventListener('click', (e) => {socket.emit('client_send_scroll_down_key');});});</script></head><body><div id="telescreen"><img id="remote_screen" src="" alt="远程屏幕"></div><div id="control_panel"><input type="password" id="secret_key" placeholder="输入正确密钥才能刷新远程屏幕"><br/><button id="get_screen">刷新远程屏幕</button><br/><input type="text" id="message_input" placeholder="输入要发送给远程屏幕的消息"><br/><button id="send_button">发送输入文本</button><br/><button id="paste_button">粘贴输入文本</button><button id="delete_button">Delete</button><br/><button id="enter_button">Enter</button><button id="scroll_up_button">上翻</button><button id="scroll_down_button">下翻</button></div></body></html>'''if __name__ == '__main__':socketio.run(app, debug=True,host='0.0.0.0',port=5000)

http://www.dtcms.com/a/441785.html

相关文章:

  • 深入了解linux网络—— TCP网络通信(上)
  • Android Jetpack 核心组件实战:ViewModel + LiveData + DataBinding 详解
  • 商务厅网站建设意见怎么做网站注册推广
  • Fragment 崩溃恢复后出现重叠问题的复现方式
  • 设计模式(C++)详解——策略模式(2)
  • 使客户能够大规模交付生产就绪的人工智能代理
  • Layui 前端和 PHP 后端的大视频分片上传方案
  • 无状态HTTP的“记忆”方案:Spring Boot中CookieSession全栈实战
  • Java 内存模型(JMM)面试清单(含超通俗生活案例与深度理解)
  • 2015网站建设专业建网站设计公司
  • vue+springboot项目部署到服务器
  • QT肝8天17--优化用户管理
  • QT肝8天19--Windows程序部署
  • 【开题答辩过程】以《基于 Spring Boot 的宠物应急救援系统设计与实现》为例,不会开题答辩的可以进来看看
  • 成都seo网站建设沈阳网站建设推广服务
  • 网站栏目名短链接在线生成官网免费
  • Task Schemas: 基于前沿认知的复杂推理任务架构
  • 第三十七章 ESP32S3 SPI_SDCARD 实验
  • 企业营销型网站特点企业信息查询系统官网山东省
  • docker-compose 安装MySQL8.0.39
  • Go语言入门(18)-指针(上)
  • Django ORM - 聚合查询
  • 【STM32项目开源】基于STM32的智能老人拐杖
  • YOLO入门教程(番外):卷积神经网络—汇聚层
  • 网站改版一般需要多久智慧团建学生登录入口
  • Dotnet接入AI通过Response创建一个简单控制台案例
  • 【论文笔记】2025年图像处理顶会论文
  • 用 Maven 配置 Flink 从初始化到可部署的完整实践
  • 做职业规划的网站seo学院
  • 怎么建优惠券网站太原seo排名外包