Python源码加密方案详解:.pyc、混淆、py2exe与Cython对比
本文深入解析Python源码保护的四种主流方案:发行.pyc文件、代码混淆、使用py2exe打包及Cython编译。详细对比各方案在实现难度、平台兼容性、破解成本及性能影响方面的优劣,帮助开发者根据实际需求选择最合适的代码保护策略。
Python源码加密概述
本文旨在详细介绍Python代码加密的具体实现方法,通过示例代码进行清晰阐述,为相关学习与工作提供有价值的参考。
笔者于去年11月在PyCon China 2018杭州站分享了Python源码加密技术,重点讲解了通过修改Python解释器来实现代码加解密的技术路径。由于整理成文耗时较长,如今终于完成并分享本文。
本系列内容将首先梳理现有源码加密方案的思路、方法、优点与不足,随后深入探讨如何通过定制Python解释器来实现更高效的源码加解密。
鉴于Python的动态特性和开源属性,其代码难以实现高强度的加密保护。社区中存在两种观点:一种认为应通过法律手段而非加密源码进行商业保护,另一种则坚持寻求技术手段进行加密。为此,业界提出了多种加密或混淆方案以保护源码安全。
常见的源码保护手段主要包括以下几种:
- 发行.pyc文件
- 代码混淆
- 使用py2exe工具
- 使用Cython工具
接下来将简要介绍上述各方案的具体实施情况。
方案一:发行.pyc文件
1 发行.pyc文件
1.1 思路
Python解释器在执行代码时,会首先生成.pyc文件,随后解释执行该二进制文件。Python解释器也支持直接执行.pyc文件。由于.pyc文件为二进制格式,无法直接查看源码内容,若向客户环境分发时仅包含.pyc文件而非.py文件,即可达到保护Python代码的目的。
1.2 方法
将.py文件编译为.pyc文件操作简便,无需运行所有代码即可提取生成的.pyc文件。
Python标准库提供了compileall模块,可轻松完成编译工作。
执行以下命令可遍历<src>目录下的所有.py文件,并将其编译为.pyc文件:
python -m compileall <src> 随后删除<src>目录下所有.py文件即可打包发布:
$ find <src> -name '*.py' -type f -print -exec rm {} \;
1.3 优点
操作简单便捷,略微提高了源码破解的门槛。
平台兼容性好,.py文件能运行的环境,.pyc文件同样可以运行。
1.4 不足
解释器兼容性较差,.pyc文件仅能在特定版本的解释器上运行。
存在现成的反编译工具,破解成本较低。python-uncompyle6即为一款效果出色的反编译工具。
执行以下命令,即可将.pyc文件反编译为.py文件:
$ uncompyle6 *compiled-python-file-pyc-or-pyo*
方案二:代码混淆技术
2 代码混淆
若代码被混淆至连作者都难以阅读的程度,是否也能达到保护源码的目的?
2.1 思路
混淆的核心在于通过一系列转换,使代码难以被轻易理解。具体操作包括:移除注释和文档字符串,使关键逻辑不易被理解;改变缩进格式,使代码排版混乱;在tokens间插入多余空格,类似改变缩进的效果;重命名函数、类和变量,通过混乱的命名增加阅读障碍;在空白行插入无效代码,以无关代码打乱阅读节奏。
2.2 方法
方法一:使用oxyry进行混淆
http://pyob.oxyry.com/ 是一个在线混淆Python代码的网站,使用便捷。
假设我们有如下涉及类、函数和参数的Python代码:
# coding: utf-8
class A(object):
"""
Description
"""
def __init__(self, x, y, default=None):
self.z = x + y
self.default = default
def name(self):
return 'No Name'
def always():
return True
num = 1
a = A(num, 999, 100)
a.name()
always()
经过Oxyry混淆后,得到如下代码:
class A (object ):#line:4
""#line:7
def __init__ (O0O0O0OO00OO000O0 ,OO0O0OOOO0000O0OO ,OO0OO00O00OO00OOO ,OO000OOO0O000OOO0 =None ):#line:9
O0O0O0OO00OO000O0 .z =OO0O0OOOO0000O0OO +OO0OO00O00OO00OOO #line:10
O0O0O0OO00OO000O0 .default =OO000OOO0O000OOO0 #line:11
def name (O000O0O0O00O0O0OO ):#line:13
return 'No Name'#line:14
def always ():#line:17
return True #line:18
num =1 #line:21
a =A (num ,999 ,100 )#line:22
a .name ()#line:23
always ()
混淆后的代码主要在注释、参数名称和空格方面进行了调整,在一定程度上增加了阅读障碍。
方法二:使用pyobfuscate库进行混淆
pyobfuscate是一个历史悠久的Python代码混淆库,但依然“老当益壮”。
对前述同一段Python代码,经pyobfuscate混淆后效果如下:
# coding: utf-8
if 64 - 64: i11iIiiIii
if 65 - 65: O0 / iIii1I11I1II1 % OoooooooOO - i1IIi
class o0OO00 ( object ) :
if 78 - 78: i11i . oOooOoO0Oo0O
if 10 - 10: IIiI1I11i11
if 54 - 54: i11iIi1 - oOo0O0Ooo
if 2 - 2: o0 * i1 * ii1IiI1i % OOooOOo / I11i / Ii1I
def __init__ ( self , x , y , default = None ) :
self . z = x + y
self . default = default
if 48 - 48: iII111i % IiII + I1Ii111 / ooOoO0o * Ii1I
def name ( self ) :
return 'No Name'
if 46 - 46: ooOoO0o * I11i - OoooooooOO
if 30 - 30: o0 - O0 % o0 - OoooooooOO * O0 * OoooooooOO
def Oo0o ( ) :
return True
if 60 - 60: i1 + I1Ii111 - I11i / i1IIi
if 40 - 40: oOooOoO0Oo0O / O0 % ooOoO0o + O0 * i1IIi
I1Ii11I1Ii1i = 1
Ooo = o0OO00 ( I1Ii11I1Ii1i , 999 , 100 )
Ooo . name ( )
Oo0o ( ) # dd678faae9ac167bc83abf78e5cb2f3f0688d3a3
Oo0o ( ) # dd678faae9ac167bc83abf78e5cb2f3f0688d3a3 相比于方法一,方法二的效果更为显著。除了对类和函数进行重命名、插入空格外,最明显的是插入了若干段无关代码,使得代码更难阅读。
2.3 优点
操作简单便捷,略微提高了源码破解的门槛。
兼容性好,只要源码逻辑兼容,混淆后的代码同样兼容。
2.4 不足
仅能对单个文件进行混淆,无法实现多个相互关联的源码文件的联动混淆。
代码结构未发生变化,仍可获取字节码,破解难度不大。
方案三:使用py2exe打包
3 使用py2exe
3.1 思路
py2exe是一款将Python脚本转换为Windows平台可执行文件的工具。其原理是将源码编译为.pyc文件,并打包必要的依赖文件,最终生成一个可执行文件。
若最终分发由py2exe打包出的二进制文件,是否即可达到保护源码的目的?
3.2 方法
使用py2exe进行打包的步骤相对简便。
1)编写入口文件。本示例中命名为hello.py:
print 'Hello World'
2)编写setup.py:
from distutils.core import setup
import py2exe
setup(console=['hello.py'])
3)生成可执行文件
python setup.py py2exe
生成的可执行文件位于dist\hello.exe。
3.3 优点
可直接打包成exe文件,便于分发和执行。
破解门槛比.pyc文件更高一些。
3.4 不足
兼容性较差,仅能在Windows系统上运行。
生成的可执行文件内部布局明确且公开,可找到源码对应的.pyc文件,进而反编译出源码。
方案四:使用Cython编译
4 使用Cython
4.1 思路
尽管Cython的主要目的是提升性能,但其原理是将.py/.pyx编译为.c文件,再将.c文件编译为.so(Unix)或.pyd(Windows),这一过程带来的额外好处是代码难以被破解。
4.2 方法
使用Cython进行开发的步骤也不复杂。
1)编写文件hello.pyx或hello.py:
def hello():
print('hello')
2)编写setup.py:
from distutils.core import setup
from Cython.Build import cythonize
setup(name='Hello World app',
ext_modules=cythonize('hello.pyx'))
3)编译为.c,再进一步编译为.so或.pyd:
python setup.py build_ext --inplace
执行python -c "from hello import hello;hello()" 即可直接引用生成的二进制文件中的hello()函数。
4.3 优点
生成的二进制.so或.pyd文件难以破解。
同时带来了性能提升。
4.4 不足
兼容性稍差,针对不同版本的操作系统可能需要重新编译。
虽然支持大多数Python代码,但若发现部分代码不支持,完善成本较高。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













