用 Cython + Pyinstaller 安全地打包程式
前言
Pyinstaller 是很多人打包 Python 程式執行檔的第一選擇,但單純使用 Pyinstaller 打包的執行檔很容易被反編譯,如果要保護辛苦寫的程式碼不被輕易看光,可以搭配 Cython 將程式預先編譯成二進制檔案 (pyd),如此一來反編譯的難度會提升不少。
程式調整
- 由於 Pyinstaller 必須要指定一個 py 檔開始打包,這表示主程式會是明碼的,這時候可以再多建一個入口程式,由入口程式去 import 並執行主程式
- Pyinstaller 會自動解析 py 程式 import 的套並打包,但無法解析 pyd 檔,所以可以直接在入口程式 import 所有被使用到的套件,包括各程式彼此間的 import
下面舉個簡單的例子:
原始程式
app.py
1
2
3
4
5
6
7
8
9from a import a
from b import b
def app():
a()
b()
if __name__ == "__main__":
app()a.py
1
2
3
4
5
6from datetime import datetime
import time
def a():
print(f"It's a. {datetime.now()}")
time.sleep(0.5)b.py
1
2
3
4from datetime import datetime
def b():
print(f"It's b. {datetime.now()}")
入口程式
main.py
1
2
3
4
5
6
7from a import a
from b import b
from app import app
from datetime import datetime
import time
app()
Cython 編譯
安裝 Cython
1
pip install cython
建立
setup.py
這裡可以一個一個列出來,也可以用萬用字元 (*)1
2
3
4
5
6from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize(["app.py", "a.py", "b.py"])
)編譯
1
python setup.py build_ext --inplace
編譯完後目錄會多出 c 檔跟 pyd 檔,類似於下面這樣:
1
2
3
4
5
6
7
8
9
10
11
12├── build
├── a.c
├── a.cp38-win_amd64.pyd
├── a.py
├── app.c
├── app.cp38-win_amd64.pyd
├── app.py
├── b.c
├── b.cp38-win_amd64.pyd
├── b.py
├── main.py
└── setup.py我們可以把編譯後的 py 檔移掉只留下 pyd 檔,會發現
main.py
一樣可以正常執行。
Pyinstaller 打包
- 安裝 Pyinstaller
1
pip install pyinstaller
- 打包 打包完的執行檔會放在 dist 目錄下,大功告成。
1
pyinstaller --onefile main.py