如何對(duì)你的Python代碼進(jìn)行加密

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

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

由于 Python 的動(dòng)態(tài)特性和開源特點(diǎn)禁悠,導(dǎo)致 Python 代碼很難做到很好的加密碍侦。社區(qū)中的一些聲音認(rèn)為這樣的限制是事實(shí),應(yīng)該通過法律手段而不是加密源碼達(dá)到商業(yè)保護(hù)的目的站玄;而還有一些聲音則是不論如何都希望能有一種手段來加密株旷。于是乎尔邓,人們想出了各種或加密、或混淆的方案齿尽,借此來達(dá)到保護(hù)源碼的目的循头。

常見的源碼保護(hù)手段有如下幾種:

發(fā)行.pyc文件

代碼混淆

使用py2exe

使用Cython

下面來簡(jiǎn)單說說這些方案贷岸。

1 發(fā)行 .pyc 文件

1.1 思路

大家都知道磷雇,Python 解釋器在執(zhí)行代碼的過程中會(huì)首先生成.pyc文件唯笙,然后解釋執(zhí)行.pyc文件中的內(nèi)容崩掘。當(dāng)然了苞慢,Python 解釋器也能夠直接執(zhí)行.pyc文件英妓。而.pyc文件是二進(jìn)制文件,無法直接看出源碼內(nèi)容辑畦。如果發(fā)行代碼到客戶環(huán)境時(shí)都是.pyc而非.py文件的話腿倚,那豈不是能達(dá)到保護(hù) Python 代碼的目的?

1.2 方法

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

事實(shí)上菲饼,Python 標(biāo)準(zhǔn)庫中提供了一個(gè)名為 compileall 的庫列赎,可以輕松地進(jìn)行編譯包吝。

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

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

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

1.3 優(yōu)點(diǎn)

簡(jiǎn)單方便诗越,提高了一點(diǎn)源碼破解門檻

平臺(tái)兼容性好砖瞧,.py能在哪里運(yùn)行,.pyc就能在哪里運(yùn)行

1.4 不足

解釋器兼容性差嚷狞,.pyc只能在特定版本的解釋器上運(yùn)行

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

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

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

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

2 代碼混淆

如果代碼被混淆到一定程度,連作者看著都費(fèi)勁的話薇搁,是不是也能達(dá)到保護(hù)源碼的目的呢斋扰?

2.1 思路

既然我們的目的是混淆,就是通過一系列的轉(zhuǎn)換啃洋,讓代碼逐漸不讓人那么容易明白损离,那就可以這樣下手:- 移除注釋和文檔窟勃。沒有這些說明亚斋,在一些關(guān)鍵邏輯上就沒那么容易明白了赖瞒。- 改變縮進(jìn)。完美的縮進(jìn)看著才舒服民逼,如果縮進(jìn)忽長(zhǎng)忽短,看著也一定鬧心燕侠。- 在tokens中間加入一定空格械巡。這就和改變縮進(jìn)的效果差不多。- 重命名函數(shù)贷祈、類粟耻、變量戈泼。命名直接影響了可讀性,亂七八糟的名字可是閱讀理解的一大障礙巨坊。- 在空白行插入無效代碼暂题。這就是障眼法言津,用無關(guān)代碼來打亂閱讀節(jié)奏。

2.2 方法

方法一:使用 oxyry 進(jìn)行混淆

http://pyob.oxyry.com/ 是一個(gè)在線混淆 Python 代碼的網(wǎng)站,使用它可以方便地進(jìn)行混淆访雪。

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

# 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()

經(jīng)過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ù)名稱和空格上做了些調(diào)整蹬叭,稍微帶來了點(diǎn)閱讀上的障礙饥悴。

方法二:使用 pyobfuscate 庫進(jìn)行混淆

pyobfuscate 算是一個(gè)頗具年頭的 Python 代碼混淆庫了瓣铣,但卻是“老當(dāng)益壯”了禽绪。

對(duì)上述同樣一段 Python 代碼循捺,經(jīng)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ù)進(jìn)行了重命名正罢、加入了一些空格回还,最明顯的是插入了若干段無關(guān)的代碼蝗柔,變得更加難讀了。

2.3 優(yōu)點(diǎn)

簡(jiǎn)單方便,提高了一點(diǎn)源碼破解門檻

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

2.4 不足

只能對(duì)單個(gè)文件混淆蒋得,無法做到多個(gè)互相有聯(lián)系的源碼文件的聯(lián)動(dòng)混淆

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

3 使用 py2exe

3.1 思路

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

如果最終發(fā)行由py2exe打包出的二進(jìn)制文件篮幢,那豈不是達(dá)到了保護(hù)源碼的目的搜锰?

3.2 方法

使用py2exe進(jìn)行打包的步驟較為簡(jiǎn)便狐胎。

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)點(diǎn)

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

破解門檻比 .pyc 更高一些

3.4 不足

兼容性差关炼,只能運(yùn)行在 Windows 系統(tǒng)上

生成的可執(zhí)行文件內(nèi)的布局是明確、公開的吏砂,可以找到源碼對(duì)應(yīng)的.pyc文件匈织,進(jìn)而反編譯出源碼

4 使用 Cython

4.1 思路

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

4.2 方法

使用Cython進(jìn)行開發(fā)的步驟也不復(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墩崩,再進(jìn)一步編譯為.so或.pyd:

1python setup.py build_ext?--inplace

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

4.3 優(yōu)點(diǎn)

生成的二進(jìn)制 .so 或 .pyd 文件難以破解

同時(shí)帶來了性能提升

4.4 不足

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市她紫,隨后出現(xiàn)的幾起案子疯潭,更是在濱河造成了極大的恐慌脊僚,老刑警劉巖增淹,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異码撰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)质帅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門涡扼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稼跳,“玉大人,你說我怎么就攤上這事吃沪√郎疲” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵票彪,是天一觀的道長(zhǎng)红淡。 經(jīng)常有香客問我,道長(zhǎng)降铸,這世上最難降的妖魔是什么在旱? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮推掸,結(jié)果婚禮上桶蝎,老公的妹妹穿的比我還像新娘。我一直安慰自己谅畅,他們只是感情好登渣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毡泻,像睡著了一般胜茧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仇味,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天呻顽,我揣著相機(jī)與錄音,去河邊找鬼丹墨。 笑死廊遍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贩挣。 我是一名探鬼主播昧碉,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼揽惹!你這毒婦竟也來了被饿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤搪搏,失蹤者是張志新(化名)和其女友劉穎狭握,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疯溺,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡论颅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年哎垦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恃疯。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漏设,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出今妄,到底是詐尸還是另有隱情郑口,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布盾鳞,位于F島的核電站犬性,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏腾仅。R本人自食惡果不足惜乒裆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望推励。 院中可真熱鬧鹤耍,春花似錦、人聲如沸验辞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽受神。三九已至,卻和暖如春格侯,著一層夾襖步出監(jiān)牢的瞬間鼻听,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工联四, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撑碴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓朝墩,卻偏偏與公主長(zhǎng)得像醉拓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子收苏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • Distutils可以用來在Python環(huán)境中構(gòu)建和安裝額外的模塊亿卤。新的模塊可以是純Python的,也可以...
    MiracleJQ閱讀 3,063評(píng)論 0 1
  • Python是一門解釋型語言鹿霸? 我初學(xué)Python時(shí)排吴,聽到的關(guān)于Python的第一句話就是,Python是一門解釋...
    文哥的學(xué)習(xí)日記閱讀 7,375評(píng)論 1 6
  • Python越來越熱門了屹堰,2019年3月TIOBE編程語言排行榜上,Python更是罕見的擊敗了“霸榜三巨頭”之一...
    諸葛青云999閱讀 2,577評(píng)論 2 17
  • 家里最近收拾搬家,其他東西都收拾好了珊肃,只有一樣-被子的處理讓我們?yōu)殡y荣刑!按理來說,一個(gè)家庭家里存放兩三床被子是正常的...
    杏朵兒閱讀 206評(píng)論 0 0
  • 1.“讀一些無用的書近范,做一些無用的事嘶摊,花一些無用的時(shí)間,都是為了在一切已知之外评矩,保留一個(gè)超越自己的機(jī)會(huì)叶堆,人生中一些...
    金星慢閱讀 153評(píng)論 0 1