如何給Python程式碼進行加密
這篇文章主要介紹瞭如何給Python程式碼進行加密,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
去年11月在PyCon China 2018 杭州站分享了 Python 原始碼加密,講述瞭如何通過修改 Python 直譯器達到加解密 Python 程式碼的目的。然而因為筆者拖延症發作,一直沒有及時整理成文字版,現在終於戰勝了它,才有了本文。
本系列將首先介紹下現有原始碼加密方案的思路、方法、優點與不足,進而介紹如何通過定製 Python 直譯器來達到更好地加解密原始碼的目的。
由於 Python 的動態特性和開源特點,導致 Python 程式碼很難做到很好的加密。社群中的一些聲音認為這樣的限制是事實,應該通過法律手段而不是加密原始碼達到商業保護的目的;而還有一些聲音則是不論如何都希望能有一種手段來加密。於是乎,人們想出了各種或加密、或混淆的方案,藉此來達到保護原始碼的目的。
常見的原始碼保護手段有如下幾種:
- 發行 .pyc 檔案
- 程式碼混淆
- 使用 py2exe
- 使用 Cython
下面來簡單說說這些方案。
1 發行 .pyc 檔案
1.1 思路
大家都知道,Python 直譯器在執行程式碼的過程中會首先生成 .pyc 檔案,然後解釋執行 .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,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,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,100 ) Ooo . name ( ) Oo0o ( ) # dd678faae9ac167bc83abf78e5cb2f3f0688d3a3
Oo0o ( ) # dd678faae9ac167bc83abf78e5cb2f3f0688d3a3
相比於方法一,方法二的效果看起來更好些。除了類和函式進行了重新命名、加入了一些空格,最明顯的是插入了若干段無關的程式碼,變得更加難讀了。
2.3 優點
簡單方便,提高了一點原始碼破解門檻
相容性好,只要原始碼邏輯能做到相容,混淆程式碼亦能
2.4 不足
只能對單個檔案混淆,無法做到多個互相有聯絡的原始碼檔案的聯動混淆
程式碼結構未發生變化,也能獲取位元組碼,破解難度不大
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 檔案,進而反編譯出原始碼
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 程式碼,但如果一旦發現部分程式碼不支援,完善成本較高
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。