工具由来
自从升级win11操作系统后,本人使用的集中远程桌面管理工具,第一次打开和关闭远程桌面后,就会出现第二次连接不上的问题,需要不断的关闭和打开软件才能重新连接,因此给我造成了一些困扰,由于本人电脑一直远程着多台机器工作,因此想重新找个工具,找来找去发现很多都是收费或免费但受限软件,索性干脆写一个简易远程能够连接,方便工作就好。
工具需求
- 只远程管理windows机器
- 调用windows自带的RDP程序开启远程
- 保存连接的信息,如IP地址、用户名和密码,当然需要加密存储,同时可以在界面查看信息
- 添加、删除的机器量不受限制
- 选择开发语言:python
- 可作为windows exe程序运行
工具界面设计
完整代码
import os.path
import tkinter as tk
from tkinter import Menu, messagebox
import subprocess
from threading import Thread
from cryptography.fernet import Fernet
import base64
import re
def encode_to_32_byte_base64(input_string):
# 将字符串填充到32字节长度
padded_input = input_string.ljust(32)[:32]
# 编码为URL安全的base64编码
encoded_bytes = base64.urlsafe_b64encode(padded_input.encode())
return encoded_bytes
def encryptpwdp_password(key, password):
f = Fernet(key)
encrypted_password = f.encrypt(password.encode())
return encrypted_password
def decrypt_password(key, encrypted_password):
f = Fernet(key)
decrypted_password = f.decrypt(encrypted_password).decode()
return decrypted_password
def encrypt_password(non_secure_string):
# Run the PowerShell command and capture the output
try:
command = ['powershell.exe', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command',
f'("{non_secure_string}" | ConvertTo-SecureString -AsPlainText -Force) | ConvertFrom-SecureString']
print(command)
output = subprocess.check_output(command, shell=True, text=True, encoding='utf-8')
print(output)
return output
except subprocess.CalledProcessError as e:
print(f"Error running PowerShell command:\n{e}")
print("Error:", e)
print("Output:", e.output)
def extract_values(filename):
dir_path = 'config'
file_path = os.path.join(dir_path, filename)
try:
with open(file_path, 'r') as file:
content = file.read()
# Define the pattern to match fields and values
pattern = r'([a-zA-Z_]+):([sb]):([^\n]+)'
# Find all matches using the pattern
matches = re.findall(pattern, content)
print(matches)
# Create a dictionary to store the extracted data
extracted_data = {}
for match in matches:
field_name, value_type, value = match
extracted_data[field_name] = value.strip()
return extracted_data
except FileNotFoundError:
print(f"File '{file_path}' not found.")
return None
def generate_connect_config(w_remote_host, w_username, w_password, w_display_host, w_pwdp):
try:
rdp_file_content = f"""\
screen mode id:i:2
smart sizing:i:1
compression:i:1
displayconnectionbar:i:1
disable wallpaper:i:1
disable themes:i:0
autoreconnection enabled:i:0
redirectclipboard:i:1
full address:s:{w_remote_host}
username:s:{w_username}
password 51:b:{w_password}
pwdpp:s:{w_pwdp}
"""
dir_path = 'config'
w_display_host = os.path.join(dir_path, w_display_host)
with open(f"{w_display_host}", "w") as rdp_file:
rdp_file.write(rdp_file_content)
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
def connect_to_remote_desktop(w_display_host):
try:
# Run the RDP file using mstsc
dir_path = 'config'
w_display_host = os.path.join(dir_path, w_display_host)
subprocess.run(["mstsc", f"{w_display_host}"], shell=True)
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
def get_inputs():
input_values = [
entries[0].get(),
entries[1].get(),
entries[2].get(),
entries[3].get()
]
if any(not value for value in input_values):
messagebox.showerror("Error", "All input fields must be filled.")
else:
print("Input values:", input_values)
encrypted_password = encrypt_password(entries[3].get())
print(encrypted_password)
pwdp = encryptpwdp_password(key, entries[3].get()).decode('utf-8')
generate_connect_config(entries[1].get(), entries[2].get(), encrypted_password, entries[0].get(), pwdp)
refresh_buttons()
messagebox.showinfo("信息", f"主机: {entries[1].get()},添加成功!")
def show_context_menu(event, button_name):
selected_button.set(button_name)
context_menu.post(event.x_root, event.y_root)
def modify_button():
button_name = selected_button.get()
# Implement the modify functionality here
messagebox.showinfo("Modify", f"Modify {button_name}")
def delete_button():
button_name = selected_button.get()
dir_path = 'config'
file_path = os.path.join(dir_path, button_name)
try:
os.remove(file_path)
print(f"File '{file_path}' deleted successfully.")
messagebox.showinfo("Delete", f"删除成功 {button_name}")
refresh_buttons()
except FileNotFoundError:
print(f"File '{file_path}' not found.")
except Exception as e:
print(f"An error occurred: {e}")
messagebox.showinfo("Delete", f"删除失败 {button_name}")
def view_button():
button_name = selected_button.get()
# Implement the view functionality here
extracted_data = extract_values(button_name)
if extracted_data:
# 使用 get() 方法获取值,如果键不存在,则返回默认值
s_address = extracted_data.get("address", "Unkown")
s_user = extracted_data.get("username", "Unkown")
s_pwdpp = extracted_data.get("pwdpp", "Unkown")
# 解密密码
s_password = decrypt_password(key, s_pwdpp.encode('utf-8'))
message = f"IP地址:{s_address}\n用户名:{s_user}\n密码:{s_password} "
messagebox.showinfo("View", message)
else:
print("Extraction failed.")
def on_button_click(button_name):
# Run the execute_command function in a separate thread
thread = Thread(target=connect_to_remote_desktop, args=(button_name,))
thread.start()
def load_buttons(folder_path):
for widget in buttons_frame.winfo_children():
widget.destroy() # Clear existing buttons
row = 0
col = 0
for filename in os.listdir(folder_path):
if os.path.isfile(os.path.join(folder_path, filename)):
button = tk.Button(buttons_frame, text=filename)
button.bind("<Button-3>", lambda event, name=filename: show_context_menu(event, name))
button.configure(command=lambda name=filename: on_button_click(name))
button.grid(row=row, column=col, padx=5, pady=5)
col += 1
if col >= 4:
col = 0
row += 1
def refresh_buttons():
folder_path = "config"
if not os.path.exists(folder_path):
messagebox.showerror("Error", "Folder not found.")
return
load_buttons(folder_path)
def main():
# 在这里替换为你自己的密钥
org_key = 'Dxbdywzq@abcd2999@20202020@2023@'
# 编码为32字节的URL安全的base64编码字节
global key
key = encode_to_32_byte_base64(org_key)
folder_path = "config"
if not os.path.exists(folder_path):
os.makedirs(folder_path) # 自动创建文件夹
if not os.path.exists(folder_path):
messagebox.showerror("Error", "Folder not found.")
# return
# Create the main application window
root = tk.Tk()
root.title("Remote Desktop Manager")
root.resizable(width=False, height=False)
# Set the initial window size
window_width = 550
window_height = 220
root.geometry(f"{window_width}x{window_height}+265+255")
# Create a frame to hold input fields using grid layout
input_frame = tk.Frame(root)
input_frame.pack()
# Create labels and entries using pack layout inside the grid frame
label_texts = ["显示名:", "IP地址:", "用户名:", "密码:"]
global entries # Declare entries as global
entries = []
for i, label_text in enumerate(label_texts):
label = tk.Label(input_frame, text=label_text)
entry = tk.Entry(input_frame)
if i == 0:
label.grid(row=1, column=0, sticky="w")
entry.grid(row=1, column=1, padx=5, pady=5, sticky="e")
if i == 1:
label.grid(row=1, column=2, sticky="w")
entry.grid(row=1, column=3, padx=5, pady=5, sticky="e")
if i == 2:
label.grid(row=2, column=0, sticky="w")
entry.grid(row=2, column=1, padx=5, pady=5, sticky="e")
if i == 3:
label.grid(row=2, column=2, sticky="w")
entry.grid(row=2, column=3, padx=5, pady=5, sticky="e")
entries.append(entry)
# Create button to get inputs
button = tk.Button(input_frame, text="添加远程", command=get_inputs)
button.grid(row=3, column=0, padx=10, pady=10)
global selected_button
selected_button = tk.StringVar()
global context_menu
context_menu = Menu(root, tearoff=0)
# context_menu.add_command(label="Modify", command=modify_button)
context_menu.add_command(label="查看", command=view_button)
context_menu.add_separator()
context_menu.add_command(label="删除", command=delete_button)
global buttons_frame
buttons_frame = tk.Frame(root)
buttons_frame.pack()
refresh_button = tk.Button(root, text="重载配置", command=refresh_buttons, width=10)
refresh_button.pack()
refresh_buttons()
root.mainloop()
if __name__ == "__main__":
main()
打包exe程序运行
打包之前,您需要先安装pyinstaller模块,以编译为windows可以执行的exe程序
pyinstaller --onefile --noconsole --icon=alert12.ico --name=远程桌面 main.py
工具下载
下载地址:https://pan.baidu.com/s/1mzejYVV1ILosSr92VwEp2A
提取码:8j6i
运行环境要求:windowns10/11 或有安装powershell的windowns系统。
注:若有使用360,可能会误报,加入信任即可。