上传文件至 /
This commit is contained in:
parent
3211d7d999
commit
e48e6bc314
247
wrapper.py
Normal file
247
wrapper.py
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
# wrapper_embed.py – 由 PyInstaller 打包,运行时提取并解密原始 EXE
|
||||||
|
import os, sys, json, hashlib, tempfile, subprocess, struct
|
||||||
|
from datetime import datetime
|
||||||
|
import mysql.connector
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import messagebox
|
||||||
|
import platform
|
||||||
|
|
||||||
|
MAGIC = b'ENC_MAGIC'
|
||||||
|
HEAD = 512
|
||||||
|
KEY = b'EXEWrapper#2024'
|
||||||
|
|
||||||
|
|
||||||
|
def xor(data):
|
||||||
|
return bytes(d ^ KEY[i % len(KEY)] for i, d in enumerate(data))
|
||||||
|
|
||||||
|
|
||||||
|
def get_machine_code():
|
||||||
|
import uuid
|
||||||
|
return hashlib.md5(str(uuid.getnode()).encode()).hexdigest()[:16].upper()
|
||||||
|
|
||||||
|
|
||||||
|
def validate(key, machine):
|
||||||
|
try:
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host='taiyiagi.xyz', user='taiyi', password='taiyi1224',
|
||||||
|
database='filesend', connection_timeout=10, autocommit=True
|
||||||
|
)
|
||||||
|
cur = conn.cursor(dictionary=True)
|
||||||
|
cur.execute('SELECT * FROM license_keys WHERE key_code=%s', (key,))
|
||||||
|
print("验证激活码")
|
||||||
|
rec = cur.fetchone()
|
||||||
|
if not rec: return False, '激活码不存在'
|
||||||
|
if rec['status'] == 'banned': return False, '激活码被封禁'
|
||||||
|
if rec['end_time'] < datetime.now(): return False, '激活码已过期'
|
||||||
|
if rec['status'] == 'active':
|
||||||
|
return (True, '验证成功') if rec['machine_code'] == machine else (False, '激活码已绑定其他设备')
|
||||||
|
if rec['status'] == 'unused':
|
||||||
|
cur.execute('UPDATE license_keys SET status=%s, machine_code=%s, start_time=NOW() WHERE key_code=%s',
|
||||||
|
('active', machine, key))
|
||||||
|
return cur.rowcount > 0, '激活成功' if cur.rowcount else '激活失败'
|
||||||
|
return False, '激活码状态异常'
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def get_bundled_data_path():
|
||||||
|
"""获取打包数据文件路径"""
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# 如果是打包后的exe,数据文件在临时目录中
|
||||||
|
bundle_dir = sys._MEIPASS
|
||||||
|
else:
|
||||||
|
# 如果是脚本运行,数据文件在当前目录
|
||||||
|
bundle_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# 查找 .dat 文件
|
||||||
|
for file in os.listdir(bundle_dir):
|
||||||
|
if file.endswith('.dat'):
|
||||||
|
return os.path.join(bundle_dir, file)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def extract_exe():
|
||||||
|
"""从打包的数据文件中提取并解密原始EXE"""
|
||||||
|
try:
|
||||||
|
# 首先尝试从打包的数据文件中读取
|
||||||
|
data_file = get_bundled_data_path()
|
||||||
|
if data_file and os.path.exists(data_file):
|
||||||
|
with open(data_file, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
else:
|
||||||
|
# 如果没有找到数据文件,尝试从当前exe文件末尾读取
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
exe = sys.executable
|
||||||
|
with open(exe, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
else:
|
||||||
|
print("未找到加密数据文件")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 查找魔数位置
|
||||||
|
pos = data.find(MAGIC)
|
||||||
|
if pos == -1:
|
||||||
|
print("未找到加密数据标识")
|
||||||
|
return None
|
||||||
|
|
||||||
|
pos += len(MAGIC)
|
||||||
|
|
||||||
|
# 读取头部信息
|
||||||
|
if pos + HEAD > len(data):
|
||||||
|
print("数据文件损坏:头部信息不完整")
|
||||||
|
return None
|
||||||
|
|
||||||
|
header_data = data[pos:pos + HEAD].rstrip(b'\0')
|
||||||
|
try:
|
||||||
|
hdr = json.loads(header_data.decode())
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"头部信息解析失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
pos += HEAD
|
||||||
|
|
||||||
|
# 解密数据
|
||||||
|
if pos >= len(data):
|
||||||
|
print("数据文件损坏:没有加密数据")
|
||||||
|
return None
|
||||||
|
|
||||||
|
encrypted_data = data[pos:]
|
||||||
|
if len(encrypted_data) == 0:
|
||||||
|
print("加密数据为空")
|
||||||
|
return None
|
||||||
|
|
||||||
|
dec = xor(encrypted_data)
|
||||||
|
|
||||||
|
# 验证文件完整性
|
||||||
|
if hashlib.sha256(dec).hexdigest() != hdr['hash']:
|
||||||
|
print("文件完整性校验失败")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(dec) != hdr['size']:
|
||||||
|
print(f"文件大小不匹配,期望: {hdr['size']}, 实际: {len(dec)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 写入临时文件
|
||||||
|
temp_dir = tempfile.gettempdir()
|
||||||
|
filename = hdr['filename']
|
||||||
|
tmp = os.path.join(temp_dir, f"temp_{os.getpid()}_{filename}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(tmp, 'wb') as f:
|
||||||
|
f.write(dec)
|
||||||
|
|
||||||
|
# 设置可执行权限
|
||||||
|
if hasattr(os, 'chmod'):
|
||||||
|
os.chmod(tmp, 0o755)
|
||||||
|
|
||||||
|
print(f"成功提取文件到: {tmp}")
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"写入临时文件失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"提取EXE文件时发生错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def run(path):
|
||||||
|
"""运行解密后的程序"""
|
||||||
|
try:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print(f"文件不存在: {path}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"正在启动程序: {path}")
|
||||||
|
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
# 使用 CREATE_NEW_CONSOLE 标志在新窗口中启动
|
||||||
|
import subprocess
|
||||||
|
subprocess.Popen([path],
|
||||||
|
creationflags=subprocess.CREATE_NEW_CONSOLE,
|
||||||
|
close_fds=False)
|
||||||
|
else: # Linux/macOS
|
||||||
|
subprocess.Popen([path], close_fds=True)
|
||||||
|
|
||||||
|
# 给程序一些启动时间
|
||||||
|
import time
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"启动程序失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def gui():
|
||||||
|
def ok():
|
||||||
|
k = e.get().strip()
|
||||||
|
if not k:
|
||||||
|
return messagebox.showerror('错误', '请输入激活码')
|
||||||
|
|
||||||
|
try:
|
||||||
|
m = get_machine_code()
|
||||||
|
success, msg = validate(k, m)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
messagebox.showinfo('成功', '激活成功!正在启动程序...')
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
# 提取并运行程序
|
||||||
|
exe = extract_exe()
|
||||||
|
if exe:
|
||||||
|
if run(exe):
|
||||||
|
print("程序启动成功")
|
||||||
|
# 延迟退出,给目标程序启动时间
|
||||||
|
import time
|
||||||
|
time.sleep(2)
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
messagebox.showerror('错误', '程序启动失败')
|
||||||
|
else:
|
||||||
|
messagebox.showerror('错误', '无法提取程序文件,请检查文件完整性')
|
||||||
|
else:
|
||||||
|
messagebox.showerror('失败', msg)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror('错误', f'验证过程出错: {str(e)}')
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title('软件激活')
|
||||||
|
root.geometry('400x200')
|
||||||
|
root.resizable(False, False)
|
||||||
|
|
||||||
|
# 居中显示窗口
|
||||||
|
root.eval('tk::PlaceWindow . center')
|
||||||
|
|
||||||
|
# 界面元素
|
||||||
|
tk.Label(root, text='请输入激活码', font=('', 13)).pack(pady=15)
|
||||||
|
e = tk.Entry(root, width=25)
|
||||||
|
e.pack()
|
||||||
|
e.focus()
|
||||||
|
|
||||||
|
tk.Button(root, text='验证并启动', command=ok).pack(pady=15)
|
||||||
|
|
||||||
|
# 显示机器码(用于调试)
|
||||||
|
machine_code = get_machine_code()
|
||||||
|
tk.Label(root, text=f'机器码: {machine_code}', font=('', 8), fg='gray').pack(pady=(10, 0))
|
||||||
|
|
||||||
|
root.bind('<Return>', lambda x: ok())
|
||||||
|
root.protocol('WM_DELETE_WINDOW', sys.exit)
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 添加调试信息
|
||||||
|
print("程序启动...")
|
||||||
|
print(f"Python版本: {sys.version}")
|
||||||
|
print(f"当前工作目录: {os.getcwd()}")
|
||||||
|
print(f"是否为打包环境: {getattr(sys, 'frozen', False)}")
|
||||||
|
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
print(f"打包临时目录: {sys._MEIPASS}")
|
||||||
|
print(f"打包临时目录内容: {os.listdir(sys._MEIPASS)}")
|
||||||
|
|
||||||
|
gui()
|
||||||
Loading…
Reference in New Issue
Block a user