#!/usr/bin/env python3 """ EXE文件验证程序 - 负责验证卡密并解密执行原程序 """ import sys import os import tempfile import subprocess import mysql.connector import atexit from cryptography.fernet import Fernet from machine_code import get_machine_code from database import LicenseDatabase import mysql import json import ctypes from ctypes import wintypes import time class EXEValidator: def __init__(self): self.separator = b"<>" self.temp_files = [] # 注册清理函数 atexit.register(self.cleanup) def cleanup(self): """清理临时文件""" for temp_file in self.temp_files: try: if os.path.exists(temp_file): os.remove(temp_file) except: pass def hide_console(self): """隐藏控制台窗口(Windows系统)""" try: if sys.platform == "win32": kernel32 = ctypes.windll.kernel32 user32 = ctypes.windll.user32 SW_HIDE = 0 hWnd = kernel32.GetConsoleWindow() if hWnd: user32.ShowWindow(hWnd, SW_HIDE) except: pass def show_message_box(self, title, message, msg_type="info"): """显示消息框""" try: if sys.platform == "win32": import ctypes if msg_type == "error": icon = 0x10 # MB_ICONERROR elif msg_type == "warning": icon = 0x30 # MB_ICONWARNING else: icon = 0x40 # MB_ICONINFORMATION ctypes.windll.user32.MessageBoxW(0, message, title, icon) else: print(f"{title}: {message}") except: print(f"{title}: {message}") def get_license_key_input(self): """获取用户输入的卡密""" try: if sys.platform == "win32": # Windows系统使用InputBox import ctypes from ctypes import wintypes # 使用VBScript创建输入框 vbs_code = ''' Dim userInput userInput = InputBox("请输入您的授权卡密:", "软件授权验证", "") WScript.Echo userInput ''' # 创建临时VBS文件 with tempfile.NamedTemporaryFile(mode='w', suffix='.vbs', delete=False) as f: f.write(vbs_code) vbs_path = f.name self.temp_files.append(vbs_path) # 执行VBS脚本获取输入 result = subprocess.run(['cscript', '//NoLogo', vbs_path], capture_output=True, text=True) if result.returncode == 0: user_input = result.stdout.strip() if user_input: return user_input return None else: # 非Windows系统使用命令行输入 return input("请输入您的授权卡密: ").strip() except: return None def extract_encrypted_data(self, exe_path): """从加密的EXE文件中提取数据""" try: with open(exe_path, 'rb') as f: data = f.read() # 查找分隔符位置 separators = [] start = 0 while True: pos = data.find(self.separator, start) if pos == -1: break separators.append(pos) start = pos + len(self.separator) if len(separators) < 3: return None, "文件格式错误:分隔符不足" # 提取各部分数据 # 结构: [验证程序, 分隔符, 数据库配置, 分隔符, 密钥, 分隔符, 加密数据] db_config_start = separators[0] + len(self.separator) db_config_end = separators[1] key_start = separators[1] + len(self.separator) key_end = separators[2] encrypted_data_start = separators[2] + len(self.separator) db_config_str = data[db_config_start:db_config_end].decode('utf-8') key = data[key_start:key_end] encrypted_data = data[encrypted_data_start:] # 解析数据库配置 config_parts = db_config_str.split('|') if len(config_parts) != 4: return None, "数据库配置格式错误" db_config = { 'host': config_parts[0], 'database': config_parts[1], 'user': config_parts[2], 'password': config_parts[3] } return { 'db_config': db_config, 'key': key, 'encrypted_data': encrypted_data }, "成功" except Exception as e: return None, f"提取数据失败: {str(e)}" def validate_license(self, db_config, license_key): """验证卡密""" try: # 获取机器码 machine_code = get_machine_code() # 连接数据库 db = LicenseDatabase( db_config['host'], db_config['database'], db_config['user'], db_config['password'] ) if not db.connect(): return False, "无法连接到授权服务器" # 验证卡密 success, message = db.validate_key(license_key, machine_code) db.close() return success, message except Exception as e: return False, f"验证过程出错: {str(e)}" def decrypt_and_run(self, key, encrypted_data): """解密并运行原程序""" try: # 解密数据 fernet = Fernet(key) decrypted_data = fernet.decrypt(encrypted_data) # 创建临时文件 with tempfile.NamedTemporaryFile(suffix='.exe', delete=False) as temp_file: temp_file.write(decrypted_data) temp_exe_path = temp_file.name self.temp_files.append(temp_exe_path) # 设置可执行权限 if os.name == 'nt': # Windows系统 import stat os.chmod(temp_exe_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) # 执行程序 subprocess.Popen([temp_exe_path], creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0) return True, "程序启动成功" except Exception as e: return False, f"解密或执行失败: {str(e)}" def run(self): """主运行函数""" # 隐藏控制台窗口 self.hide_console() try: # 获取当前程序路径 if getattr(sys, 'frozen', False): exe_path = sys.executable else: exe_path = os.path.abspath(__file__) # 提取加密数据 extracted_data, message = self.extract_encrypted_data(exe_path) if not extracted_data: self.show_message_box("错误", f"读取授权信息失败: {message}", "error") return False # 最多尝试3次输入卡密 max_attempts = 3 for attempt in range(max_attempts): # 获取用户输入的卡密 license_key = self.get_license_key_input() if not license_key: if attempt < max_attempts - 1: self.show_message_box("提示", "请输入有效的卡密", "warning") continue else: self.show_message_box("错误", "未输入卡密,程序将退出", "error") return False # 验证卡密 success, message = self.validate_license(extracted_data['db_config'], license_key) if success: # 验证成功,解密并运行程序 run_success, run_message = self.decrypt_and_run( extracted_data['key'], extracted_data['encrypted_data'] ) if run_success: # 给程序一些时间启动 time.sleep(1) return True else: self.show_message_box("错误", f"程序启动失败: {run_message}", "error") return False else: # 验证失败 if attempt < max_attempts - 1: self.show_message_box("验证失败", f"{message}\n\n剩余尝试次数: {max_attempts - attempt - 1}", "warning") else: self.show_message_box("验证失败", f"{message}\n\n已达到最大尝试次数,程序将退出", "error") return False return False except Exception as e: self.show_message_box("系统错误", f"程序运行出错: {str(e)}", "error") return False def main(): """主函数""" validator = EXEValidator() success = validator.run() if not success: sys.exit(1) if __name__ == "__main__": main()