作为资深开发者,我们有时需要保护部分核心代码不以明文形式部署。Python 强大的导入系统(Import System)为我们提供了实现此目的的手段:自定义导入钩子(Import Hook)。通过向 sys.meta_path 注入自定义的 Finder 和 Loader,我们可以拦截特定的模块导入请求,并在加载时对其进行解密。
本文将通过一个简单的 XOR 加密示例,展示如何构建一个自定义的导入机制,实现代码的加密加载。
核心概念
Python 3.3+ 的导入机制主要依赖两个抽象类:
- Meta Path Finder(查找器): 实现 find_spec(fullname, path, target=None) 方法。它负责在 sys.meta_path 中被调用,确定如何导入模块,并返回一个 ModuleSpec 对象。
- Loader(加载器): 由 ModuleSpec 携带,负责实际执行代码。必须实现 exec_module(module) 方法,将模块的内容(此时应为解密后的代码)编译并执行到模块命名空间中。
第一步:准备加密模块文件
我们首先定义一个简单的 XOR 加密/解密函数,并使用它来加密我们的目标模块文件。
创建文件 secret_module_source.py:
# secret_module_source.py - 原始代码
def get_secret_message():
return "This message was successfully decrypted during import!"
print("--- Secret module loaded via custom hook ---")
创建加密工具 encrypt_tool.py 来生成加密文件 secret_module.enc:
# encrypt_tool.py
# 统一使用的密钥
KEY = 42
def simple_xor_encrypt(data, key):
return bytes([b ^ key for b in data])
# 读取明文代码
with open('secret_module_source.py', 'rb') as f:
source_data = f.read()
# 加密并写入目标文件
encrypted_data = simple_xor_encrypt(source_data, KEY)
with open('secret_module.enc', 'wb') as f:
f.write(encrypted_data)
print("Encryption complete: secret_module.enc created.")
# 注意:在实际部署中,你需要移除 secret_module_source.py 文件。
运行 python encrypt_tool.py。
第二步:实现自定义 Finder 和 Loader
现在我们创建主程序文件 main.py,其中包含 Finder、Loader 的定义以及 Hook 的注册。
# main.py
import sys
import importlib.abc
import importlib.util
# 密钥必须与加密时一致
KEY = 42
TARGET_MODULE_NAME = "secret_module"
ENCRYPTED_FILE = "secret_module.enc"
def simple_xor_decrypt(data, key):
return bytes([b ^ key for b in data])
class EncryptedLoader(importlib.abc.Loader):
"""负责解密并执行模块代码"""
def __init__(self, data):
self.encrypted_data = data
def create_module(self, spec):
# 允许系统默认创建模块对象
return None
def exec_module(self, module):
# 1. 解密数据
decrypted_code_bytes = simple_xor_decrypt(self.encrypted_data, KEY)
# 2. 将字节转换为字符串
decrypted_source = decrypted_code_bytes.decode('utf-8')
# 3. 编译代码对象
code = compile(
decrypted_source,
module.__spec__.origin,
'exec'
)
# 4. 在模块的命名空间中执行编译后的代码
exec(code, module.__dict__)
class EncryptedFinder(importlib.abc.MetaPathFinder):
"""负责查找特定模块文件,并提供定制化的 Loader"""
def find_spec(self, fullname, path, target=None):
if fullname != TARGET_MODULE_NAME:
return None
try:
with open(ENCRYPTED_FILE, 'rb') as f:
encrypted_data = f.read()
# 创建加载器实例,传入加密数据
loader = EncryptedLoader(encrypted_data)
# 创建模块规范 (ModuleSpec)
spec = importlib.util.spec_from_loader(fullname, loader)
# 标记来源,方便调试 (可选)
spec.origin = f'encrypted_source:{ENCRYPTED_FILE}'
return spec
except FileNotFoundError:
print(f"Warning: Encrypted file {ENCRYPTED_FILE} not found.")
return None
# --- 注册 Import Hook ---
# 插入到 sys.meta_path 的最前面,确保它优先于默认导入机制被检查
sys.meta_path.insert(0, EncryptedFinder())
print("Custom Encrypted Import Hook registered.")
# --- 导入测试 ---
# 尝试导入加密的模块
import secret_module
# 调用模块中的函数
message = secret_module.get_secret_message()
print(f"Received message: {message}")
第三步:运行验证
确保你已经运行了 encrypt_tool.py 生成了 secret_module.enc。
运行 python main.py,你将看到如下输出:
Custom Encrypted Import Hook registered.
--- Secret module loaded via custom hook ---
Received message: This message was successfully decrypted during import!
这证明了在 Python 执行 import secret_module 时,系统成功调用了我们自定义的 EncryptedFinder,并由 EncryptedLoader 负责在内存中解密、编译和执行了代码,而硬盘上的文件始终保持加密状态。
汤不热吧