如何對你的Python代碼進行加密

去年11月在PyCon China 2018 杭州站分享了 Python 源碼加密,講述了如何通過修改 Python 解釋器達到加解密 Python 代碼的目的。然而因為筆者拖延癥發(fā)作,一直沒有及時整理成文字版,現(xiàn)在終于戰(zhàn)勝了它,才有了本文。

本系列將首先介紹下現(xiàn)有源碼加密方案的思路、方法、優(yōu)點與不足,進而介紹如何通過定制 Python 解釋器來達到更好地加解密源碼的目的。

由于 Python 的動態(tài)特性和開源特點,導致 Python 代碼很難做到很好的加密。社區(qū)中的一些聲音認為這樣的限制是事實,應該通過法律手段而不是加密源碼達到商業(yè)保護的目的;而還有一些聲音則是不論如何都希望能有一種手段來加密。于是乎,人們想出了各種或加密、或混淆的方案,借此來達到保護源碼的目的。

常見的源碼保護手段有如下幾種:

發(fā)行.pyc文件

代碼混淆

使用py2exe

使用Cython

下面來簡單說說這些方案。

1 發(fā)行 .pyc 文件

1.1 思路

大家都知道,Python 解釋器在執(zhí)行代碼的過程中會首先生成.pyc文件,然后解釋執(zhí)行.pyc文件中的內容。當然了,Python 解釋器也能夠直接執(zhí)行.pyc文件。而.pyc文件是二進制文件,無法直接看出源碼內容。如果發(fā)行代碼到客戶環(huán)境時都是.pyc而非.py文件的話,那豈不是能達到保護 Python 代碼的目的?

1.2 方法

把.py文件編譯為.pyc文件,是件非常輕松地事情,可不需要把所有代碼跑一遍,然后去撈生成的.pyc文件。

事實上,Python 標準庫中提供了一個名為 compileall 的庫,可以輕松地進行編譯。

執(zhí)行如下命令能夠將遍歷<src>目錄下的所有.py文件,將之編譯為.pyc文件:

python -m compileall<src>然后刪除<src>目錄下所有.py文件就可以打包發(fā)布了:

1$ find ?-name?'*.py'?-type?f?-print?-exec?rm {} \;

1.3 優(yōu)點

簡單方便,提高了一點源碼破解門檻

平臺兼容性好,.py能在哪里運行,.pyc就能在哪里運行

1.4 不足

解釋器兼容性差,.pyc只能在特定版本的解釋器上運行

有現(xiàn)成的反編譯工具,破解成本低

python-uncompyle6 就是這樣一款反編譯工具,效果出眾。

執(zhí)行如下命令,即可將.pyc文件反編譯為.py文件:

1$ uncompyle6?*compiled-python-file-pyc-or-pyo*

2 代碼混淆

如果代碼被混淆到一定程度,連作者看著都費勁的話,是不是也能達到保護源碼的目的呢?

2.1 思路

既然我們的目的是混淆,就是通過一系列的轉換,讓代碼逐漸不讓人那么容易明白,那就可以這樣下手:- 移除注釋和文檔。沒有這些說明,在一些關鍵邏輯上就沒那么容易明白了。- 改變縮進。完美的縮進看著才舒服,如果縮進忽長忽短,看著也一定鬧心。- 在tokens中間加入一定空格。這就和改變縮進的效果差不多。- 重命名函數(shù)、類、變量。命名直接影響了可讀性,亂七八糟的名字可是閱讀理解的一大障礙。- 在空白行插入無效代碼。這就是障眼法,用無關代碼來打亂閱讀節(jié)奏。

2.2 方法

方法一:使用 oxyry 進行混淆

http://pyob.oxyry.com/ 是一個在線混淆 Python 代碼的網站,使用它可以方便地進行混淆。

假定我們有這樣一段 Python 代碼,涉及到了類、函數(shù)、參數(shù)等內容:

# coding: utf-8

clas?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 ()

混淆后的代碼主要在注釋、參數(shù)名稱和空格上做了些調整,稍微帶來了點閱讀上的障礙。

方法二:使用 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

相比于方法一,方法二的效果看起來更好些。除了類和函數(shù)進行了重命名、加入了一些空格,最明顯的是插入了若干段無關的代碼,變得更加難讀了。

2.3 優(yōu)點

簡單方便,提高了一點源碼破解門檻

兼容性好,只要源碼邏輯能做到兼容,混淆代碼亦能

2.4 不足

只能對單個文件混淆,無法做到多個互相有聯(lián)系的源碼文件的聯(lián)動混淆

代碼結構未發(fā)生變化,也能獲取字節(jié)碼,破解難度不大

3 使用 py2exe

3.1 思路

py2exe 是一款將 Python 腳本轉換為 Windows 平臺上的可執(zhí)行文件的工具。其原理是將源碼編譯為.pyc文件,加之必要的依賴文件,一起打包成一個可執(zhí)行文件。

如果最終發(fā)行由py2exe打包出的二進制文件,那豈不是達到了保護源碼的目的?

3.2 方法

使用py2exe進行打包的步驟較為簡便。

1)編寫入口文件。本示例中取名為hello.py:

1print?'Hello World'

2)編寫setup.py:

from?distutils.core?import?setup

import?py2exe

setup(console=['hello.py'])

3)生成可執(zhí)行文件

1python setup.py py2exe

生成的可執(zhí)行文件位于dist\hello.exe。

3.3 優(yōu)點

能夠直接打包成 exe,方便分發(fā)和執(zhí)行

破解門檻比 .pyc 更高一些

3.4 不足

兼容性差,只能運行在 Windows 系統(tǒng)上

生成的可執(zhí)行文件內的布局是明確、公開的,可以找到源碼對應的.pyc文件,進而反編譯出源碼

4 使用 Cython

4.1 思路

雖說Cython的主要目的是帶來性能的提升,但是基于它的原理:將.py/.pyx編譯為.c文件,再將.c文件編譯為.so(Unix) 或.pyd(Windows),其帶來的另一個好處就是難以破解。

4.2 方法

使用Cython進行開發(fā)的步驟也不復雜。

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:

1python setup.py build_ext?--inplace

執(zhí)行python -c "from hello import hello;hello()"即可直接引用生成的二進制文件中的hello()函數(shù)。

4.3 優(yōu)點

生成的二進制 .so 或 .pyd 文件難以破解

同時帶來了性能提升

4.4 不足

兼容性稍差,對于不同版本的操作系統(tǒng),可能需要重新編譯

雖然支持大多數(shù) Python 代碼,但如果一旦發(fā)現(xiàn)部分代碼不支持,完善成本較高

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Distutils可以用來在Python環(huán)境中構建和安裝額外的模塊。新的模塊可以是純Python的,也可以...
    MiracleJQ閱讀 3,240評論 0 1
  • Python是一門解釋型語言? 我初學Python時,聽到的關于Python的第一句話就是,Python是一門解釋...
    文哥的學習日記閱讀 7,509評論 1 6
  • Python越來越熱門了,2019年3月TIOBE編程語言排行榜上,Python更是罕見的擊敗了“霸榜三巨頭”之一...
    諸葛青云999閱讀 2,644評論 2 17
  • 家里最近收拾搬家,其他東西都收拾好了,只有一樣-被子的處理讓我們?yōu)殡y!按理來說,一個家庭家里存放兩三床被子是正常的...
    杏朵兒閱讀 280評論 0 0
  • 1.“讀一些無用的書,做一些無用的事,花一些無用的時間,都是為了在一切已知之外,保留一個超越自己的機會,人生中一些...
    金星慢閱讀 173評論 0 1

友情鏈接更多精彩內容