使用 Cython 对 python 代码加密打包
目前我在使用 cx_Freeze 对 python 程序打包成可执行文件,但是 cx_Freeze 的核心功能是将 Python 脚本、Python 解释器以及所有依赖的库文件打包到一个独立的可执行文件(如 Windows 下的 .exe 文件)或一个包含所有文件的目录中。打包后的文件中包含的是 Python 的字节码 .pyc
文件,这个文件是可以被反编译回近似的源代码的。
通过使用 Cython 将 Python 源代码编译成 C 语言,然后再生成本地二进制文件(.pyd)。然后正常使用 cx_Freeze 打包,这样做可以极大地提高代码的保护级别,防止被轻易逆向。
安装必要的 Python 包
需要安装 Cython 和 Numpy,在终端或命令行中运行:
pip install Cython numpy
安装 C/C++ 编译器
Cython 将 Python 代码转换成 C 代码,但最终需要一个 C 编译器来将 C 代码编译成机器码。这是最关键的一步。
对于 Windows 用户:
- 访问 Visual Studio 下载页面:https://visualstudio.microsoft.com/zh-hans/downloads/。
- 在 "Tools for Visual Studio" (所有下载 -> Visual Studio 工具) 中找到并下载 "Build Tools for Visual Studio"。
- 运行安装程序,在 "工作负荷" 标签页中,勾选 "使用 C++ 的桌面开发"。
- 点击安装。安装完成后,您可能需要重启电脑。
对于 macOS 用户:
打开终端并运行 xcode-select --install
。这会安装苹果的命令行开发者工具,其中包含了 Clang 编译器。
对于 Linux 用户 (例如 Ubuntu/Debian):
打开终端并运行 sudo apt update && sudo apt install build-essential
。
修改 setup.py 文件
如果项目目录结构如下:
/my_project
|-- main.py # 你的主程序文件
|-- /src # 你的其他模块目录
| |-- func1.py
| |-- func2.py
|-- setup.py # cx_Freeze 的配置文件
根据以上目录结构,下面是一个配置文件示例:
from cx_Freeze import setup, Executable
import sys, os, io
# =============================================================================
# Cython 自动化编译集成
# =============================================================================
try:
from Cython.Build import cythonize
from setuptools import Extension
import numpy
except ImportError:
print("\n[错误] 缺少必要的库。请先安装 Cython 和 Numpy:")
print("pip install Cython numpy")
sys.exit(1)
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') # 修复非中文系统打包时报错
# 增加递归调用深度限制
sys.setrecursionlimit(1500)
# 定义相关路径
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
ENTRY_POINT = "main.py"
# 检查是否为打包命令
build_commands = {"build", "bdist_msi", "bdist_dmg", "bdist_mac"}
is_building = any(cmd in sys.argv for cmd in build_commands)
# --- Cython 编译配置 ---
# 此函数会自动查找 src 目录下的所有 .py 文件并准备将它们编译
def find_extensions_to_compile(dir_path="src"):
"""Find all .py files to be compiled by Cython."""
extensions = []
# 添加 numpy 的头文件路径,这对于编译依赖 numpy/scipy 的代码至关重要
numpy_include = numpy.get_include()
for root, dirs, files in os.walk(dir_path):
for file in files:
# 我们只编译 .py 文件,但跳过 __init__.py 文件
if file.endswith(".py") and file != "__init__.py":
path = os.path.join(root, file)
# 将文件路径转换为模块路径,例如 "src/library/function.py" -> "src.library.function"
module_path = path.replace(os.sep, '.')[:-3]
extensions.append(
Extension(
name=module_path,
sources=[path],
include_dirs=[numpy_include] # 包含 numpy 头文件
)
)
print(f"--- 找到 {len(extensions)} 个模块准备通过 Cython 编译...")
return extensions
# 仅在执行打包命令时才准备编译列表
extensions = []
if is_building:
# 1. 编译 src 目录下的所有模块
extensions.extend(find_extensions_to_compile("src"))
# 2. 明确地将 main.py 添加到编译列表
print(f"--- 添加入口文件 '{ENTRY_POINT}' 到编译列表...")
main_extension = Extension(
name="main", # 编译后的模块名
sources=[ENTRY_POINT],
include_dirs=[numpy.get_include()]
)
extensions.append(main_extension)
# =============================================================================
# cx_Freeze 配置
# =============================================================================
# 安装依赖
build_exe_options = {
"packages": [
],
"excludes": ["email"] + [ext.name for ext in extensions], # 排除 Cython 编译的模块
"include_files": [
],
"includes": [],
# 性能优化选项
"optimize": 2, # 使用Python优化
"include_msvcr": False, # 不包含MSVC运行库
}
# 基础设置
base = "Win32GUI" if sys.platform == "win32" else None
directory_table = [
# ...
]
shortcut_table = [
(
# ...
),
(
# ...
),
]
msi_data = {"Directory": directory_table, "Shortcut": shortcut_table}
bdist_msi_options = {
# ...
}
executables = [
Executable(
"main.py", # 入口文件 依然调用 py 程序,cx_Freeze 会自动识别并使用加密后的文件
# ...
)
]
# =============================================================================
# 清理函数
# =============================================================================
def cleanup_generated_files():
"""查找并删除由 Cython 生成的所有 .c 文件。"""
print("\n--- 正在运行清理程序:删除生成的 C 文件... ---")
for root, dirs, files in os.walk(ROOT_DIR):
# 避免进入不相关的目录
if 'myenv' in root or '.git' in root or 'build' in root or 'dist' in root:
continue
for file in files:
if file.endswith('.c'):
file_path = os.path.join(root, file)
try:
os.remove(file_path)
print(f"--- 已删除: {file_path}")
except OSError as e:
print(f"--- 删除失败 {file_path}: {e}")
# =============================================================================
# 执行打包
# =============================================================================
try:
setup(
# ...
# 关键步骤:将找到的 .py 文件交给 Cythonize 进行编译
ext_modules=cythonize(
extensions,
compiler_directives={'language_level': "3"}, # 使用 Python 3 语法
quiet=True # 减少不必要的编译输出
) if is_building else [],
# ...
)
finally:
# 只有在执行打包命令时才运行清理
if is_building:
cleanup_generated_files()
运行打包命令打包即可,如:
python setup.py bdist_msi
检查加密情况
安装完成后,进入安装路径的 Lib - site-packages 文件夹,会看到加密后的 .pyd
程序文件。.pyd
文件是 Windows 上的二进制动态链接库,本质上和 .dll 文件一样。如果加密失败:您会在这里看到 .pyc
文件或者甚至原始的 .py
文件。
标签:无