<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 源碼混淆與加密

          共 5753字,需瀏覽 12分鐘

           ·

          2020-12-24 03:01

          ?△點(diǎn)擊上方Python貓”關(guān)注 ,回復(fù)“1”領(lǐng)取電子書

          作者:Collapsar
          來源:360BugCloud

          Python 是一種解釋型語言,沒有編譯過程,發(fā)布程序的同時(shí)就相當(dāng)于公開了源碼,這也是其作為開源語言的一個(gè)特性。但在某些場景下,我們的源碼是不想被別人看到的,例如開發(fā)商業(yè)軟件、編寫 0day 漏洞 POC/EXP、免殺 shellcode 等。


          以免殺為例,如果打包的源碼沒做任何處理,安全研究人員在捕獲到樣本后連分析的過程都省掉了,直接通過源碼鎖定特征,很快免殺就會(huì)失效,這顯然不是我們想看到的。因此對源碼做相應(yīng)的保護(hù)還是有必要的。


          注:本文代碼只為介紹源碼保護(hù)方法,不涉及免殺姿勢。


          目前保護(hù) Python 代碼主要有以下幾種方式:

          • 對代碼進(jìn)行混淆以降低源碼可讀性
          • 將 py 文件編譯為二進(jìn)制 pyc 文件
          • 使用 Pyinstaller 打包源碼為二進(jìn)制可執(zhí)行文件
          • 使用 PyArmor 加密腳本
          • 將 py/pyc 文件使用 AES 加密為 pye 文件
          • 將 py 文件轉(zhuǎn)為 c 文件后編譯為動(dòng)態(tài)鏈接庫文件


          代碼混淆

          代碼混淆是指在不改變代碼邏輯的情況下,對代碼結(jié)構(gòu)進(jìn)行變換,通過一些帶有混淆性質(zhì)的命名、注釋等,使代碼變得晦澀難懂,從而達(dá)到保護(hù)代碼的作用。這里提供兩種代碼混淆的方式:


          代碼混淆庫 pyobfuscate

          pyobfuscate 會(huì)對代碼中用戶定義的類、函數(shù)、變量等進(jìn)行重命名、更改代碼縮進(jìn)(默認(rèn)1)、移除注釋、添加不影響邏輯的代碼語句,最終起到混淆的作用。不過 pyobfuscate 使用 Python2 編寫,無法解析 Python3 中的 f-string 等特殊語法,因此使用前需要將源碼進(jìn)行一定程度的修改,當(dāng)然也可以直接修改 pyobfuscate 庫,增加對 Python3 版本的支持。


          樣例 (對 malicious.py 文件進(jìn)行混淆):

          python2 pyobfuscate.py malicious.py > malicious_obfuscated.py



          效果如下圖所示,左側(cè)為一段從云端獲取 shellcode 加載進(jìn)內(nèi)存執(zhí)行的代碼,右圖為其混淆后的結(jié)果。

          ????


          可以看出代碼雖然進(jìn)行了一定程度的變換,但代碼結(jié)構(gòu)基本還是原來的樣子,并不能很有效的增加破解難度。


          利用 AST 混淆源碼

          AST,即抽象語法樹,它可以將源代碼以樹狀結(jié)構(gòu)表示。Python 內(nèi)置了 ast 模塊,該模塊通過內(nèi)置函數(shù) compile() 和 parse() 將 ?Python 源代碼解析為 AST,之后可以利用 ast 模塊內(nèi)的方法對 ast 節(jié)點(diǎn)進(jìn)行相應(yīng)的操作,混淆處理后使用 codegen 庫將 AST 重新生成為 Python 源碼。


          樣例:

          python2 astobf.py malicious.py > malicious_astobfed.py



          效果如下圖所示,AST 混淆后的代碼略有修改,以保證 Python3 下腳本可正常執(zhí)行。



          混淆后的代碼使用 __import__ 動(dòng)態(tài)導(dǎo)入模塊、使用 getattr 調(diào)用類方法,這樣就可以以字符串方式傳入模塊名和方法名,借由字符串翻轉(zhuǎn)拼接、數(shù)字計(jì)算等方式達(dá)到混淆目的,相比之下,AST 方式的混淆效果明顯要優(yōu)于 pyobfuscate 庫。


          編譯為 pyc 文件

          pyc 文件是 Python 的字節(jié)碼文件,其存在的意義在于每次調(diào)用模塊時(shí),不用重新對該模塊進(jìn)行解釋,從而提高效率,減少性能損耗。但是在運(yùn)行一個(gè)單獨(dú)的腳本時(shí),該腳本是不會(huì)被編譯為 pyc 文件的,這是由于 Python 的解釋器認(rèn)為只有導(dǎo)入的包才會(huì)被不斷復(fù)用,才有編譯的價(jià)值。不過 Python 提供了 py_compile 庫和 compileall 程序用于手動(dòng)編譯 py 文件。


          py_compile

          import py_compile
          py_compile.compile(file="malicious.py")


          compileall

          python -m compileall ./



          編譯為字節(jié)碼文件后,確實(shí)沒辦法直接讀取源碼了,但是 Python 有 uncompyle6 這么一個(gè)跨版本反編譯器,可以將 Python 字節(jié)碼轉(zhuǎn)換回等效的 Python 源代碼。

          python -m pip install uncompyle6
          uncompyle6 malicious.cpython-37.pyc > malicious_Decompiled.py



          而且 pyc 還有一個(gè)弊端,就是它依賴于 Python 解釋器的版本,使用某版本解釋器編譯的 pyc 文件必須使用相同版本解釋器運(yùn)行才能正常工作, 所以實(shí)際上將 py 文件編譯為 pyc 文件的實(shí)用性并不是很大。


          打包為獨(dú)立可執(zhí)行程序

          通過將 Python 文件打包為獨(dú)立可執(zhí)行程序也是一種保護(hù)源碼的方式。Windows 平臺(tái)下 ,有 Pyinstaller 、 py2exe 和 cx_Freeze 等多種打包程序可以使用,以 Pyinstaller 為例,打包 malicious.py 命令如下:


          python -m pip install pyinstaller
          pyinstaller -Fw -i myicon.ico malicious.py


          -F 表示生成單文件,-w 表示隱藏控制臺(tái)窗口,-i 表示為生成的 exe 文件添加圖標(biāo)。



          Python 打包的 exe 程序并不是將文件編譯為真正的機(jī)器碼,而是將腳本編譯為 pyc 后連同依賴文件、當(dāng)前的 Python 解釋器一同打包起來,根據(jù)命令參數(shù)生成文件夾或打包成單獨(dú)的可執(zhí)行文件。之后運(yùn)行 exe 時(shí),實(shí)際運(yùn)行的是一個(gè)引導(dǎo)加載程序,引導(dǎo)加載程序會(huì)創(chuàng)建一個(gè)臨時(shí)的 Python 環(huán)境,通過解釋器副本來執(zhí)行 pyc 文件。


          由于這種運(yùn)行方式的特殊性,Pyinstaller 打包的 exe 文件也是可以被還原出源碼的。使用 pyinstxtractor 解包 exe:

          python pyinstxtractor.py malicious.exe



          解包后的文件夾內(nèi)包含了 malicious.pyc 文件,之后使用 uncompyle6 反編譯該文件就可以拿到源碼。所以將 Python 打包為 exe 只相當(dāng)于在編譯為 pyc 的基礎(chǔ)上添加了一步打包操作,同樣不能很有效的對源碼進(jìn)行保護(hù)。


          使用 PyArmor 加密代碼

          PyArmor 是一個(gè)用于加密和保護(hù) Python 腳本的工具。它能夠在運(yùn)行時(shí)刻保護(hù) Python 腳本的二進(jìn)制代碼不被泄露,設(shè)置加密后 Python 源代碼的有效期限,綁定加密后的 Python 源代碼到硬盤、網(wǎng)卡等硬件設(shè)備。


          它的保障機(jī)制主要包括:

          • 加密編譯后的代碼塊,保護(hù)模塊中的字符串和常量
          • 在腳本運(yùn)行時(shí)候動(dòng)態(tài)加密和解密每一個(gè)函數(shù)(代碼塊)的二進(jìn)制代碼
          • 代碼塊執(zhí)行完成之后清空堆棧局部變量
          • 通過授權(quán)文件限制加密后腳本的有效期和設(shè)備環(huán)境


          PyArmor 的工作原理相對復(fù)雜,有興趣的朋友可以參考官方的說明文檔:

          https://pyarmor.readthedocs.io/。


          使用 PyArmor 默認(rèn)加密方式加密 malicious.py:

          pyarmor obfuscate malicious.py



          加密后的文件前兩行代碼是引導(dǎo)代碼,用于加載 pytransform 動(dòng)態(tài)鏈接庫和添加三個(gè)內(nèi)置函數(shù)到 builtins 模塊,之后調(diào)用 __pyarmor__ 導(dǎo)入加密模塊執(zhí)行加密代碼。加密后的文件目錄還有一個(gè)名叫 pytransform 的運(yùn)行輔助包,它是解密文件所必須的,因此打包加密文件時(shí)需要同時(shí)將運(yùn)行輔助包打包進(jìn)去。

          pyinstaller -Fw --add-data "pytransform;pytransform" malicious.py



          PyArmor 使用分片式技術(shù)來保護(hù) Python 腳本。所謂分片保護(hù),就是單獨(dú)加密每一個(gè)函數(shù),在運(yùn)行腳本的時(shí)候,只有當(dāng)前調(diào)用的函數(shù)被解密,其他函數(shù)都沒有解密。而一旦函數(shù)執(zhí)行完成,就又會(huì)重新加密。這種方式相對于混淆來說,效果明顯要好的多。


          加密為 pye 文件

          pyconcrete 是另一個(gè) python 的文件加密庫,安裝它需要提供一個(gè)密鑰,用于之后對源碼文件進(jìn)行加密,同時(shí)由于過程中涉及 .c 文件的編譯,因此 Windows 下需要安裝 VC++ build tools,Linux 下需要安裝 GCC。


          解壓出 pyconcrete 庫源碼后,使用 setup 進(jìn)行安裝。

          python setup.py install



          安裝成功后復(fù)制 pyconcrete-admin.py 文件到項(xiàng)目文件夾就可以使用了。


          pyconcrete 可以將源碼文件夾下所有 py/pyc 文件通過 AES128 加密為 pye 文件,該文件無法被正常的 Pyhon 解釋器解釋,需要使用 pyconcrete 程序加載運(yùn)行。當(dāng)然也可以將函數(shù)定義部分提出來作為庫文件單獨(dú)加密,函數(shù)調(diào)用部分獨(dú)立出來作為一個(gè)入口,如下,將 malicious.py 文件拆分為 malicious_func.py 和 malicious_enter.py。



          對 malicious_func.py 文件單獨(dú)加密。

          python pyconcrete-admin.py compile --source=malicious_func.py --pye



          加密后在只有 malicious_enter.py 和 malicious_func.pye 兩個(gè)文件的情況下運(yùn)行 malicious_enter.py 文件,腳本是可以在本地正常運(yùn)行的,因?yàn)榻忉屍髟趯?dǎo)入 pye 文件時(shí)會(huì)自動(dòng)調(diào)用環(huán)境變量中的 pyconcrete 進(jìn)行解密。



          如果想將其打包為 exe 在其他機(jī)器上運(yùn)行,還需要進(jìn)行一些修改:

          1. 修改 malicious_enter.py 文件,在首部導(dǎo)入 pyconcrete,以及加密腳本中需要用到的庫,這是為了在調(diào)用 Pyinstaller 時(shí),將 pyconcrete 解密程序和腳本依賴的庫同時(shí)打包進(jìn) exe。pyconcrete 庫需要在其他庫之前導(dǎo)入,它會(huì)自動(dòng)和其他模塊掛鉤,在其目錄尋找 pye 文件,然后通過 _pyconcrete.pyd 對 pye 文件進(jìn)行解密。
          2. Pyinstaller 不會(huì)將 pye 文件主動(dòng)打包進(jìn) exe,需要在打包時(shí)通過 --add-data 添加。



          這樣打包出的 exe 就可以執(zhí)行了。



          使用 pyconcrete 加密的源碼在運(yùn)行時(shí)會(huì)調(diào)用 _pyconcrete.pyd 文件進(jìn)行解密,該文件內(nèi)存儲(chǔ)了用于解密源碼的密鑰。由于其密鑰隱藏在二進(jìn)制數(shù)據(jù)中,無法通過十六進(jìn)制編輯器直接看到,因此想要解密源碼,就必須對 _pyconcrete.pyd 文件進(jìn)行逆向分析,提取密鑰。


          編譯為 pyd 文件

          上面提到的 pyd 文件是 Python 的動(dòng)態(tài)鏈接庫,類似 Windows 下的 DLL 和 Linux 下的 SO,它是 Cython 結(jié)合 C 的編譯器編譯而來,涉及 C 的編譯, 因此同樣需要 VC++ build tools 或 GCC。


          實(shí)際上,Cython 是一門單獨(dú)的語言,專門用來寫 Python 的 C 擴(kuò)展。原本是為了解決 Python 語言的效率問題,但由于其有專門的轉(zhuǎn)換器可以將 .py 文件轉(zhuǎn)換為 .c 文件 (自動(dòng)加入大量 C-Python API ) 后編譯為 pyd,因此也可以利用這個(gè)特點(diǎn)來保護(hù) Python 源碼,下面為編譯方法:


          pyd 的文件為庫文件,所以也需要一個(gè) py 文件進(jìn)行調(diào)用,這里還是使用 malicious_enter.py 和 malicious_func.py 作為示例。


          創(chuàng)建一個(gè) py 文件,用于將 malicious_func.py 編譯為 pyd。

          # -*- coding: utf-8 -*-

          from distutils.core import setup
          from Cython.Build import cythonize

          setup(
          ? ?ext_modules = cythonize(['malicious_func.py',]),
          ? )


          cythonize 方法會(huì)將 malicious_func.py 的 Python 代碼轉(zhuǎn)換為 Cython 代碼,之后調(diào)用 setup 將 .c 文件編譯為 pyd。

          python build_pyd.py build_ext --inplace



          可以直接運(yùn)行。



          也可以打包為 exe,Pyinstaller 會(huì)自動(dòng)將 pyd 文件作為依賴導(dǎo)入。



          編譯為 pyd 后,想要了解源碼的邏輯就必須通過逆向來分析,相較于從 _pyconcrete.pyd 中提取密鑰解密 pye,這種直接將完整邏輯代碼編譯為二進(jìn)制文件的方式更不容易被逆向出來,邏輯寫的越復(fù)雜,逆向分析的代價(jià)就越高。


          最后,上面說的這些 Python 源碼保護(hù)方法其實(shí)正常情況下很少會(huì)用得到,既然使用了 Python,一般也不會(huì)有人刻意去隱藏自己的代碼。不過對于安全領(lǐng)域,這些方法還是有一定價(jià)值的。拿免殺來說,復(fù)雜的加解密流程,配合上面某些方法,說不定就能很好的隱藏自己的特征,養(yǎng)出一匹低調(diào)的馬兒。

          Python貓技術(shù)交流群開放啦!群里既有國內(nèi)一二線大廠在職員工,也有國內(nèi)外高校在讀學(xué)生,既有十多年碼齡的編程老鳥,也有中小學(xué)剛剛?cè)腴T的新人,學(xué)習(xí)氛圍良好!想入群的同學(xué),請?jiān)诠?hào)內(nèi)回復(fù)『交流群』,獲取貓哥的微信(謝絕廣告黨,非誠勿擾?。?/span>~

          近期熱門文章推薦:

          Python最會(huì)變魔術(shù)的魔術(shù)方法,我覺得是它!
          餓了么交易系統(tǒng) 5 年演化史
          Python 任務(wù)自動(dòng)化工具 tox 教程
          Python 為什么會(huì)有個(gè)奇怪的“...”對象?

          感謝創(chuàng)作者的好文
          瀏覽 147
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产黄色视频免费 | 色婷婷A V| 国产在线小电影 | 亚洲视频国产 | 夜夜操网站 |