2025-10-23 18:28:10 +08:00
|
|
|
|
"""
|
2025-10-28 13:18:45 +08:00
|
|
|
|
安全的EXE加密器(V2改进版)
|
|
|
|
|
|
- 使用持久化缓存的验证器
|
|
|
|
|
|
- 激活一次,长期使用
|
|
|
|
|
|
- 到期提醒功能
|
|
|
|
|
|
- 智能验证策略
|
|
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
作者:太一
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import hashlib
|
|
|
|
|
|
import json
|
|
|
|
|
|
import tempfile
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
import shutil
|
2025-10-28 13:18:45 +08:00
|
|
|
|
import uuid
|
2025-10-23 18:28:10 +08:00
|
|
|
|
from typing import Tuple
|
2025-10-28 13:18:45 +08:00
|
|
|
|
from datetime import datetime
|
2025-10-23 18:28:10 +08:00
|
|
|
|
from cryptography.fernet import Fernet
|
|
|
|
|
|
import base64
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 加密文件的特殊标识(嵌入在验证器中)
|
|
|
|
|
|
ENCRYPTION_MARKER = "SECURE_EXE_VALIDATOR_V2_TAIYI"
|
|
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
class SecureEXEEncryptor:
|
2025-10-28 13:18:45 +08:00
|
|
|
|
"""安全的EXE加密器 V2(改进版)- 支持版本管理"""
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self.encryption_key = None
|
2025-10-28 13:18:45 +08:00
|
|
|
|
self.metadata_file = "files/file_metadata.json"
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
def generate_encryption_key(self) -> bytes:
|
|
|
|
|
|
"""生成加密密钥"""
|
|
|
|
|
|
return Fernet.generate_key()
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
def _is_encrypted_file(self, file_path: str) -> bool:
|
|
|
|
|
|
"""检测文件是否已经被加密过"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 方法1: 检查文件中是否包含加密标识
|
|
|
|
|
|
with open(file_path, 'rb') as f:
|
|
|
|
|
|
content = f.read()
|
|
|
|
|
|
# 检查是否包含验证器的特殊标记
|
|
|
|
|
|
if ENCRYPTION_MARKER.encode() in content:
|
|
|
|
|
|
return True
|
|
|
|
|
|
# 检查是否包含验证器代码特征
|
|
|
|
|
|
if b"ENCRYPTED_RESOURCE" in content and b"smart_validate" in content:
|
|
|
|
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"检测文件时出错: {e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def _load_metadata(self) -> dict:
|
|
|
|
|
|
"""加载元数据文件"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if os.path.exists(self.metadata_file):
|
|
|
|
|
|
with open(self.metadata_file, 'r', encoding='utf-8') as f:
|
|
|
|
|
|
return json.load(f)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"加载元数据失败: {e}")
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
def _save_metadata(self, metadata: dict):
|
|
|
|
|
|
"""保存元数据文件"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
os.makedirs(os.path.dirname(self.metadata_file), exist_ok=True)
|
|
|
|
|
|
with open(self.metadata_file, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
json.dump(metadata, f, indent=2, ensure_ascii=False)
|
|
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"保存元数据失败: {e}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def _get_or_create_software_id(self, software_name: str, software_id: str = None) -> str:
|
|
|
|
|
|
"""获取或创建软件ID"""
|
|
|
|
|
|
metadata = self._load_metadata()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果提供了软件ID,直接使用
|
|
|
|
|
|
if software_id:
|
|
|
|
|
|
return software_id
|
|
|
|
|
|
|
|
|
|
|
|
# 查找是否已经有该软件名的记录
|
|
|
|
|
|
for key, info in metadata.items():
|
|
|
|
|
|
if info.get('software_name') == software_name and info.get('software_id'):
|
|
|
|
|
|
print(f"✅ 找到已存在的软件ID: {info['software_id']}")
|
|
|
|
|
|
return info['software_id']
|
|
|
|
|
|
|
|
|
|
|
|
# 生成新的软件ID
|
|
|
|
|
|
new_id = str(uuid.uuid4())
|
|
|
|
|
|
print(f"🆕 生成新的软件ID: {new_id}")
|
|
|
|
|
|
return new_id
|
|
|
|
|
|
|
|
|
|
|
|
def _save_encryption_metadata(self, output_path: str, source_path: str,
|
|
|
|
|
|
software_name: str, software_id: str,
|
|
|
|
|
|
version: str, file_hash: str):
|
|
|
|
|
|
"""保存加密元数据"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
metadata = self._load_metadata()
|
|
|
|
|
|
|
|
|
|
|
|
# 生成唯一键
|
|
|
|
|
|
file_key = str(uuid.uuid4())
|
|
|
|
|
|
|
|
|
|
|
|
# 保存元数据
|
|
|
|
|
|
metadata[file_key] = {
|
|
|
|
|
|
'original_name': os.path.basename(source_path),
|
|
|
|
|
|
'encrypted_path': output_path,
|
|
|
|
|
|
'software_name': software_name,
|
|
|
|
|
|
'software_id': software_id,
|
|
|
|
|
|
'version': version,
|
|
|
|
|
|
'hash': file_hash,
|
|
|
|
|
|
'size': os.path.getsize(output_path),
|
|
|
|
|
|
'encryption_time': str(datetime.now()),
|
|
|
|
|
|
'source_path': source_path
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 保存到文件
|
|
|
|
|
|
if self._save_metadata(metadata):
|
|
|
|
|
|
print(f"✅ 元数据已保存")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"⚠️ 元数据保存失败")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"保存元数据时出错: {e}")
|
|
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
def encrypt_exe(self, source_path: str, output_path: str,
|
2025-10-25 17:49:09 +08:00
|
|
|
|
api_config: dict, software_name: str,
|
2025-10-28 13:18:45 +08:00
|
|
|
|
use_v2_validator: bool = True,
|
|
|
|
|
|
software_id: str = None,
|
|
|
|
|
|
version: str = "1.0.0") -> Tuple[bool, str]:
|
2025-10-23 18:28:10 +08:00
|
|
|
|
"""
|
2025-10-28 13:18:45 +08:00
|
|
|
|
加密EXE文件(使用V2改进版验证器)
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
source_path: 原始EXE路径
|
|
|
|
|
|
output_path: 输出路径
|
|
|
|
|
|
api_config: API配置(不包含数据库密码)
|
|
|
|
|
|
software_name: 软件名称
|
2025-10-28 13:18:45 +08:00
|
|
|
|
use_v2_validator: 保留参数(兼容性),现在统一使用V2验证器
|
|
|
|
|
|
software_id: 软件唯一ID(用于版本管理),为None则自动生成
|
|
|
|
|
|
version: 软件版本号,默认1.0.0
|
2025-10-23 18:28:10 +08:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
print(f"开始加密: {source_path}")
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 1. 检测是否已经是加密文件
|
|
|
|
|
|
if self._is_encrypted_file(source_path):
|
|
|
|
|
|
return False, "❌ 检测到这是一个已加密的文件!\n\n为避免多层验证,请使用原始未加密的EXE文件。\n\n如需更新软件版本:\n1. 使用原始新版本EXE文件\n2. 在加密时使用相同的软件ID\n3. 这样用户的激活码可以继续使用"
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 读取原始EXE
|
2025-10-23 18:28:10 +08:00
|
|
|
|
with open(source_path, 'rb') as f:
|
|
|
|
|
|
original_content = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
print(f"原始文件大小: {len(original_content)} 字节")
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 3. 获取或创建软件ID
|
|
|
|
|
|
actual_software_id = self._get_or_create_software_id(software_name, software_id)
|
|
|
|
|
|
print(f"软件ID: {actual_software_id}")
|
|
|
|
|
|
print(f"软件版本: {version}")
|
|
|
|
|
|
|
|
|
|
|
|
# 4. 生成加密密钥
|
2025-10-23 18:28:10 +08:00
|
|
|
|
self.encryption_key = self.generate_encryption_key()
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 5. 加密EXE内容
|
2025-10-23 18:28:10 +08:00
|
|
|
|
fernet = Fernet(self.encryption_key)
|
|
|
|
|
|
encrypted_content = fernet.encrypt(original_content)
|
|
|
|
|
|
|
|
|
|
|
|
print(f"加密后大小: {len(encrypted_content)} 字节")
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 6. 计算哈希
|
2025-10-23 18:28:10 +08:00
|
|
|
|
file_hash = hashlib.sha256(original_content).hexdigest()
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 7. 准备资源数据
|
2025-10-25 17:49:09 +08:00
|
|
|
|
key_part1 = base64.b64encode(self.encryption_key[:16]).decode()
|
|
|
|
|
|
key_part2 = base64.b64encode(self.encryption_key[16:]).decode()
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
resource_data = {
|
|
|
|
|
|
'data': base64.b64encode(encrypted_content).decode(),
|
|
|
|
|
|
'hash': file_hash,
|
2025-10-25 17:49:09 +08:00
|
|
|
|
'key_hint': key_part1,
|
|
|
|
|
|
'key_part2': key_part2,
|
2025-10-28 13:18:45 +08:00
|
|
|
|
'software_id': actual_software_id,
|
|
|
|
|
|
'version': version,
|
2025-10-23 18:28:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 6. 选择验证器模板(现在统一使用V2版本)
|
|
|
|
|
|
validator_template_path = "validator_secure.py"
|
2025-10-25 17:49:09 +08:00
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
if not os.path.exists(validator_template_path):
|
|
|
|
|
|
return False, f"找不到验证器模板: {validator_template_path}"
|
|
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
print(f"使用验证器模板: {validator_template_path}")
|
|
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
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)}'
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 替换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"]}"'
|
|
|
|
|
|
)
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
if 'api_key' in api_config:
|
|
|
|
|
|
final_code = final_code.replace(
|
|
|
|
|
|
'API_KEY = "taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224"',
|
|
|
|
|
|
f'API_KEY = "{api_config["api_key"]}"'
|
|
|
|
|
|
)
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 替换软件名称(保留用于显示)
|
2025-10-23 18:28:10 +08:00
|
|
|
|
final_code = final_code.replace(
|
|
|
|
|
|
'SOFTWARE_NAME = "SOFTWARE_NAME_PLACEHOLDER"',
|
|
|
|
|
|
f'SOFTWARE_NAME = "{software_name}"'
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 替换软件ID(用于验证)
|
|
|
|
|
|
final_code = final_code.replace(
|
|
|
|
|
|
'SOFTWARE_ID = "SOFTWARE_ID_PLACEHOLDER"',
|
|
|
|
|
|
f'SOFTWARE_ID = "{actual_software_id}"'
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
# 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"⚠️ 请妥善保管此文件,建议加密存储!")
|
2025-10-25 17:49:09 +08:00
|
|
|
|
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# 保存元数据
|
|
|
|
|
|
self._save_encryption_metadata(
|
|
|
|
|
|
output_path=output_path,
|
|
|
|
|
|
source_path=source_path,
|
|
|
|
|
|
software_name=software_name,
|
|
|
|
|
|
software_id=actual_software_id,
|
|
|
|
|
|
version=version,
|
|
|
|
|
|
file_hash=file_hash
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示V2验证器功能说明
|
|
|
|
|
|
print("\n" + "="*60)
|
|
|
|
|
|
print("✅ 使用V2验证器(支持版本管理),包含以下功能:")
|
|
|
|
|
|
print(" 1. 持久化缓存存储(激活一次,长期使用)")
|
|
|
|
|
|
print(" 2. 智能验证策略(减少重复验证)")
|
|
|
|
|
|
print(" 3. 到期提醒功能(提前7天开始提醒)")
|
|
|
|
|
|
print(" 4. 增强的离线验证(直到许可证过期)")
|
|
|
|
|
|
print(" 5. 改善的用户界面(显示剩余天数)")
|
|
|
|
|
|
print(" 6. 软件版本管理(更新软件不影响激活状态)")
|
|
|
|
|
|
print("="*60)
|
|
|
|
|
|
print(f"\n📋 软件信息:")
|
|
|
|
|
|
print(f" 软件名称: {software_name}")
|
|
|
|
|
|
print(f" 软件ID: {actual_software_id}")
|
|
|
|
|
|
print(f" 版本号: {version}")
|
|
|
|
|
|
print(f" 文件哈希: {file_hash[:16]}...")
|
|
|
|
|
|
print("\n💡 版本管理说明:")
|
|
|
|
|
|
print(f" • 软件ID是唯一标识,用于版本管理")
|
|
|
|
|
|
print(f" • 更新软件时请使用相同的软件ID")
|
|
|
|
|
|
print(f" • 这样用户的激活码可以继续在新版本上使用")
|
|
|
|
|
|
print("="*60 + "\n")
|
2025-10-25 17:49:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 清理临时文件
|
|
|
|
|
|
try:
|
|
|
|
|
|
os.remove(temp_py)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
return success, msg
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2025-10-25 17:49:09 +08:00
|
|
|
|
import traceback
|
|
|
|
|
|
error_detail = traceback.format_exc()
|
|
|
|
|
|
print(f"加密过程出错: {error_detail}")
|
2025-10-23 18:28:10 +08:00
|
|
|
|
return False, f"加密过程出错: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
def _compile_to_exe(self, python_file: str, output_path: str) -> Tuple[bool, str]:
|
2025-10-25 17:49:09 +08:00
|
|
|
|
"""编译Python文件为EXE"""
|
2025-10-23 18:28:10 +08:00
|
|
|
|
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}")
|
|
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 2. 创建工作目录
|
2025-10-23 18:28:10 +08:00
|
|
|
|
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}")
|
|
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 4. 构建PyInstaller命令
|
2025-10-23 18:28:10 +08:00
|
|
|
|
exe_name = os.path.splitext(os.path.basename(output_path))[0]
|
|
|
|
|
|
|
|
|
|
|
|
cmd = [
|
|
|
|
|
|
'pyinstaller',
|
2025-10-25 17:49:09 +08:00
|
|
|
|
'--clean',
|
|
|
|
|
|
'--noconfirm',
|
|
|
|
|
|
'--onefile',
|
|
|
|
|
|
'--windowed',
|
|
|
|
|
|
'--name', exe_name,
|
|
|
|
|
|
'--distpath', build_dir,
|
|
|
|
|
|
'--workpath', os.path.join(build_dir, 'build'),
|
|
|
|
|
|
'--specpath', build_dir,
|
2025-10-23 18:28:10 +08:00
|
|
|
|
# 添加隐藏导入
|
2025-10-28 13:18:45 +08:00
|
|
|
|
'--hidden-import', 'io',
|
2025-10-23 18:28:10 +08:00
|
|
|
|
'--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',
|
2025-10-25 17:49:09 +08:00
|
|
|
|
'--hidden-import', 'sqlite3',
|
2025-10-23 18:28:10 +08:00
|
|
|
|
# 收集所有cryptography包
|
|
|
|
|
|
'--collect-all', 'cryptography',
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 禁用UPX
|
2025-10-23 18:28:10 +08:00
|
|
|
|
'--noupx',
|
|
|
|
|
|
work_py
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
print("开始编译...")
|
|
|
|
|
|
print(f"命令: {' '.join(cmd)}")
|
|
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 5. 执行编译
|
2025-10-23 18:28:10 +08:00
|
|
|
|
result = subprocess.run(
|
|
|
|
|
|
cmd,
|
|
|
|
|
|
capture_output=True,
|
|
|
|
|
|
text=True,
|
2025-10-25 17:49:09 +08:00
|
|
|
|
timeout=600,
|
2025-10-23 18:28:10 +08:00
|
|
|
|
cwd=build_dir,
|
|
|
|
|
|
encoding='utf-8',
|
|
|
|
|
|
errors='replace'
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 6. 输出详细日志
|
|
|
|
|
|
if result.stdout:
|
|
|
|
|
|
print("=== 编译输出 ===")
|
2025-10-25 17:49:09 +08:00
|
|
|
|
print(result.stdout[-2000:])
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
if result.stderr:
|
|
|
|
|
|
print("=== 编译错误 ===")
|
2025-10-25 17:49:09 +08:00
|
|
|
|
print(result.stderr[-2000:])
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
# 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:
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 清理临时文件(如果编译失败)
|
2025-10-23 18:28:10 +08:00
|
|
|
|
if build_dir and os.path.exists(build_dir):
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not os.path.exists(output_path):
|
|
|
|
|
|
print(f"⚠️ 编译失败,工作目录已保留用于调试: {build_dir}")
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2025-10-25 17:49:09 +08:00
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
|
|
|
|
|
# ========== 测试代码 ==========
|
|
|
|
|
|
if __name__ == '__main__':
|
2025-10-28 13:18:45 +08:00
|
|
|
|
print("EXE加密器 (V2改进版) - 测试")
|
2025-10-25 17:49:09 +08:00
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
2025-10-23 18:28:10 +08:00
|
|
|
|
# 测试加密
|
|
|
|
|
|
encryptor = SecureEXEEncryptor()
|
|
|
|
|
|
|
|
|
|
|
|
# 配置
|
|
|
|
|
|
api_config = {
|
2025-10-25 17:49:09 +08:00
|
|
|
|
'api_url': 'http://localhost:5100/',
|
|
|
|
|
|
'api_key': 'taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224taiyi1224'
|
2025-10-23 18:28:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# 示例:加密一个测试EXE
|
|
|
|
|
|
# success, msg = encryptor.encrypt_exe(
|
|
|
|
|
|
# source_path='test.exe',
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# output_path='test_encrypted.exe',
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# api_config=api_config,
|
2025-10-28 13:18:45 +08:00
|
|
|
|
# software_name='TestApp'
|
2025-10-25 17:49:09 +08:00
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
|
|
# print(f"\n结果: {success}")
|
|
|
|
|
|
# print(f"消息: {msg}")
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|
2025-10-25 17:49:09 +08:00
|
|
|
|
print("\n使用说明:")
|
|
|
|
|
|
print("1. 在main.py中使用此加密器")
|
2025-10-28 13:18:45 +08:00
|
|
|
|
print("2. 现在默认使用V2验证器(改进版)")
|
|
|
|
|
|
print("3. V2功能:持久化缓存、到期提醒、智能验证策略")
|
2025-10-23 18:28:10 +08:00
|
|
|
|
|