转自:如何防止你的代码被窃取?Python代码加密方案汇总(带实例验证)_jmpy3库-CSDN博客
Python代码加密方案汇总
文章目录
Python代码加密方案汇总
需求描述
基础:Python文件格式
`.py` python源代码
`.pyc` 编译得到的字节码文件
`.pyo` 编译优化后得到的字节码文件
`.pyd` 可被Python调用的Windows DLL文件
待加密代码
基础加密:`.pyc`加密
进阶加密1:代码混淆
进阶加密2:`.pyd`/`.so`加密
成熟的解决方案:Pyarmor
Pyarmor 的基础使用
Pyarmor 的进阶使用
高级加密:定制Python解释器
需求描述
如何保护 python 的源代码?
举个栗子:张三将自己的编写的一个python文件发给了李四,张三希望李四能够正常使用这个文件(可以直接通过标准python解释器执行,或者可以被其他python文件调用),但张三不希望李四看到这个python文件的源代码。
在阅读本文讨论的源代码加密之前,有以下内容需要注意:
Python是FLOSS(自由/开放源码软件)之一,因此加密python的源代码不符合FLOSS的理念
不存在任何技术手段可以完全阻止用户阅读你的代码,但可以通过技术手段使得用户获得可阅读代码的成本更高
本文所讨论的代码加密仅仅是一个简单的保护机制,但如果遇到顶级黑客,或许他们甚至会访问内存来查看源码
使用法律、商业手段会比技术手段更加有效,例如软件使用许可证、SaaS等
基础:Python文件格式
为了理解python代码加密的基本原理,首先来了解下python的文件格式。
.py python源代码
这个大家都知道,不做过多阐述。这里着重介绍下Python代码的执行。
Python 代码的执行过程和 Java 类似:
将.py文件编译得到 Python 的字节码
Python 虚拟机(Python Virtual Machine) 执行编译好的字节码
Python 虚拟机和 Java 的 JVM 类似,但 Python虚拟机的抽象化程度更高(但不是性能更强)
.pyc 编译得到的字节码文件
在首次导入一个python库的时候,为了让以后再次导入更方便也更快,python会构建一个包含该库的字节码*.pyc文件
你可以在你的库文件的__pycache__文件夹下看到每个.py文件对应的.pyc文件
.pyo 编译优化后得到的字节码文件
Python 3.5之前将*.pyc文件通过优化器(-O)创建的文件,可以略微提升加载速度
Python 3.5 之后已经取消了.pyo文件的概念,优化后的文件也会以.pyc文件存储
从.pyc或.pyo文件中读取程序比从.py文件中读取得更快,但只是优化了加载速度,不会优化运行速度
.pyd 可被Python调用的Windows DLL文件
基本的Windows DLL文件,是Python的动态链接库
并非从python代码生成,而是其他语言写成的可以被Python调用的Windows DLL文件,例如C++
关于Windows DLL文件的介绍可以参看 Microsoft-什么是DLL?
待加密代码
接下来以实例展示各种代码隐藏的方式。
待加密的原始代码如下:
# -*- coding: UTF-8 –*-
# filename: ergou_test.py
import datetime
class Today():
def get_time(self):
print(datetime.datetime.now())
def say(self):
print("hello from Ergou!")
if __name__ == '__main__':
t=Today()
t.say()
t.get_time()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
直接运行结果如下:
基础加密:.pyc加密
加密难度:⭐️
安全性:⭐️
最基础的加密方法是利用 Python 自带的编译器将源代码文件.py 编译得到的二进制的字节码文件.pyc。
二进制的字节码文件对于初学者而言有一定的代码隐藏作用,但也只能简单隐藏。
在命令行中输入如下代码可以得到.pyc文件:
python -m py_compile ergou_test.py
1
在.\__pycache__目录下可以看到字节码文件ergou_test.cpython-38.pyc
字节码文件ergou_test.cpython-38.pyc可以在别的Python文件中导入使用,也可以直接执行。
如果要对多个文件或整个项目进行编译,可以使用Python标准库中的compileall库。
但是字节码文件极易被反编译得到源代码。
推荐一个反编译库uncompyle,可以直接使用 Pip 安装:
pip install uncompyle
1
在命令行中运行反编译命令:
uncompyle6 ergou_test.cpython-38.pyc > ergou_test.py
1
反编译得到的ergou_test.py文件如下:
# uncompyle6 version 3.7.2
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)]
# Embedded file name: ergou_test.py
# Compiled at: 2020-07-22 11:41:54
# Size of source mod 2**32: 273 bytes
import datetime
class Today:
def get_time(self):
print(datetime.datetime.now())
def say(self):
print('hello from Ergou!')
if __name__ == '__main__':
t = Today()
t.say()
t.get_time()
# okay decompiling ergou_test.cpython-38.pyc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
可以看到和原始代码基本没有区别,所以.pyc的加密方式基本相当于裸奔。
进阶加密1:代码混淆
对于代码加密,我们可以换个思路:我们可以暴露代码,但是只要你看不懂我的代码,也就无法使用我的代码了。
代码混淆可以使用pyminifier库,
安装:
pip install pyminifier
1
使用:
pyminifier --nonlatin --replacement-length=10 -O ergou_test.py
1
对于单个文件,会直接输出混淆后的代码:
import datetime
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈܙ=print
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ堛=datetime.now
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ𨱕=datetime.datetime
class 鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ𑰟():
def 鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈﮕ(self):
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈܙ(鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ𨱕.now())
def 鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ𐠗(self):
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈܙ("hello from Ergou!")
if __name__=='__main__':
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ䱺=鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ𑰟()
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ䱺.鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ𐠗()
鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈ䱺.鄮롍ﹹݾ𐼖氉ﵫ𨐟ࡈﮕ()
# Created by pyminifier (https://github.com/liftoff/pyminifier)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
这样混淆的代码可读性变得极差,然而即便如此,还是不难看出代码内部中的逻辑,通过变量名替换等方法还是可以看出其大致的逻辑。
pyminifier中还有另外一种代码混淆方法,利用Base64对代码进行再次编码,再利用lzma算法进行压缩,但是这种加密方式仅仅用于代码没有调用隐式导入的情况。
使用:
pyminifier --lzma "ergou_test.py"
1
结果:
import lzma, base64
exec(lzma.decompress(base64.b64decode('/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AC9AIddADSbSme4Ujxz0DHnfZG4YVh3r9CsdtAwW4DRCnyvCgYFNNvit5ucVyZEXm0xrZQFnMmnv5z9aXgGq8oGWLMz+nFaI+A7zI5M115jvtlkHe2PTQ44cNNJgVhXoX718yXUd9RQuI13Z9g+nUZiG4oGdJRmK7JehLK/UQ2Tic8JFOCKT4lM8+hv4AAAZj5170QAhWgAAaMBvgEAALPN0p2xxGf7AgAAAAAEWVo=')))
# Created by pyminifier (https://github.com/liftoff/pyminifier)
1
2
3
这样得到的代码完全看不出原来的逻辑,但是Base64非常容易被反编译,因此加密效果还是有限的。
可以考虑将上诉两者混淆方法结合起来,这样可以进一步增大代码的混淆程度。
进阶加密2:.pyd/.so加密
加密难度:⭐️ ⭐️ ⭐️
安全性:⭐️ ⭐️ ⭐️
Cython是一个编程语言,它通过类似Python的语法来编写C扩展并可以被Python调用。能够将Python+C混合编码的.pyx脚本转换为C代码,主要用于优化Python脚本性能或Python调用C函数库。基于它的原理,我们可以得到一种代码加密的思路:将 .py/.pyx 编译为 .c 文件,再将 .c 文件编译为 .so(Unix) 或 .pyd(Windows),这样得到的文件更难反编译。
注意:Windows环境下使用该库可能需要配置Microsoft Visual C++相关库
推荐一个库jmpy3,该库能够一键完成上诉流程。
安装:
pip install jmpy3
1
使用:
jmpy -i "ergou_test.py" -m 0
1
在.\dist文件夹下得到ergou_test.pyd文件
可以在Python文件中导入ergou_test.pyd,并使用其中的方法
关于这种加密方法的安全性,笔者目前尚未发现能将.so/.pyd文件直接反编译为.py的方法,因此较为可靠。
成熟的解决方案:Pyarmor
加密难度:⭐️ ⭐️⭐️
安全性:⭐️ ⭐️ ⭐️ ⭐️⭐️
经过多番搜索,笔者找到了一个比较完善的Python脚本加密方案:pyarmor
pyarmor能够加密 Python 脚本,同时还具备以下功能:
设置加密脚本的有效期限
绑定加密脚本到硬盘、网卡等硬件设备
Pyarmor的加密原理比较复杂,在此不做阐述,感兴趣的可以直接查看他的官方文档。Pyarmor的开发者为中国人,因此文档也是中文,查看起来还是比较方便的。
特别注意
Pyarmor是一个共享软件,试用版免费但存在功能限制(主要是加密代码不能超过 32 MB),完整版的授权价格为 286.00 人民币。
详细的软件许可说明可以参看Pyarmor软件许可
接下来展示pyarmor的使用。
Pyarmor 的基础使用
安装:
pip install pyarmor
1
基础使用 加密代码:
pyarmor obfuscate ergou_test.py
1
在输出目录.\dist文件夹下可以看到加密后的脚本ergou_test.py,代码内容如下:
from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'\x50\x59\x41\x52\x4d\x4f\x52\x00\x00\x03\x08\x00\x55\x0d\x0d\x0a\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\xc0\x10\x00\x00\x00\x00\x00\x18\x4d\x8f\xde\x78\xa2\x8e\xb4\x57\xd4\x7f\xbd\x06\x57\x35\x4b\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x20\x87\x36\x3e\xec\x5e\xa8\x81\x31\x30\x52\xa7\x6b\xf0\x82\xde\x45\x9e\x37\x7f\xde\x9e\xec\x04\xfa\xec\x6c\x5b\x09\x0b\x68\x05\x03\x72\xdd\x6a\x82\xff\x0e\x14\x13\x41\xa3\x6f\x22\xf0\x00\x96\xd1\xe9\xc6\xd8\x7d\x9d\xa6\x8b\xf5\xa3\x7a\x35\x52\xed\x05\x15\xa4\x2c\xdb\x10\xe5\x9c\xc0\xfc\x38\x11\x59\xd2\x26\x4f\xb5\x3f\xba\x10\xad\xe4\x26\x67\xac\x64\x15\xce\x6e\x84\x90\xeb\x8b\x1f\xb9\xbf\xc0\xbb\x7f\xa7\x8c【这里省略了一段很长很长很长的十六进制数】\xb5\x3f\xba', 2)
1
2
3
除了加密脚本之外,额外的那个目录 pytransform 叫做 运行辅助包 ,它是运行加密脚本不可缺少的。
脚本可以直接运行:
python ergou_test.py
1
也可以导入到别的脚本中:
如果需要分享代码的话,需要将.\dist目录下的全部文件都发送过去,对方无需安装Pyarmor库。
Pyarmor 的进阶使用
加密脚本的同时会在输出目录下面生成一个默认许可文件 dist/license.lic ,它 允许加密脚本运行在任何设备上并且永不过期。
可以根据需求生成指定有效日期、主机MAC地址、硬盘编号甚至是Docker容器ID的许可文件。
生成有效日期到 2020-07-22 的许可文件:、
pyarmor licenses --expired 2020-07-22 code-001
1
执行这条命令 Pyarmor 会生成一个带有效期的认证文件:
创建 license.lic ,保存在 licenses/code-001
创建 license.lic.txt ,保存在 licenses/code-001
然后将许可文件license.lic文件复制到.\dist\pytransform目录下,替换原来的license.lic。
这样,加密脚本在2020年7月22日之后就无法在运行了,比如现在就无法执行了。
censes --expired 2020-07-22 code-001
1
执行这条命令 Pyarmor 会生成一个带有效期的认证文件:
创建 license.lic ,保存在 licenses/code-001
创建 license.lic.txt ,保存在 licenses/code-001
然后将许可文件license.lic文件复制到.\dist\pytransform目录下,替换原来的license.lic。
这样,加密脚本在2020年7月22日之后就无法在运行了,比如现在就无法执行了。
高级加密:定制Python解释器
未完待续…
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_43207777/article/details/108351862