如何保護你的 Python 代碼

REF 

https://www.cnblogs.com/dhcn/p/11077447.html

https://blog.csdn.net/apollo_miracle/article/details/106316090

 

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

  • 發行 .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 進行混淆

 是一個在線混淆 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

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

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 進行開發的步驟也不復雜。

pip install pycrypto 

pip install Cython 

1)編寫文件  test.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('test.py'))

3)編譯爲 .c,再進一步編譯爲 .so 或 .pyd

python setup.py build_ext --inplace

執行

  python -c "from test import hello;hello()" 

即可直接引用生成的二進制文件中的 hello() 函數。

 

4.3 優點

  • 生成的二進制 .so 或 .pyd 文件難以破解
  • 同時帶來了性能提升

4.4 不足

  • 兼容性稍差,對於不同版本的操作系統,可能需要重新編譯
  • 雖然支持大多數 Python 代碼,但如果一旦發現部分代碼不支持,完善成本較高

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章