modify: pyarmor 加密支持

This commit is contained in:
2026-05-04 19:06:51 +08:00
parent 0388984080
commit 1fdb5bd1bf
17 changed files with 1418 additions and 90 deletions
+295
View File
@@ -0,0 +1,295 @@
"""
集成测试:测试 PyArmor 加密打包 -> 存储 -> 加载 -> 执行 全流程
"""
import sys
import os
import tempfile
import shutil
# 添加项目模块路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
TEST_DB = os.path.join(os.path.dirname(__file__), "test_pyarmor_integration.db")
def create_test_module():
"""创建测试用的 Python 模块"""
return """
def greet(name):
return f"Hello, {name}! This is a PyArmor protected module."
class Calculator:
def add(self, a, b):
return a + b
def multiply(self, a, b):
return a * b
VERSION = "1.0.0"
"""
def test_pyarmor_obfuscate():
"""测试 PyArmor 混淆功能"""
print("=" * 60)
print("测试 1: PyArmor 混淆功能")
print("=" * 60)
from module_bank.pyarmor_encryption import PyArmorEncryption
enc = PyArmorEncryption(obf_code=1, obf_mod=1)
# 检查 PyArmor 是否可用
if not enc.check_pyarmor_available():
print("ERROR: PyArmor 未安装或不可用")
return False
source = create_test_module()
print(f"原始代码长度: {len(source)} 字节")
try:
obfuscated_code, runtime_data = enc.obfuscate_source(
source, "test_protected_module"
)
print(f"混淆后代码长度: {len(obfuscated_code)} 字节")
print(f"运行时文件: {'有' if runtime_data else '无'}")
print(f"运行时数据大小: {len(runtime_data) if runtime_data else 0} 字节")
# 验证是否被保护
assert enc.is_pyarmor_protected(obfuscated_code), "代码应被标记为 PyArmor 保护"
print(f"PyArmor 保护标记: 正确")
# 提取运行时模块名
runtime_name = enc.extract_runtime_info(obfuscated_code)
print(f"运行时模块名: {runtime_name}")
print("测试 1 通过!\n")
return True
except Exception as e:
print(f"ERROR: {e}")
import traceback
traceback.print_exc()
return False
def test_pack_with_pyarmor():
"""测试使用 PyArmor 打包到 SQLite"""
print("=" * 60)
print("测试 2: 使用 PyArmor 打包到 SQLite")
print("=" * 60)
# 清理旧的测试数据库
if os.path.exists(TEST_DB):
os.remove(TEST_DB)
try:
from module_bank.python_to_sqlite import PythonToSQLite
# 使用 PyArmor 创建打包器
packer = PythonToSQLite(
TEST_DB,
pyarmor_obfuscate=True,
pyarmor_obf_code=1,
pyarmor_obf_mod=1,
)
source = create_test_module()
packer.importer.add_module("test_protected_module", source, is_package=False)
print("模块已打包到数据库")
# 验证模块已存储
modules = packer.list_modules()
module_names = [m["module_name"] for m in modules]
assert "test_protected_module" in module_names, "模块应在数据库中存在"
print(f"数据库中的模块: {module_names}")
# 验证运行时已存储
runtimes = packer.importer.list_runtimes()
print(f"数据库中的运行时: {[r['runtime_name'] for r in runtimes]}")
# 验证 pyarmor_protected 标记
cursor = packer.importer.connection.execute(
"SELECT pyarmor_protected FROM python_modules WHERE module_name = ?",
("test_protected_module",),
)
row = cursor.fetchone()
assert row["pyarmor_protected"] == 1, "模块应标记为 pyarmor_protected"
print("PyArmor 保护标记: 已正确设置")
print("测试 2 通过!\n")
return True
except Exception as e:
print(f"ERROR: {e}")
import traceback
traceback.print_exc()
return False
def test_import_pyarmor_module():
"""测试从 SQLite 导入并执行 PyArmor 保护的模块"""
print("=" * 60)
print("测试 3: 从 SQLite 导入并执行 PyArmor 保护模块")
print("=" * 60)
try:
from module_bank.python_to_sqlite import PythonToSQLite
from module_bank.sqlite_meta_path_finder import SQLiteMetaPathFinder
packer = PythonToSQLite(TEST_DB)
# 安装导入器
finder = packer.install_importer()
# 尝试导入模块
import importlib
mod = importlib.import_module("test_protected_module")
print(f"模块导入成功: {mod}")
print(f"模块名: {mod.__name__}")
print(f"模块文件: {mod.__file__}")
# 调用模块函数
result = mod.greet("World")
print(f"调用 greet('World'): {result}")
assert result == "Hello, World! This is a PyArmor protected module."
# 使用模块类
calc = mod.Calculator()
assert calc.add(3, 4) == 7
assert calc.multiply(3, 4) == 12
print(f"Calculator.add(3, 4) = {calc.add(3, 4)}")
print(f"Calculator.multiply(3, 4) = {calc.multiply(3, 4)}")
# 访问模块变量
print(f"VERSION = {mod.VERSION}")
assert mod.VERSION == "1.0.0"
print("测试 3 通过!\n")
# 清理
sys.meta_path.remove(finder)
if "test_protected_module" in sys.modules:
del sys.modules["test_protected_module"]
return True
except Exception as e:
print(f"ERROR: {e}")
import traceback
traceback.print_exc()
return False
def test_cli_with_pyarmor():
"""测试 CLI 的 PyArmor 参数"""
print("=" * 60)
print("测试 4: CLI --pyarmor 参数")
print("=" * 60)
try:
import tempfile
# 创建临时源文件
tmpdir = tempfile.mkdtemp()
src_file = os.path.join(tmpdir, "hello_module.py")
with open(src_file, "w", encoding="utf-8") as f:
f.write("def hello():\n return 'Hello from PyArmor CLI!'\n")
db_path = os.path.join(tmpdir, "test_cli.db")
# 测试解析 --pyarmor 参数
from module_bank.cli import main as cli_main
# 保存原始argv
old_argv = sys.argv
try:
sys.argv = [
"mbank",
"pack",
src_file,
"--db",
db_path,
"--name",
"hello_cli_module",
"--pyarmor",
]
cli_main()
print("CLI pack with --pyarmor 执行成功")
# 验证模块已用 PyArmor 保护
from module_bank.sqlite_module_importer import SQLiteModuleImporter
importer = SQLiteModuleImporter(db_path)
cursor = importer.connection.execute(
"SELECT pyarmor_protected FROM python_modules WHERE module_name = ?",
("hello_cli_module",),
)
row = cursor.fetchone()
assert row and row["pyarmor_protected"] == 1, "CLI 打包的模块应启用 PyArmor"
print("CLI 打包的模块已正确启用 PyArmor 保护")
# 验证运行时存储
runtimes = importer.list_runtimes()
assert len(runtimes) > 0, "CLI 打包后应有运行时文件存储在数据库中"
print(f"运行时已存储: {[r['runtime_name'] for r in runtimes]}")
finally:
sys.argv = old_argv
shutil.rmtree(tmpdir, ignore_errors=True)
print("测试 4 通过!\n")
return True
except Exception as e:
print(f"ERROR: {e}")
import traceback
traceback.print_exc()
return False
def cleanup():
"""清理测试文件"""
if os.path.exists(TEST_DB):
os.remove(TEST_DB)
print(f"已清理测试数据库: {TEST_DB}")
if __name__ == "__main__":
print("\n" + "=" * 60)
print("PyArmor 集成测试套件")
print("=" * 60 + "\n")
tests = [
("PyArmor 混淆功能", test_pyarmor_obfuscate),
("PyArmor 打包到 SQLite", test_pack_with_pyarmor),
("从 SQLite 导入 PyArmor 模块", test_import_pyarmor_module),
("CLI --pyarmor 参数", test_cli_with_pyarmor),
]
passed = 0
failed = 0
for name, func in tests:
print(f"\n>>> 运行测试: {name}")
try:
if func():
passed += 1
else:
failed += 1
except Exception as e:
print(f"测试异常: {e}")
import traceback
traceback.print_exc()
failed += 1
cleanup()
print("\n" + "=" * 60)
print(f"测试完成: {passed} 通过, {failed} 失败")
print("=" * 60)
sys.exit(1 if failed > 0 else 0)
@@ -0,0 +1,2 @@
# Pyarmor 9.2.4 (trial), 000000, 2026-05-04T17:52:33.319452
from .pyarmor_runtime import __pyarmor__
File diff suppressed because one or more lines are too long
+53
View File
@@ -0,0 +1,53 @@
"""测试 PyArmor 加密功能"""
import tempfile
import os
import sys
import shutil
# 确保能导入项目模块
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
def test_pyarmor_encrypt():
"""测试 PyArmor encrypt_script 功能"""
import pyarmor.pyarmor as pa
tmpdir = tempfile.mkdtemp()
try:
src = os.path.join(tmpdir, "test_mod.py")
with open(src, "w", encoding="utf-8") as f:
f.write('def hello():\n print("Hello from PyArmor!")\n')
dest = os.path.join(tmpdir, "dist", "test_mod.py")
os.makedirs(os.path.dirname(dest), exist_ok=True)
# Check capsule
capsule = pa.DEFAULT_CAPSULE
print(f"Capsule: {capsule}")
print(f"Capsule exists: {os.path.exists(capsule)}")
# Try encrypt
result = pa.encrypt_script(
pubkey=capsule,
filename=src,
destname=dest,
)
print(f"Result: {result}")
# Check output
if os.path.exists(dest):
with open(dest, "r", encoding="utf-8") as f:
content = f.read()
print(f"Output length: {len(content)} bytes")
print(f"First 300 chars:\n{content[:300]}")
print(f"\n--- Full content ({len(content)} bytes) ---")
print(content)
else:
print(f"ERROR: Output file not created at {dest}")
finally:
shutil.rmtree(tmpdir, ignore_errors=True)
if __name__ == "__main__":
test_pyarmor_encrypt()