""" 安全的EXE加密器 V2 - 使用改进的验证器 作者:太一 """ import os import hashlib import json import tempfile import subprocess import shutil from typing import Tuple from cryptography.fernet import Fernet import base64 class SecureEXEEncryptor: """安全的EXE加密器 V2(改进版)""" def __init__(self): self.encryption_key = None def generate_encryption_key(self) -> bytes: """生成加密密钥""" return Fernet.generate_key() def encrypt_exe(self, source_path: str, output_path: str, api_config: dict, software_name: str, use_v2_validator: bool = True) -> Tuple[bool, str]: """ 加密EXE文件 参数: source_path: 原始EXE路径 output_path: 输出路径 api_config: API配置(不包含数据库密码) software_name: 软件名称 use_v2_validator: 是否使用V2版本的验证器(推荐) """ try: print(f"开始加密: {source_path}") # 1. 读取原始EXE with open(source_path, 'rb') as f: original_content = f.read() print(f"原始文件大小: {len(original_content)} 字节") # 2. 生成加密密钥 self.encryption_key = self.generate_encryption_key() # 3. 加密EXE内容 fernet = Fernet(self.encryption_key) encrypted_content = fernet.encrypt(original_content) print(f"加密后大小: {len(encrypted_content)} 字节") # 4. 计算哈希 file_hash = hashlib.sha256(original_content).hexdigest() # 5. 准备资源数据 key_part1 = base64.b64encode(self.encryption_key[:16]).decode() key_part2 = base64.b64encode(self.encryption_key[16:]).decode() resource_data = { 'data': base64.b64encode(encrypted_content).decode(), 'hash': file_hash, 'key_hint': key_part1, 'key_part2': key_part2, } # 6. 选择验证器模板 validator_template_path = "validator_secure.py" if use_v2_validator else "validator_secure.py" if not os.path.exists(validator_template_path): return False, f"找不到验证器模板: {validator_template_path}" print(f"使用验证器模板: {validator_template_path}") with open(validator_template_path, 'r', encoding='utf-8') as f: template_code = f.read() # 7. 替换占位符 final_code = template_code.replace( '# ENCRYPTED_RESOURCE_PLACEHOLDER\nENCRYPTED_RESOURCE = None', f'ENCRYPTED_RESOURCE = {json.dumps(resource_data)}' ) # 替换API配置 if 'api_url' in api_config: final_code = final_code.replace( 'API_BASE_URL = "http://localhost:5100/"', f'API_BASE_URL = "{api_config["api_url"]}"' ) if 'api_key' in api_config: final_code = final_code.replace( 'API_KEY = "taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224"', f'API_KEY = "{api_config["api_key"]}"' ) # 替换软件名称 final_code = final_code.replace( 'SOFTWARE_NAME = "SOFTWARE_NAME_PLACEHOLDER"', f'SOFTWARE_NAME = "{software_name}"' ) # 8. 写入临时Python文件 temp_py = tempfile.mktemp(suffix='.py') with open(temp_py, 'w', encoding='utf-8') as f: f.write(final_code) print(f"生成的Python文件: {temp_py}") # 9. 编译为EXE success, msg = self._compile_to_exe(temp_py, output_path) if success: # 保存密钥(用于测试和恢复) key_file = output_path + '.key' with open(key_file, 'wb') as f: f.write(self.encryption_key) print(f"⚠️ 密钥已保存到: {key_file}") print(f"⚠️ 请妥善保管此文件,建议加密存储!") # 显示改进说明 if use_v2_validator: print("\n" + "="*60) print("✅ 已使用V2验证器,包含以下改进:") print(" 1. 持久化缓存存储(不会被系统清理)") print(" 2. 智能验证策略(减少重复验证)") print(" 3. 到期提醒功能(提前7天开始提醒)") print(" 4. 增强的离线验证") print(" 5. 改善的用户界面") print("="*60 + "\n") # 清理临时文件 try: os.remove(temp_py) except: pass return success, msg except Exception as e: import traceback error_detail = traceback.format_exc() print(f"加密过程出错: {error_detail}") return False, f"加密过程出错: {str(e)}" def _compile_to_exe(self, python_file: str, output_path: str) -> Tuple[bool, str]: """编译Python文件为EXE""" build_dir = None try: # 1. 检查PyInstaller print("检查 PyInstaller...") result = subprocess.run(['pyinstaller', '--version'], capture_output=True, text=True, timeout=10) if result.returncode != 0: return False, "PyInstaller未安装或不可用。请运行: pip install pyinstaller" pyinstaller_version = result.stdout.strip() print(f"PyInstaller 版本: {pyinstaller_version}") # 2. 创建工作目录 build_dir = os.path.join(os.path.dirname(os.path.abspath(output_path)), '.build_temp') os.makedirs(build_dir, exist_ok=True) print(f"工作目录: {build_dir}") # 3. 复制Python文件到工作目录 py_name = os.path.basename(python_file) work_py = os.path.join(build_dir, py_name) shutil.copy2(python_file, work_py) print(f"工作文件: {work_py}") # 4. 构建PyInstaller命令 exe_name = os.path.splitext(os.path.basename(output_path))[0] cmd = [ 'pyinstaller', '--clean', '--noconfirm', '--onefile', '--windowed', '--name', exe_name, '--distpath', build_dir, '--workpath', os.path.join(build_dir, 'build'), '--specpath', build_dir, # 添加隐藏导入 '--hidden-import', 'cryptography.fernet', '--hidden-import', 'cryptography.hazmat.primitives', '--hidden-import', 'cryptography.hazmat.backends', '--hidden-import', 'requests', '--hidden-import', 'urllib3', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.messagebox', '--hidden-import', '_tkinter', '--hidden-import', 'sqlite3', # 收集所有cryptography包 '--collect-all', 'cryptography', # 禁用UPX '--noupx', work_py ] print("开始编译...") print(f"命令: {' '.join(cmd)}") # 5. 执行编译 result = subprocess.run( cmd, capture_output=True, text=True, timeout=600, cwd=build_dir, encoding='utf-8', errors='replace' ) # 6. 输出详细日志 if result.stdout: print("=== 编译输出 ===") print(result.stdout[-2000:]) if result.stderr: print("=== 编译错误 ===") print(result.stderr[-2000:]) # 7. 检查编译结果 if result.returncode == 0: compiled_exe = os.path.join(build_dir, f"{exe_name}.exe") print(f"查找编译文件: {compiled_exe}") if os.path.exists(compiled_exe): # 移动到目标位置 os.makedirs(os.path.dirname(output_path), exist_ok=True) if os.path.exists(output_path): os.remove(output_path) shutil.move(compiled_exe, output_path) print(f"✅ 编译成功: {output_path}") print(f"文件大小: {os.path.getsize(output_path) / 1024 / 1024:.2f} MB") # 清理工作目录 try: shutil.rmtree(build_dir) print("已清理临时文件") except: pass return True, "编译成功" else: return False, f"找不到编译输出文件: {compiled_exe}" else: # 提取关键错误信息 error_lines = [] if result.stderr: error_lines = [line for line in result.stderr.split('\n') if 'error' in line.lower() or 'failed' in line.lower()] error_msg = '\n'.join(error_lines[-10:]) if error_lines else result.stderr[-1000:] return False, f"编译失败 (返回码: {result.returncode})\n{error_msg}" except subprocess.TimeoutExpired: return False, "编译超时(超过10分钟)。请检查系统资源或Python依赖。" except Exception as e: import traceback error_detail = traceback.format_exc() print(f"编译异常: {error_detail}") return False, f"编译过程出错: {str(e)}\n\n详细信息:\n{error_detail[-500:]}" finally: # 清理临时文件(如果编译失败) if build_dir and os.path.exists(build_dir): try: if not os.path.exists(output_path): print(f"⚠️ 编译失败,工作目录已保留用于调试: {build_dir}") except: pass # ========== 测试代码 ========== if __name__ == '__main__': print("EXE加密器 V2 - 测试") print("=" * 60) # 测试加密 encryptor = SecureEXEEncryptor() # 配置 api_config = { 'api_url': 'http://localhost:5100/', 'api_key': 'taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224' } # 示例:加密一个测试EXE # success, msg = encryptor.encrypt_exe( # source_path='test.exe', # output_path='test_encrypted_v2.exe', # api_config=api_config, # software_name='TestApp', # use_v2_validator=True # 使用V2验证器 # ) # print(f"\n结果: {success}") # print(f"消息: {msg}") print("\n使用说明:") print("1. 在main.py中使用此加密器") print("2. 设置 use_v2_validator=True 使用改进版验证器") print("3. V2验证器包含持久化缓存、到期提醒等改进功能")