Python项目进行加密打包的实践指南

 更新时间:2026年01月15日 09:36:03   作者:@fai  
在实际开发中,我们有时需要将 Python 项目交付给客户或部署到不受控环境中,但又不希望源代码被轻易查看甚至篡改,所以本文将基于一个自研的 Encryptor 工具类,分享如何实现一个轻量级、可扩展的 Python 项目加密打包方案,需要的朋友可以参考下

引言

在实际开发中,我们有时需要将 Python 项目交付给客户或部署到不受控环境中,但又不希望源代码被轻易查看甚至篡改。虽然 Python 作为解释型语言天然难以完全隐藏逻辑,但我们仍可通过编译为字节码 + 自定义加密 + 运行时解密执行的方式,大幅提升逆向分析的门槛。

本文将基于一个自研的 Encryptor 工具类,分享如何实现一个轻量级、可扩展的 Python 项目加密打包方案。

整体思路

我们的目标是:

  1. 保留原有项目结构(包括子目录、.py.pyw 文件);
  2. 将每个 Python 源文件编译为字节码并加密
  3. 生成一个“运行时”模块,用于在运行时动态解密并执行加密后的字节码;
  4. 输出一个独立的加密项目副本,无需原始源码即可运行。

整个流程如下:

原始项目 (testproject/)
├── main.py
├── utils/
│   └── helper.py
└── ...

↓ Encryptor.encryptProject()

加密项目 (testproject-dist/)
├── main.py        ← 内容变为: from __runtime__ import __run__; __run__(b'...', globals())
├── utils/
│   └── helper.py  ← 同上
└── __runtime__/   ← 包含解密和执行逻辑

一、准备工作

由于我们需要在运行时动态解密并执行加密后的字节码,但是又不想暴露解密算法,那么需要将解密函数单独编译为.pyd或者其他(.so/.dll)机器码文件,这里给出示例:

# runtime.py
def __run__(en_code: bytes, globals_: dict):
    import struct
    import marshal

    def decrypt(en_code: bytes) -> bytes:
        en_code, offset_bytes = en_code[:-4], en_code[-4:]  # 去除偏移量
        insert_offset = struct.unpack("<I", offset_bytes)[0]
        key = en_code[insert_offset : insert_offset + 64]  # 提取key
        en_code = en_code[:insert_offset] + en_code[insert_offset + 64 :]  # 去除key
        # 解密
        de_code = bytes([en_code[i] ^ key[i % len(key)] for i in range(len(en_code))])
        return de_code

    code = marshal.loads(decrypt(en_code))
    exec(code, globals_, globals_)

完成编译后,新建runtime文件夹,将pyd文件(如runtime.cp312-win_amd64.pyd)放入,并新建__init.py__文件,输入一行代码:

from .runtime import __run__ 

python解释器会根据目前设备自动选择runtime.cp312-win_amd64.pyd,所以并不需要更改文件名。

二、核心实现解析

1. 密钥生成:动态随机 + SHA3-512

def __generateKey(self) -> bytes:
    key = "".join([chr(randint(33, 126)) for _ in range(64)])
    return hashlib.sha3_512(key.encode("utf-8")).digest()
  • 每次加密一个文件时,都会独立生成一个 64 字节的随机密钥
  • 使用 sha3_512 哈希确保密钥分布均匀且不可预测;
  • 注意:密钥不会保存在外部,而是直接嵌入加密数据中(见下文)。

安全提示:此设计便于单文件自包含,但若攻击者能提取密钥,则可解密。适用于防普通用户查看,非高强度安全场景。

2. 加密流程:字节码 + 异或 + 随机插入密钥

code = compile(..., "exec")
bytecode = marshal.dumps(code)
en_code = bytes([bytecode[i] ^ key[i % len(key)] for i in range(len(bytecode))])
insert_offset = randint(0, len(en_code) - 1)
en_code = en_code[:insert_offset] + key + en_code[insert_offset:]
en_code += struct.pack("<I", insert_offset)  # 尾部记录偏移

步骤分解:

  1. 编译源码为 CodeObjectcompile()
  2. 序列化为字节码marshal.dumps()(Python 内部格式)
  3. 用密钥异或加密(简单但有效,避免明文字节码)
  4. 将密钥随机插入加密数据中间(增加静态分析难度)
  5. 在末尾写入 4 字节偏移量(小端无符号整数),用于运行时定位密钥位置

这种“密钥内嵌 + 偏移记录”的设计,使得每个加密文件都是自包含的,无需额外配置。

3. 输出加密文件:注入运行时调用

加密后的 .py 文件内容变为:

from __runtime__ import __run__
__run__(b'\x12\xaf...', globals())

其中 b'\x12\xaf...' 是加密后的字节数据(包含密钥和偏移)。
当 Python 执行该文件时,会调用 __runtime__.__run__ 来解密并执行原始逻辑。

4. 运行时模块(runtime/)

虽然本文未展示 runtime/__init__.py 的内容,但其核心逻辑大致如下:

def __run__(en_code: bytes, globals_: dict):
    import struct
    import marshal

    def decrypt(en_code: bytes) -> bytes:
        en_code, offset_bytes = en_code[:-4], en_code[-4:]  # 去除偏移量
        insert_offset = struct.unpack("<I", offset_bytes)[0]
        key = en_code[insert_offset : insert_offset + 64]  # 提取key
        en_code = en_code[:insert_offset] + en_code[insert_offset + 64 :]  # 去除key
        # 解密
        de_code = bytes([en_code[i] ^ key[i % len(key)] for i in range(len(en_code))])
        return de_code

    code = marshal.loads(decrypt(en_code))
    exec(code, globals_, globals_)

此模块需随加密项目一同分发,但本身不含敏感逻辑,可公开。

三、使用方式

只需一行代码即可加密整个项目:

encryptor = Encryptor("testproject")  # 源目录
encryptor.encryptProject()            # 输出到 testproject-dist/

支持递归处理所有 .py.pyw 文件,并自动创建目标目录结构。

四、优缺点分析

优点

  • 简单有效:无需第三方依赖,仅用标准库;
  • 自包含:每个文件独立解密,无全局密钥管理;
  • 保留结构:项目组织不变,兼容原有导入路径;
  • 防小白:普通用户无法直接阅读源码。

局限

  • 非高强度加密:异或加密可被内存 dump 或动态调试破解;
  • 性能开销:每次执行需解密,对启动速度有轻微影响;
  • 不防反编译:一旦字节码被还原,可用 uncompyle6 等工具反编译;
  • 密钥内嵌风险:熟练攻击者可提取密钥批量解密。

若需更高安全性,建议结合:

  • C 扩展封装核心逻辑
  • 商业混淆工具(如 PyArmor)
  • 网络授权验证

五、总结

本文实现的 Encryptor 是一个教学级但实用的 Python 项目加密方案。它平衡了易用性与基础保护能力,适合用于:

  • 内部工具分发
  • 客户端脚本保护
  • 防止 casual 代码窥探

虽然不能做到“绝对安全”,但在多数场景下已足够提升逆向成本。更重要的是,通过这个过程,我们深入理解了 Python 的编译、字节码、marshal 序列化等底层机制。

附:注意事项

  • 确保 runtime/ 目录与加密脚本同级;
  • 不要加密 __main__.py 以外的入口文件时破坏 if __name__ == "__main__" 逻辑;
  • 测试加密后项目是否能正常运行!

以上就是Python项目进行加密打包的实践指南的详细内容,更多关于Python项目加密打包的资料请关注脚本之家其它相关文章!

相关文章

  • Python获取好友地区分布及好友性别分布情况代码详解

    Python获取好友地区分布及好友性别分布情况代码详解

    利用Python + wxpy 可以快速的查询自己好友的地区分布情况,以及好友的性别分布数量。还可以批量下载好友的头像,拼接成大图。感兴趣的朋友跟随小编一起看看吧
    2019-07-07
  • python实现bitmap数据结构详解

    python实现bitmap数据结构详解

    bitmap是很常用的数据结构,比如用于Bloom Filter中,下面是使用python实现bitmap数据结构的代码讲解,需要的朋友可以参考下
    2014-02-02
  • Python实现关键路径和七格图计算详解

    Python实现关键路径和七格图计算详解

    这篇文章主要为大家详细介绍了如何利用Python实现关键路径和七格图计算,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-03-03
  • python 实现图片特效处理

    python 实现图片特效处理

    这篇文章主要介绍了python 实现图片特效处理,对于 ​图片处理​,在日常生活中我们常常能够看到的,下面我们就来利用Python来对图片进行特效操作,需要的朋友可以参考一下
    2022-04-04
  • python实现一个简单的udp通信的示例代码

    python实现一个简单的udp通信的示例代码

    这篇文章主要介绍了python实现一个简单的udp通信的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • 对比分析BN和dropout在预测和训练时区别

    对比分析BN和dropout在预测和训练时区别

    这篇文章主要为大家介绍了对比分析BN和dropout在预测和训练时区别,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • python正则匹配抓取豆瓣电影链接和评论代码分享

    python正则匹配抓取豆瓣电影链接和评论代码分享

    抓取豆瓣各类型电影的链接和评论,按评分排列
    2013-12-12
  • 10个简单但很有用的Python装饰器分享

    10个简单但很有用的Python装饰器分享

    装饰器(Decorators)是Python中一种强大而灵活的功能,用于修改或增强函数或类的行为,本文为大家整理了10个简单但很有用的Python装饰器,希望对大家有所帮助
    2023-08-08
  • 基于Pygame实现简单的贪吃蛇游戏

    基于Pygame实现简单的贪吃蛇游戏

    Pygame是一个专门用来开发游戏的Python模块,主要用于开发、设计 2D 电子游戏。本文主要为大家介绍了通过Pygame制作一个简单的贪吃蛇游戏,感兴趣的同学可以关注一下
    2021-12-12
  • Python如何telnet到网络设备

    Python如何telnet到网络设备

    这篇文章主要介绍了Python如何telnet到网络设备,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2021-02-02

最新评论