Exeprotector/encryptor.py
2025-08-08 18:28:46 +08:00

310 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import shutil
import subprocess
import tempfile
import json
import struct
import hashlib
import zlib
from typing import Tuple, Optional
import time
class EXEEncryptor:
"""EXE文件加密器类 - 提供EXE文件加密、编译和验证功能"""
def __init__(self):
"""初始化加密器"""
pass
# encryptor.py
def encrypt_file(self, source_path: str, output_path: str, validator_path: str, db_config: dict) -> Tuple[
bool, str]:
"""重新实现将原始exe作为资源嵌入验证器exe中"""
try:
# 读取原始exe内容作为资源
with open(source_path, 'rb') as f:
original_content = f.read()
# 计算原始exe的哈希用于验证完整性
file_hash = hashlib.sha256(original_content).hexdigest()
# 创建资源数据原始exe + 配置信息)
resource_data = {
'original_exe': original_content.hex(), # 十六进制字符串
'original_size': len(original_content),
'file_hash': file_hash,
'db_config': db_config,
'encryption_time': int(time.time())
}
# 将资源数据写入Python文件作为字符串常量
resource_code = json.dumps(resource_data, separators=(',', ':'))
# 读取验证器模板validator_wrapper.py
# with open(validator_path, 'r', encoding='utf-8') as f:
# 读取验证器模板
with open(validator_path, 'r', encoding='utf-8', errors='ignore') as f:
validator_template = f.read()
# 构造资源数据(确保是合法 JSON
resource_data = {
'original_exe': original_content.hex(),
'original_size': len(original_content),
'file_hash': file_hash,
'db_config': db_config,
}
# 插入资源数据到验证器模板中
final_validator_code = validator_template.replace(
'# RESOURCE_DATA_PLACEHOLDER\nRESOURCE_DATA = None',
f'RESOURCE_DATA = {json.dumps(resource_data)}'
)
print("插入后的代码片段:")
print(final_validator_code[:500]) # 打印前500字符
# 写入临时文件
temp_validator_py = os.path.join(tempfile.gettempdir(), 'temp_validator.py')
with open(temp_validator_py, 'w', encoding='utf-8') as f:
f.write(final_validator_code)
# 调试:检查是否插入成功
with open(temp_validator_py, 'r', encoding='utf-8') as f:
content = f.read()
if 'RESOURCE_DATA = {"original_exe"' in content:
print("✅ 资源数据已成功插入")
else:
print("❌ 资源数据未插入,请检查替换逻辑")
# 使用PyInstaller将验证器编译为exe
success, msg = self.compile_to_exe(temp_validator_py, output_path)
if not success:
return False, msg
# 清理临时文件
if os.path.exists(temp_validator_py):
os.remove(temp_validator_py)
return True, f"壳程序编译成功:{output_path}"
except Exception as e:
return False, f"套壳失败:{str(e)}"
def _create_encryption_header(self, original_size: int, db_config: dict, file_hash: str) -> bytes:
"""创建加密文件头信息"""
header_data = {
'version': '2.0',
'original_size': original_size,
'encryption_time': int(time.time()),
'compression_level': 6,
'file_hash': file_hash,
'db_config': db_config
}
return json.dumps(header_data).encode('utf-8').ljust(256, b'\x00')
def _simple_encrypt(self, data: bytes) -> bytes:
"""简化版XOR加密"""
key = b'EXEProtector#2024'
encrypted = bytearray(data)
key_len = len(key)
for i in range(len(encrypted)):
encrypted[i] ^= key[i % key_len]
return bytes(encrypted)
def compile_to_exe(self, python_file: str, output_path: str = None) -> Tuple[bool, str]:
"""尝试将Python文件编译为EXE"""
try:
# 确保Python文件存在
py_file = python_file.replace('.exe', '.py')
if not os.path.exists(py_file):
return False, f"Python源文件不存在: {py_file}"
print(f"Debug: 开始编译 {py_file}{python_file}")
# 检查PyInstaller
try:
result = subprocess.run(['pyinstaller', '--version'],
capture_output=True, text=True, timeout=10)
if result.returncode != 0:
return self._fallback_to_python_file(py_file, python_file)
except (subprocess.TimeoutExpired, FileNotFoundError):
return self._fallback_to_python_file(py_file, python_file)
# 获取文件信息
file_dir = os.path.dirname(os.path.abspath(py_file))
exe_name = os.path.basename(python_file).replace('.exe', '')
# 切换到文件目录
original_cwd = os.getcwd()
os.chdir(file_dir)
try:
# 创建规范文件内容
spec_content = self._create_spec_content(py_file, exe_name)
spec_file = f'{exe_name}.spec'
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"Debug: 创建规范文件: {spec_file}")
# 使用规范文件编译
cmd = ['pyinstaller', '--clean', '--noconfirm', spec_file]
print(f"Debug: 执行编译命令: {' '.join(cmd)}")
# 执行编译
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode == 0:
# 检查输出文件
output_exe = os.path.join('dist', f'{exe_name}.exe')
if os.path.exists(output_exe):
# 移动到目标位置
target_path = os.path.basename(python_file)
final_output = output_path or os.path.basename(python_file).replace('.py', '.exe')
os.makedirs(os.path.dirname(final_output), exist_ok=True)
if os.path.exists(final_output):
os.remove(final_output)
shutil.move(output_exe, final_output)
print(f"Debug: 编译成功,输出文件: {final_output}")
return True, "EXE编译成功"
else:
print(f"Debug: 找不到输出文件: {output_exe}")
return False, "编译完成但找不到输出文件"
else:
# 编译失败保留Python文件
error_info = result.stderr[:500] if result.stderr else result.stdout[:500]
print(f"Debug: 编译失败: {error_info}")
return self._fallback_to_python_file(py_file, python_file, f"编译失败: {error_info}")
finally:
os.chdir(original_cwd)
except subprocess.TimeoutExpired:
print("Debug: 编译超时")
return self._fallback_to_python_file(py_file, python_file, "编译超时")
except Exception as e:
print(f"Debug: 编译过程异常: {e}")
return False, f"编译过程异常: {str(e)}"
def _create_spec_content(self, py_file: str, exe_name: str) -> str:
"""创建PyInstaller规范文件内容"""
return f'''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['{os.path.basename(py_file)}'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
'mysql.connector',
'cryptography.fernet',
'tkinter',
'tkinter.messagebox',
'tkinter.scrolledtext',
'hashlib',
'subprocess',
'tempfile',
'threading',
'atexit',
'psutil',
'ctypes',
'win32api',
'win32con',
'win32security',
'win32process',
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='{exe_name}',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
uac_admin=False,
uac_uiaccess=False,
)
'''
def _fallback_to_python_file(self, py_file: str, target_file: str,
reason: str = "PyInstaller不可用") -> Tuple[bool, str]:
"""回退到Python文件"""
try:
# 将.exe改为.py
python_target = target_file.replace('.exe', '.py')
if py_file != python_target:
if os.path.exists(python_target):
os.remove(python_target)
shutil.copy2(py_file, python_target)
message = f"{reason}使用Python文件: {python_target}"
print(f"Debug: {message}")
return True, message
except Exception as e:
error_msg = f"回退失败: {str(e)}"
print(f"Debug: {error_msg}")
return False, error_msg
def _cleanup_build_files(self, exe_name: str):
"""清理编译产生的临时文件"""
try:
# 删除build目录
if os.path.exists('build'):
shutil.rmtree('build', ignore_errors=True)
# 删除dist目录
if os.path.exists('dist'):
shutil.rmtree('dist', ignore_errors=True)
# 删除spec文件
spec_file = f'{exe_name}.spec'
if os.path.exists(spec_file):
os.remove(spec_file)
print("Debug: 清理临时文件完成")
except Exception as e:
print(f"Debug: 清理临时文件时出错: {e}")