Python 中如何安全地進行編譯和反編譯?

2021-02-20 Python中文社區

作者:江風引雨,Blog: http://blog.luzy.tk/

用Python寫腳本,小程序可謂非常方便,但它需要有特定的python環境才能運行,因此如果你想在別的電腦上運行時就會出現許多問題,就算已經安裝了Python,但版本可能相差較大,且相關的依賴庫沒有安裝,同樣不能正常運行。那有沒有一種工具能把我們寫的代碼和依賴庫以及編譯環境打包到一起呢?答案是肯定的,Pyinstaller就是一款不錯的工具,可以一鍵把你的代碼打包成exe文件。下面就先來聊一聊pyinstaller的使用方法。

一、用Pyinstaller打包python代碼

1. 安裝Pyinstaller

安裝過程非常簡單,在命令行中運行:

pip install pyinstaller

即可完成安裝。

2. 打包代碼

我寫了一段簡單的代碼作為例子,為了更清晰地演示打包過程,我將main()函數寫在了單獨的文件中,並將mylib.py作為一個庫引入。

#   mylib.py
#
import time


def myfunc():
    now = time.time()
    time_str = time.strftime("%Y-%m-%d %H:%M", time.localtime(now))
    print('現在是' + time_str)
    print("Have a nice day!")

#   main.py
#
import mylib
import os

if __name__ == "__main__":
    mylib.myfunc()
    os.system('pause')

pyinstaller.exe -F yourcode.py

PS D:\文檔\tmp\test> pyinstaller.exe -F main.py
580 INFO: PyInstaller: 3.6
582 INFO: Python: 3.7.3
585 INFO: Platform: Windows-10-10.0.18362-SP0
592 INFO: wrote D:\文檔\tmp\test\main.spec
596 INFO: UPX is not available.
611 INFO: Extending PYTHONPATH with paths
['D:\\文檔\\tmp\\test', 'D:\\文檔\\tmp\\test']
612 INFO: checking Analysis
614 INFO: Building Analysis because Analysis-00.toc is non existent
614 INFO: Initializing module dependency graph...
620 INFO: Caching module graph hooks...
657 INFO: Analyzing base_library.zip ...
13893 INFO: Caching module dependency graph...
14161 INFO: running Analysis Analysis-00.toc
14233 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable
  required by d:\programfiles\python\python.exe
15748 INFO: Analyzing D:\文檔\tmp\test\main.py
15751 INFO: Processing module hooks...
15752 INFO: Loading module hook "hook-encodings.py"...
16003 INFO: Loading module hook "hook-pydoc.py"...
16011 INFO: Loading module hook "hook-xml.py"...
16916 INFO: Looking for ctypes DLLs
16917 INFO: Analyzing run-time hooks ...
16925 INFO: Looking for dynamic libraries
17373 INFO: Looking for eggs
17374 INFO: Using Python library d:\programfiles\python\python37.dll
17374 INFO: Found binding redirects:
[]
17377 INFO: Warnings written to D:\文檔\tmp\test\build\main\warn-main.txt
17447 INFO: Graph cross-reference written to D:\文檔\tmp\test\build\main\xref-main.html
17506 INFO: checking PYZ
17507 INFO: Building PYZ because PYZ-00.toc is non existent
17508 INFO: Building PYZ (ZlibArchive) D:\文檔\tmp\test\build\main\PYZ-00.pyz
18600 INFO: Building PYZ (ZlibArchive) D:\文檔\tmp\test\build\main\PYZ-00.pyz completed successfully.
18637 INFO: checking PKG
18639 INFO: Building PKG because PKG-00.toc is non existent
18640 INFO: Building PKG (CArchive) PKG-00.pkg
22329 INFO: Building PKG (CArchive) PKG-00.pkg completed successfully.
22332 INFO: Bootloader d:\programfiles\python\lib\site-packages\PyInstaller\bootloader\Windows-64bit\run.exe
22334 INFO: checking EXE
22335 INFO: Building EXE because EXE-00.toc is non existent
22336 INFO: Building EXE from EXE-00.toc
22416 INFO: Appending archive to EXE D:\文檔\tmp\test\dist\main.exe
22641 INFO: Building EXE from EXE-00.toc completed successfully.

D:.
│  main.py
│  main.spec
│  mylib.py
├─build
│  └─main
│          Analysis-00.toc
│          base_library.zip
│          EXE-00.toc
│          main.exe.manifest
│          PKG-00.pkg
│          PKG-00.toc
│          PYZ-00.pyz
│          PYZ-00.toc
│          warn-main.txt
│          xref-main.html
└─dist
       main.exe

其中dist文件夾中就是生成的exe文件。

直接雙擊exe文件就能正常運行了。

但這篇文章的重點並不是介紹如何使用pyinstaller。此時,我又在思考,如此簡單的打包過程究竟安全嗎?打包成的exe文件會不會輕而易舉地被反編譯?

查閱了相關資料後發現,確實可能。

二、pyinstaller 的反編譯過程

1. 下載並使用pyinstxtractor解包

我們第一步採用的工具是pyinstxtractor.py,可以將pyinstaller 生成的exe文件解包成pyc文件。

項目地址:
https://sourceforge.net/projects/pyinstallerextractor/

之後把這個文件複製到待解包exe同級目錄下,運行如下命令:

python pyinstxtractor.py xx.exe

運行後生成xx.exe_extracted文件夾 ,裡面有一堆dll ,pyd等文件,我們需要注意的是裡面有一個xxx.exe.manifest文件,xxx可能與你的exe文件名不同,但這才是它的真實名字。然後找到一個叫xxx的沒有後綴名的文件,它其實就是你之前打包的那個.py文件對應的pyc文件。

找到一個叫xxx的沒有後綴名的文件

我們還注意到此目錄下還有一個PYZ-00.pyz_extracted文件夾,裡面都是引入的依賴庫,當然,我們自己寫的mylib.py也在其中,它也是我們反編譯的對象。

2. 反編譯pyc文件

找到了pyc文件,下面自然就是對它進行解密了。pyc其實是python程序執行過程中產生的緩存文件,我們直接運行python代碼時也會看到。對於這種格式的反編譯是比較簡單的,網上有許多工具,甚至還有很多在線工具。這裡為了方便,我就採用了一款在線工具。附上連結:http://tools.bugscaner.com/decompyle/

但直接將我們找到的pyc文件上傳會發現無法反編譯。原因是什麼呢?我們用十六進位編輯器(大家網上搜就行,我這裡用的是wxMEdit)打開這個文件,與之前直接運行py文件生成的pyc文件比較。

我先來看一下main.pyc的區別,左邊是我們解包出來的,右邊是運行生成的。

左邊是我們解包出來的,右邊是運行生成的

發現唯一的差別就是少了第一行16個字節(叫做 magic number 表示python的版本和編譯時間),那我們把它加上是不是就能正常解析了呢?確實是這樣,但沒有原始pyc文件怎麼辦?我們再到xx.exe_extracted文件夾裡找一找。會發現有一個叫struct的文件,我們給他加上後綴.pyc反編譯試試。發現成功反編譯出如下內容:

struct反編譯結果

這就說明它的 magic number 是正確的,那我們只要把它的前16個字節複製過去不就行了?我們再來試試,成了!main.py中的內容被成功反編譯出來了。

main.pyc反編譯結果

下面同理也能反編譯出mylib.py等依賴庫中的內容,不過值得注意的是,網上很多教程都沒有提到依賴庫的pyc文件缺少的字節數與主程序的不同!

左:struct文件 | 中:解包出的mylib.pyc | 右:正確的pyc文件

我們發現它不是缺少了16個字節,而是中間少了4個字節!那麼,我們只需要把struct頭部的16個字節覆蓋掉mylib.pyc的前12個字節。

改好之後再進行反編譯。

mylib.pyc反編譯內容

反編譯成功!不過中文字符被解析成了Unicode編碼,可以再使用相應工具轉換。

可以看到,通過pyinstaller打包的exe,還是能被較為容易地反編譯的。那麼有加密打包的方法嗎?其實pyinstaller本身就是支持加密的,下面就來說一說如何加密打包。

三、使用pyinstaller加密打包exe

其實只要在打包時加個key參數就能加密

pyinstaller.exe -F --key 123456 xxx.py

不過需要依賴pycrypto包,而python一般是不自帶這個包的。因此需要我們手動安裝。

1. 安裝pycrypto包

原本安裝過程應該很簡單,通過pip就能安裝。

pip install pycrypto

不過安裝過程好像要調用VS編譯器編譯,這就造成了莫名其妙的問題,如果你在安裝過程中沒有報錯,那麼恭喜你,你可以跳過這部分了。

我在網上找了很多解決方法都沒效,最後終於在StackOverflow上找到了一篇回答,完美解決了這個問題。原答案地址:https://stackoverflow.com/a/46921479/12954728

解決方法如下,前提是你電腦上安裝了Visual studio

以我的vs2015為例

1、在開始菜單中找到VS文件夾,用管理員身份運行這個」兼容工具命令提示符「

2、在你的VS安裝目錄下找到stdint.h這個文件,最好用everything搜索一下

3、輸入set CL=-FI"你的路徑\stdint.h"設置環境變量

4、然後再執行pip install pycrypto就能成功安裝了

2. 使用pyinstaller加密打包

現在執行如下命令就能加密打包了。key後面為密鑰可以隨便輸。

pyinstaller.exe -F --key 123456 xxx.py

3. 反編譯測試

那麼我們再來測試一下加密打包的exe還能不能被反編譯。

再次執行pyinstxtractor.py

PS > python pyinstxtractor.py .\main-encrypt.exe
  import imp
[*] Processing .\main-encrypt.exe
[*] Pyinstaller version: 2.1+
[*] Python version: 37
[*] Length of package: 5787283 bytes
[*] Found 63 files in CArchive
[*] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap
[+] Possible entry point: main
[*] Found 136 files in PYZ archive
[!] Error: Failed to decompress Crypto, probably encrypted. Extracting as is.
[!] Error: Failed to decompress Crypto.Cipher, probably encrypted. Extracting as is.
[!] Error: Failed to decompress __future__, probably encrypted. Extracting as is.
[!] Error: Failed to decompress _compat_pickle, probably encrypted. Extracting as is.
[!] Error: Failed to decompress argparse, probably encrypted. Extracting as is.
[!] Error: Failed to decompress ast, probably encrypted. Extracting as is.
[!] Error: Failed to decompress base64, probably encrypted. Extracting as is.
[!] Error: Failed to decompress bdb, probably encrypted. Extracting as is.
[!] Error: Failed to decompress bisect, probably encrypted. Extracting as is.
[!] Error: Failed to decompress bz2, probably encrypted. Extracting as is.
[!] Error: Failed to decompress calendar, probably encrypted. Extracting as is.
[!] Error: Failed to decompress cmd, probably encrypted. Extracting as is.
[!] Error: Failed to decompress code, probably encrypted. Extracting as is.
[!] Error: Failed to decompress codeop, probably encrypted. Extracting as is.

這次下面輸出了一長串Error,看來確實是被加密了。

我們再來看一看文件夾。

main-encrypt.exe_extracted文件夾裡似乎沒什麼變化,但PYZ-00.pyz_extracted文件夾裡全是加密文件,應該是無法反編譯了。

不過對外層文件夾中的main文件進行同樣操作後依然是可以反編譯出源碼的。

看來這個加密只針對依賴庫。

四、總結

如果你不希望別人得到你的源碼,建議將你程序的入口函數寫在一個單獨的文件裡,並採用加密方式打包exe。這樣的話,就算別人嘗試反編譯也只能得到你的入口函數。

原文地址:https://blog.luzy.tk/archives/155

投稿郵箱:pythonpost@163.com

【Python中文社區專屬優惠碼】

指定特惠伺服器每人限購1臺

▼ 點擊閱讀原文,即享阿里雲產品1折優惠起

相關焦點

  • 反編譯apk教程下篇—利用軟體進行反編譯
    昨天給大家寫了反編譯apk的準備工作,果然不出我所料,閱讀量比平時低了將近一半,也許是本人文筆不好,也有可能是教程太枯燥,導致看的人很少。但是,咱不能虎頭蛇尾,所以我還是決定含淚把這個教程寫完。昨天已經給大家寫了如何配置java環境,今天就進入正題,說下如何反編譯apk。其實反編譯並沒有想像中的那麼難,我用的也是網上大神提供的工具。
  • Python反編譯之字節碼
    Python是如何工作的Python 通常被描述為一種解釋語言,在這種語言中,你的原始碼在程序運行時被翻譯成CPU指令,但這只是說對了部分。和許多解釋型語言一樣,Python 實際上將原始碼編譯為虛擬機的一組指令,Python 解釋器就是該虛擬機的實現。其中這種中間格式稱為「字節碼」。
  • 一日一技:如何將exe反編譯成Python腳本
    請看下文:今天我將教大家如何反編譯exe文件,即將自己或別人寫好的exe,還原成Python源碼。以最近寫Python一鍵自動整理歸類文件為例進行演示,運行所需的代碼和文件都會在文末提供給大家。uncompyle6可以反編譯.pyc後綴結尾的文件,兩種命令形式:uncompyle6 xxx.pyc>xxx.pyuncompyle6 -o xxx.py xxx.pyc以前面編碼過程中生成的緩存為例進行演示:uncompyle6 auto_organize.cpython
  • APK反編譯及防護技術
    Android端的項目越來越多,面臨的安全問題也很多。本文我們主要一些android反編譯與防護技術方法。(三)實戰下面我們來看看如何反編譯一個APK直接通過Android killer打開反編譯的對象APK就可以了。
  • 別再問我exe反編譯成Python腳本了!
    個人博客地址:https://blog.csdn.net/as604049322今天我將教大家如何反編譯exe文件,即將自己或別人寫好的exe,還原成Python源碼。以最近寫Python一鍵自動整理歸類文件為例進行演示,運行所需的代碼和文件都會在文末提供給大家。
  • Java代碼的編譯與反編譯
    2、用編譯程序產生目標程序的動作。 編譯就是把高級語言變成計算機可以識別的2進位語言,計算機只認識1和0,編譯程序把人們熟悉的語言換成2進位的。 編譯程序把一個源程序翻譯成目標程序的工作過程分為五個階段:詞法分析;語法分析;語義檢查和中間代碼生成;代碼優化;目標代碼生成。主要是進行詞法分析和語法分析,又稱為源程序分析,分析過程中發現有語法錯誤,給出提示信息。
  • 必學,唬住面試官的Python技巧,Python反編譯字節碼
    Python是如何工作的 Python 通常被描述為一種解釋語言,在這種語言中,你的原始碼在程序運行時被翻譯成CPU指令,但這只是說對了部分。和許多解釋型語言一樣,Python 實際上將原始碼編譯為虛擬機的一組指令,Python 解釋器就是該虛擬機的實現。其中這種中間格式稱為「字節碼」。
  • python編譯後的pyd爆破
    py: python 腳本文件(source code)pyc: 腳本文件編譯得到的字節碼, 二進位文件,python文件經過編譯器編譯之後的文件。可以提高文件加載速度。pyo: 腳本文件開啟優化編譯選項(-O)編譯得到的字節碼,二進位文件,優化編譯後的文件。可以通過python -O file.py生成。pyd: 基本的Windows DLL文件,python的動態連結庫。
  • Android Apk的反編譯和加密
    下面就簡單介紹一下如何將我們從網上下載的Apk文件進行反編譯得到我們想要獲得的資源文件和源碼。一、Apk文件組成  Android的應用程式APK文件說到底也是一個壓縮文件,那麼可以通過解壓縮得打裡面的文件內容,不過很顯然,當你去解壓完去查看的時候,發現裡面的很多東西和你想像中的不太一樣。
  • 面試官:如何防止你的 jar 包被反編譯?
    這將對反編譯帶來一定的困難。對於私有函數、局部變量,通常可以改變它們的符號,而不影響程序的運行。但是對於一些接口名稱、公有函數、成員變量,如果有其它外部模塊需要引用這些符號,我們往往需要保留這些名稱,否則外部模塊找不到這些名稱的方法和變量。因此,多數的混淆工具對於符號混淆,都提供了豐富的選項,讓用戶選擇是否、如何進行符號混淆。
  • 如何反編譯Android 5.0 framework
    為了更好的適配,我們不得不對framework層進行反編譯,在Android更新到5.0後,開發人員對framework的反編譯也出現了新的變化。以前分散在framework文件夾根目錄裡的那些odex文件全部集中在了framework文件夾中的arm(或arm64)子文件夾中,而且通過正常的反編譯發現這些odex並不是像5.0以前一樣是我們所需要的東西。圖:arm文件夾裡的各種odex文件2.
  • APK反編譯步驟
    但是我們通過網上提供了一些工具,還是可以將apk進行反編譯的,apk反編譯之後,我們就可以看到開發這個應用使用的資源文件(圖片)、layout、樣式、相關的實現代碼等,apk反編譯也算是Android開發中一個比較實用的技巧吧,當我們對別人開發好的應用感興趣時,我們就可以通過這種技術手段將別人打包好的apk進行反編譯,繼而可以看到我們感興趣的內容,(註:反編譯不是讓各位開發者去對一個應用破解搞重裝什麼的
  • 面試官一問:如何防止你的 jar 包被反編譯?
    Part1序由於Java字節碼的抽象級別較高,因此它們較容易被反編譯。本文介紹了幾種常用的方法,用於保護Java字節碼不被反編譯。通常,這些方法不能夠絕對防止程序被反編譯,而是加大反編譯的難度而已,因為這些方法都有自己的使用環境和弱點。
  • 你會信任你的AutoIT反編譯程序嗎?
    AutoIt 目前最新是v3版本,這是一個使用類似BASIC腳本語言的免費軟體,它的設計用於Windows GUI(圖形用戶界面)中進行自動化操作,利用模擬鍵盤按鍵,滑鼠移動和窗口/控制項的組合來實現自動化任務。而這是其它語言不可能做到或無可靠方法實現的(例如VBScript和SendKeys)。
  • .net反編譯的九款神器
    2、ILSpy     ILspy是一款非常優秀的.net平臺反編譯軟體,使用ILspy,可以輕鬆的反編譯C#及VB程序,軟體本身是C#程序,是一款開源軟體。JustDecompile與Reflector相比的話,個人更喜歡JustDecompile,因為他免費,而且對於一些C#動態類型的反編譯效果比較好,對於某個第三方程序集,如果它缺乏文檔,或者是一個bug 或性能問題的根源,反編譯往往是最快捷的解決方案。
  • Android逆向之旅---反編譯利器Apktool和Jadx源碼分析以及常見反編譯錯誤修復
    和res下面的一下xml文件都是亂碼的,因為他們是遵循Android中的arsc文件格式,關於這個格式不了解的同學可以網上搜一下,關於這個文件格式我在之前的幾篇文章中做了格式解析:Android中如何解析資源文件樣式 其實不管是什麼文件格式,都是有文件格式的說明文檔的,只要按照這個說明文章去做解析即可第二件事:使用apktool工具進行反編譯
  • 手把手教你搞懂Android反編譯
    什麼是反編譯我們知道,Android的程序打包後會生成一個APK文件,這個文件可以直接安裝到任何Android手機上,因此,反編譯就是對這個APK進行反編譯。Android的反編譯分成兩個部分:1、一個是對代碼反編譯,也就是java文件的反編譯。2、一個是對資源反編譯,也就是res文件的反編譯。
  • 我終於做了APP反編譯!
    APP反編譯
  • apk 下載 反編譯一氣呵成
    那麼如何模仿呢,我覺得需要以下這麼幾步。 1、下載apk文件。 2、獲取圖片資源 3、獲取代碼資源獲取 Apk 文件Google Paly 的 apk 獲取方式方式一:瀏覽器輸入 https://apps.evozi.com/apk-downloader/ ,輸入 apk 的網頁地址。
  • 7款開源Java反編譯工具
    今天我們要來分享一些關於Java的反編譯工具,反編譯聽起來是一個非常高上大的技術詞彙,通俗的說,反編譯是一個對目標可執行程序進行逆向分析,從而得到原始代碼的過程