pyencode-protector 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyencode_protector-0.3.0/PKG-INFO +276 -0
- pyencode_protector-0.3.0/README.md +253 -0
- pyencode_protector-0.3.0/pyproject.toml +41 -0
- pyencode_protector-0.3.0/setup.cfg +4 -0
- pyencode_protector-0.3.0/src/pyencode/__init__.py +5 -0
- pyencode_protector-0.3.0/src/pyencode/__main__.py +8 -0
- pyencode_protector-0.3.0/src/pyencode/builder.py +628 -0
- pyencode_protector-0.3.0/src/pyencode/cli.py +136 -0
- pyencode_protector-0.3.0/src/pyencode/code_hardening.py +185 -0
- pyencode_protector-0.3.0/src/pyencode/container.py +319 -0
- pyencode_protector-0.3.0/src/pyencode/crypto.py +319 -0
- pyencode_protector-0.3.0/src/pyencode/discovery.py +168 -0
- pyencode_protector-0.3.0/src/pyencode/errors.py +59 -0
- pyencode_protector-0.3.0/src/pyencode/integrity.py +88 -0
- pyencode_protector-0.3.0/src/pyencode/inventory.py +356 -0
- pyencode_protector-0.3.0/src/pyencode/manifest.py +196 -0
- pyencode_protector-0.3.0/src/pyencode/opaque.py +574 -0
- pyencode_protector-0.3.0/src/pyencode/runtime_template/__init__.py +99 -0
- pyencode_protector-0.3.0/src/pyencode/runtime_template/_build.py +15 -0
- pyencode_protector-0.3.0/src/pyencode/runtime_template/_mp_main.py +7 -0
- pyencode_protector-0.3.0/src/pyencode/runtime_template/_runtime.py +1488 -0
- pyencode_protector-0.3.0/src/pyencode/source.py +50 -0
- pyencode_protector-0.3.0/src/pyencode_protector.egg-info/PKG-INFO +276 -0
- pyencode_protector-0.3.0/src/pyencode_protector.egg-info/SOURCES.txt +31 -0
- pyencode_protector-0.3.0/src/pyencode_protector.egg-info/dependency_links.txt +1 -0
- pyencode_protector-0.3.0/src/pyencode_protector.egg-info/entry_points.txt +2 -0
- pyencode_protector-0.3.0/src/pyencode_protector.egg-info/requires.txt +1 -0
- pyencode_protector-0.3.0/src/pyencode_protector.egg-info/top_level.txt +1 -0
- pyencode_protector-0.3.0/tests/test_code_hardening.py +181 -0
- pyencode_protector-0.3.0/tests/test_core.py +153 -0
- pyencode_protector-0.3.0/tests/test_integration.py +1086 -0
- pyencode_protector-0.3.0/tests/test_inventory.py +191 -0
- pyencode_protector-0.3.0/tests/test_opaque.py +311 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyencode-protector
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A small cross-platform Python module protector
|
|
5
|
+
Author: PyEncode contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/VanThanBK/pyencode
|
|
8
|
+
Project-URL: Repository, https://github.com/VanThanBK/pyencode
|
|
9
|
+
Project-URL: Issues, https://github.com/VanThanBK/pyencode/issues
|
|
10
|
+
Keywords: python,code-protection,obfuscation,encryption
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Topic :: Security :: Cryptography
|
|
19
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: cryptography>=42
|
|
23
|
+
|
|
24
|
+
# PyEncode
|
|
25
|
+
|
|
26
|
+
PyEncode là công cụ bảo vệ ứng dụng CPython thuần Python, hướng tới Windows và
|
|
27
|
+
Linux. Mỗi module được biên dịch thành code object, tuần tự hóa bằng `marshal`,
|
|
28
|
+
nén rồi mã hóa xác thực bằng AES-256-GCM. Runtime giải mã và thực thi trong bộ
|
|
29
|
+
nhớ; source và bytecode rõ không được ghi ra file tạm.
|
|
30
|
+
|
|
31
|
+
Phiên bản 0.3 dùng định dạng PYE2:
|
|
32
|
+
|
|
33
|
+
- Mỗi artifact có tên ngẫu nhiên 128 bit, ví dụ
|
|
34
|
+
`myapp/7b82a40e9aa14e60db7094f2de3fbc33.pye`.
|
|
35
|
+
- PYE2 không có header JSON rõ; file chỉ chứa magic, nonce và ciphertext/tag.
|
|
36
|
+
- Tên module, entry point và cờ package nằm trong một module index được mã hóa.
|
|
37
|
+
- Manifest format 3 được ký Ed25519 và chứa hash SHA-256 của artifact.
|
|
38
|
+
- Runtime, launcher, resource và support tree do builder sao chép đều được ký,
|
|
39
|
+
key-bind và kiểm tra lại lúc `install()`.
|
|
40
|
+
- Loader chỉ trả một trampoline nhỏ cho `runpy`, không trả code object thật của
|
|
41
|
+
ứng dụng. Khóa module được dẫn xuất theo nhu cầu và không giữ trong finder.
|
|
42
|
+
|
|
43
|
+
Mục tiêu là tăng đáng kể chi phí phân tích so với `.pyc`, không phải tạo cơ chế
|
|
44
|
+
chống reverse tuyệt đối.
|
|
45
|
+
|
|
46
|
+
## Python được hỗ trợ
|
|
47
|
+
|
|
48
|
+
PyEncode 0.3 yêu cầu **CPython chuẩn 3.10 trở lên**. CI kiểm thử trực tiếp các
|
|
49
|
+
phiên bản 3.10 đến 3.15; các CPython feature release mới hơn được phép cài và
|
|
50
|
+
build theo hướng forward-compatible, rồi được đưa vào ma trận bắt buộc sau khi
|
|
51
|
+
có bản phát hành và dependency wheel tương ứng. PyPy và các interpreter khác
|
|
52
|
+
hiện chưa được hỗ trợ.
|
|
53
|
+
|
|
54
|
+
Runtime là Python thuần nên cùng source runtime dùng được trên Windows và
|
|
55
|
+
Linux. Tuy nhiên artifact đã build gắn với đúng CPython major/minor:
|
|
56
|
+
|
|
57
|
+
- build bằng CPython 3.11 → chạy bằng CPython 3.11;
|
|
58
|
+
- không chạy artifact đó bằng 3.10, 3.12 hoặc 3.14;
|
|
59
|
+
- muốn phát hành cho nhiều minor version, build một output bằng từng interpreter.
|
|
60
|
+
|
|
61
|
+
Nếu bundle có thêm `.pyd`, `.so`, `.dll` hoặc dependency native qua `--support`,
|
|
62
|
+
toàn bundle còn phụ thuộc hệ điều hành, kiến trúc và ABI của các file đó. Phạm
|
|
63
|
+
vi kiểm thử hiện tại là CPython GIL tiêu chuẩn; free-threaded/debug ABI cần build
|
|
64
|
+
và kiểm tra riêng.
|
|
65
|
+
|
|
66
|
+
## Cài đặt
|
|
67
|
+
|
|
68
|
+
Sau khi package được phát hành trên PyPI, cài bằng:
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
py -3.11 -m venv .venv
|
|
72
|
+
.venv\Scripts\Activate.ps1
|
|
73
|
+
python -m pip install --upgrade pip
|
|
74
|
+
python -m pip install pyencode-protector
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Tên lệnh CLI và import namespace vẫn là `pyencode`.
|
|
78
|
+
|
|
79
|
+
Để phát triển trực tiếp từ source, cài editable trong virtual environment:
|
|
80
|
+
|
|
81
|
+
Windows PowerShell:
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
py -3.11 -m venv .venv
|
|
85
|
+
.venv\Scripts\Activate.ps1
|
|
86
|
+
python -m pip install --upgrade pip
|
|
87
|
+
python -m pip install -e .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Linux:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
python3.11 -m venv .venv
|
|
94
|
+
source .venv/bin/activate
|
|
95
|
+
python -m pip install --upgrade pip
|
|
96
|
+
python -m pip install -e .
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Dependency runtime duy nhất là `cryptography>=42`.
|
|
100
|
+
|
|
101
|
+
## Sử dụng
|
|
102
|
+
|
|
103
|
+
Bảo vệ một file:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pyencode build hello.py -o dist/hello
|
|
107
|
+
python dist/hello/run.py
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Bảo vệ package có `__main__.py`:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pyencode build src/myapp -o dist/myapp
|
|
114
|
+
python dist/myapp/run.py
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Chỉ định entry module:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pyencode build src -o dist/app --entry myapp.__main__
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Ký và key-bind thêm dependency/support tree:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pyencode build src -o dist/app \
|
|
127
|
+
--entry myapp.main \
|
|
128
|
+
--support build/vendor _vendor \
|
|
129
|
+
--support public/config.json config.json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`--support SOURCE DEST` có thể lặp lại. Nếu `SOURCE` là thư mục, nội dung cây
|
|
133
|
+
được chép dưới `DEST`; nếu là file, `DEST` là đường dẫn file đầu ra. Symlink,
|
|
134
|
+
reparse point, đường dẫn thoát root và va chạm chữ hoa/thường bị từ chối.
|
|
135
|
+
|
|
136
|
+
Đích chính xác `_vendor` được xem là bootstrap dependency path cho host không
|
|
137
|
+
có `cryptography` cài sẵn (như Fusion). Chỉ đúng entry `_vendor` trong
|
|
138
|
+
`sys.path` được giữ lúc verifier nạp; mọi đường dẫn con khác trong bundle bị
|
|
139
|
+
cách ly. Vì thư viện mật mã vendored phải chạy trước khi Ed25519 có thể tự kiểm
|
|
140
|
+
chứng bundle, `_vendor` là một phần bootstrap được tin cậy theo thiết kế của
|
|
141
|
+
runtime Python thuần; chỉ đóng gói wheel lấy từ nguồn tin cậy.
|
|
142
|
+
|
|
143
|
+
Các tùy chọn chính:
|
|
144
|
+
|
|
145
|
+
- `-o`, `--output`: thư mục output; phải chưa tồn tại hoặc hoàn toàn rỗng.
|
|
146
|
+
- `-e`, `--entry`: dotted name của entry module/package.
|
|
147
|
+
- `--exclude GLOB`: loại đường dẫn khớp glob; có thể lặp.
|
|
148
|
+
- `--no-resources`: không sao chép file không phải Python trong source tree.
|
|
149
|
+
- `--keep-docstrings`: giữ docstring.
|
|
150
|
+
- `--optimize {0,1,2}`: mức tối ưu CPython.
|
|
151
|
+
- `--expires YYYY-MM-DD`: từ chối chạy sau ngày UTC đã cho.
|
|
152
|
+
- `--launcher PATH`: dùng launcher `.py` tùy biến.
|
|
153
|
+
- `--support SOURCE DEST`: sao chép, ký và key-bind file/cây hỗ trợ.
|
|
154
|
+
- `--rename-locals`: đổi tên metadata của local không phải tham số.
|
|
155
|
+
- `--allow-extra-data`: cho host tạo thêm file/thư mục dữ liệu không thực thi;
|
|
156
|
+
file đã ký và Python/native artifact lạ vẫn bị kiểm tra.
|
|
157
|
+
|
|
158
|
+
`--rename-locals` là opt-in vì code dùng alias động của `locals()`, đọc
|
|
159
|
+
`frame.f_locals`, debugger hoặc một số framework introspection có thể cần tên
|
|
160
|
+
local gốc. Argument, closure, line table và exception table luôn được giữ.
|
|
161
|
+
|
|
162
|
+
## Launcher tùy biến và host nhúng
|
|
163
|
+
|
|
164
|
+
Launcher được sao chép nguyên byte, ký và tham gia dẫn xuất khóa. Nó phải đặt:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import sys
|
|
168
|
+
sys.dont_write_bytecode = True
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
trước mọi import không phải bootstrap. Builder kiểm tra điều kiện tối thiểu này.
|
|
172
|
+
Một launcher ship an toàn cũng phải kiểm tra `pyencode_runtime` không chứa
|
|
173
|
+
`__pycache__`, `.pyc` hoặc file lạ **trước** khi import package; launcher mặc
|
|
174
|
+
định và pipeline Weldments đã có preflight đầy đủ.
|
|
175
|
+
|
|
176
|
+
Host như Fusion có thể nạp entry mà không ghi tên module rõ trong stub:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
import sys
|
|
180
|
+
sys.dont_write_bytecode = True
|
|
181
|
+
|
|
182
|
+
# Thực hiện preflight pyencode_runtime tại đây.
|
|
183
|
+
from pyencode_runtime import load_entry
|
|
184
|
+
|
|
185
|
+
implementation = load_entry()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`install()` cố ý trả `None`. `load_entry()` trả module entry cho embedding host;
|
|
189
|
+
`run()` thực thi entry với ngữ nghĩa `__main__`.
|
|
190
|
+
|
|
191
|
+
## Output
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
dist/app/
|
|
195
|
+
├── .pyencode-manifest.json
|
|
196
|
+
├── myapp/
|
|
197
|
+
│ ├── 14a0...f91c.pye
|
|
198
|
+
│ ├── 7b82...bc33.pye
|
|
199
|
+
│ └── assets/config.json
|
|
200
|
+
├── pyencode_runtime/
|
|
201
|
+
│ ├── __init__.py
|
|
202
|
+
│ ├── _build.py
|
|
203
|
+
│ ├── _mp_main.py
|
|
204
|
+
│ └── _runtime.py
|
|
205
|
+
├── requirements.txt
|
|
206
|
+
└── run.py
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Resource không được mã hóa, nhưng byte/path của resource build-time được ký và
|
|
210
|
+
key-bind. Mặc định toàn bộ cây output là bất biến: runtime từ chối mọi file hoặc
|
|
211
|
+
thư mục được thêm, xóa hay đổi byte.
|
|
212
|
+
|
|
213
|
+
Với host tự tạo metadata/cache cạnh add-in, `--allow-extra-data` cho phép thêm
|
|
214
|
+
file và thư mục dữ liệu mà không làm bundle chết. Chế độ này không cho sửa/xóa
|
|
215
|
+
file đã ký, không cho thêm `.py`, `.pyc`, native library hoặc `.pye` lạ, và vẫn
|
|
216
|
+
từ chối symlink/reparse point. Không dùng data bổ sung này làm đầu vào tin cậy
|
|
217
|
+
cho license hay quyết định bảo mật. Pipeline Weldments bật chế độ tương thích
|
|
218
|
+
này để Fusion có thể tạo `.vscode`, log hoặc cache khác nhau giữa các máy.
|
|
219
|
+
|
|
220
|
+
`__file__` của protected module là đường dẫn thật tới artifact ngẫu nhiên. Vì
|
|
221
|
+
vậy `Path(__file__).parent`, `pkgutil.get_data()` và `importlib.resources` kể cả
|
|
222
|
+
resource lồng thư mục vẫn hoạt động; code phụ thuộc basename/stem của
|
|
223
|
+
`__file__` sẽ thấy token ngẫu nhiên.
|
|
224
|
+
|
|
225
|
+
Import theo fullname, relative import, circular import, namespace package,
|
|
226
|
+
Unicode module, reload, `runpy.run_module()` và `multiprocessing` spawn của app
|
|
227
|
+
chạy qua launcher mặc định đều có test tích hợp. `pkgutil.iter_modules()` và
|
|
228
|
+
`walk_packages()` không thể tự khám phá tên protected child vì tên đó cố ý nằm
|
|
229
|
+
trong index mã hóa; import theo fullname đã biết vẫn hoạt động.
|
|
230
|
+
|
|
231
|
+
Với embedding host gọi `load_entry()`, spawn hoạt động nếu child cũng chạy lại
|
|
232
|
+
bootstrap của host. Trường hợp host chỉ bootstrap bên trong
|
|
233
|
+
`if __name__ == '__main__'` rồi spawn trực tiếp một target protected cần launcher
|
|
234
|
+
riêng; bootstrap spawn tự động hiện tập trung vào app chạy qua `run()`.
|
|
235
|
+
|
|
236
|
+
Phiên bản 0.3 dùng một namespace runtime cố định trong mỗi process. Không nạp
|
|
237
|
+
hai distribution PyEncode độc lập vào cùng một interpreter; chạy chúng ở hai
|
|
238
|
+
process riêng. Runtime namespace ngẫu nhiên/đa-bundle là hạng mục tương thích
|
|
239
|
+
trước khi công bố API ổn định 1.0.
|
|
240
|
+
|
|
241
|
+
## Giới hạn bảo mật
|
|
242
|
+
|
|
243
|
+
PyEncode làm khó phân tích tĩnh nhưng không thể giữ bí mật tuyệt đối trên máy do
|
|
244
|
+
người khác toàn quyền kiểm soát:
|
|
245
|
+
|
|
246
|
+
- Chế độ mặc định là offline nên key material vẫn phải nằm trong bundle. Người
|
|
247
|
+
có kinh nghiệm có thể đọc runtime và tái tạo quá trình dẫn xuất khóa.
|
|
248
|
+
- Code object phải xuất hiện trong bộ nhớ khi thực thi. Tracing, debugger,
|
|
249
|
+
monkeypatch hoặc hook native vẫn có thể quan sát code/runtime state.
|
|
250
|
+
- Integrity interlock chặn sửa file, tráo artifact, chèn code và re-sign đơn
|
|
251
|
+
giản; nó không biến Python thuần thành trust anchor native.
|
|
252
|
+
- Tên module/entry được che khi nằm yên, nhưng xuất hiện lúc import trong
|
|
253
|
+
`sys.modules`, traceback và runtime state. Thư mục package/resource vẫn có thể
|
|
254
|
+
tiết lộ một phần cấu trúc.
|
|
255
|
+
- `--expires` dựa trên đồng hồ hệ thống và không thay thế hệ thống license.
|
|
256
|
+
|
|
257
|
+
Đừng đặt API key/private key dài hạn trong client. Bước nâng cấp tạo khác biệt
|
|
258
|
+
bảo mật thực sự cho sản phẩm giá trị cao là external/envelope key lấy từ license
|
|
259
|
+
server hoặc keyring; native runtime chủ yếu tăng thêm chi phí phân tích.
|
|
260
|
+
|
|
261
|
+
## Kiểm thử
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
python -m unittest discover -v
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
CI chạy trên Windows và Ubuntu với CPython 3.10–3.15. Bộ test bao phủ PYE2,
|
|
268
|
+
module index mã hóa, KDF vectors, tamper/re-sign, resource/support integrity,
|
|
269
|
+
opaque filenames, import/package/namespace/Unicode, `runpy`, multiprocessing
|
|
270
|
+
spawn, launcher thường không có `-B`, policy hết hạn và output atomic.
|
|
271
|
+
|
|
272
|
+
## Phát hành
|
|
273
|
+
|
|
274
|
+
Quy trình phát hành production bằng GitHub OIDC được ghi tại
|
|
275
|
+
[RELEASING.md](RELEASING.md). Workflow chỉ upload lên PyPI khi push tag khớp
|
|
276
|
+
chính xác version, ví dụ `v0.3.0`.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# PyEncode
|
|
2
|
+
|
|
3
|
+
PyEncode là công cụ bảo vệ ứng dụng CPython thuần Python, hướng tới Windows và
|
|
4
|
+
Linux. Mỗi module được biên dịch thành code object, tuần tự hóa bằng `marshal`,
|
|
5
|
+
nén rồi mã hóa xác thực bằng AES-256-GCM. Runtime giải mã và thực thi trong bộ
|
|
6
|
+
nhớ; source và bytecode rõ không được ghi ra file tạm.
|
|
7
|
+
|
|
8
|
+
Phiên bản 0.3 dùng định dạng PYE2:
|
|
9
|
+
|
|
10
|
+
- Mỗi artifact có tên ngẫu nhiên 128 bit, ví dụ
|
|
11
|
+
`myapp/7b82a40e9aa14e60db7094f2de3fbc33.pye`.
|
|
12
|
+
- PYE2 không có header JSON rõ; file chỉ chứa magic, nonce và ciphertext/tag.
|
|
13
|
+
- Tên module, entry point và cờ package nằm trong một module index được mã hóa.
|
|
14
|
+
- Manifest format 3 được ký Ed25519 và chứa hash SHA-256 của artifact.
|
|
15
|
+
- Runtime, launcher, resource và support tree do builder sao chép đều được ký,
|
|
16
|
+
key-bind và kiểm tra lại lúc `install()`.
|
|
17
|
+
- Loader chỉ trả một trampoline nhỏ cho `runpy`, không trả code object thật của
|
|
18
|
+
ứng dụng. Khóa module được dẫn xuất theo nhu cầu và không giữ trong finder.
|
|
19
|
+
|
|
20
|
+
Mục tiêu là tăng đáng kể chi phí phân tích so với `.pyc`, không phải tạo cơ chế
|
|
21
|
+
chống reverse tuyệt đối.
|
|
22
|
+
|
|
23
|
+
## Python được hỗ trợ
|
|
24
|
+
|
|
25
|
+
PyEncode 0.3 yêu cầu **CPython chuẩn 3.10 trở lên**. CI kiểm thử trực tiếp các
|
|
26
|
+
phiên bản 3.10 đến 3.15; các CPython feature release mới hơn được phép cài và
|
|
27
|
+
build theo hướng forward-compatible, rồi được đưa vào ma trận bắt buộc sau khi
|
|
28
|
+
có bản phát hành và dependency wheel tương ứng. PyPy và các interpreter khác
|
|
29
|
+
hiện chưa được hỗ trợ.
|
|
30
|
+
|
|
31
|
+
Runtime là Python thuần nên cùng source runtime dùng được trên Windows và
|
|
32
|
+
Linux. Tuy nhiên artifact đã build gắn với đúng CPython major/minor:
|
|
33
|
+
|
|
34
|
+
- build bằng CPython 3.11 → chạy bằng CPython 3.11;
|
|
35
|
+
- không chạy artifact đó bằng 3.10, 3.12 hoặc 3.14;
|
|
36
|
+
- muốn phát hành cho nhiều minor version, build một output bằng từng interpreter.
|
|
37
|
+
|
|
38
|
+
Nếu bundle có thêm `.pyd`, `.so`, `.dll` hoặc dependency native qua `--support`,
|
|
39
|
+
toàn bundle còn phụ thuộc hệ điều hành, kiến trúc và ABI của các file đó. Phạm
|
|
40
|
+
vi kiểm thử hiện tại là CPython GIL tiêu chuẩn; free-threaded/debug ABI cần build
|
|
41
|
+
và kiểm tra riêng.
|
|
42
|
+
|
|
43
|
+
## Cài đặt
|
|
44
|
+
|
|
45
|
+
Sau khi package được phát hành trên PyPI, cài bằng:
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
py -3.11 -m venv .venv
|
|
49
|
+
.venv\Scripts\Activate.ps1
|
|
50
|
+
python -m pip install --upgrade pip
|
|
51
|
+
python -m pip install pyencode-protector
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Tên lệnh CLI và import namespace vẫn là `pyencode`.
|
|
55
|
+
|
|
56
|
+
Để phát triển trực tiếp từ source, cài editable trong virtual environment:
|
|
57
|
+
|
|
58
|
+
Windows PowerShell:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
py -3.11 -m venv .venv
|
|
62
|
+
.venv\Scripts\Activate.ps1
|
|
63
|
+
python -m pip install --upgrade pip
|
|
64
|
+
python -m pip install -e .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Linux:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
python3.11 -m venv .venv
|
|
71
|
+
source .venv/bin/activate
|
|
72
|
+
python -m pip install --upgrade pip
|
|
73
|
+
python -m pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Dependency runtime duy nhất là `cryptography>=42`.
|
|
77
|
+
|
|
78
|
+
## Sử dụng
|
|
79
|
+
|
|
80
|
+
Bảo vệ một file:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pyencode build hello.py -o dist/hello
|
|
84
|
+
python dist/hello/run.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Bảo vệ package có `__main__.py`:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pyencode build src/myapp -o dist/myapp
|
|
91
|
+
python dist/myapp/run.py
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Chỉ định entry module:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pyencode build src -o dist/app --entry myapp.__main__
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Ký và key-bind thêm dependency/support tree:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pyencode build src -o dist/app \
|
|
104
|
+
--entry myapp.main \
|
|
105
|
+
--support build/vendor _vendor \
|
|
106
|
+
--support public/config.json config.json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`--support SOURCE DEST` có thể lặp lại. Nếu `SOURCE` là thư mục, nội dung cây
|
|
110
|
+
được chép dưới `DEST`; nếu là file, `DEST` là đường dẫn file đầu ra. Symlink,
|
|
111
|
+
reparse point, đường dẫn thoát root và va chạm chữ hoa/thường bị từ chối.
|
|
112
|
+
|
|
113
|
+
Đích chính xác `_vendor` được xem là bootstrap dependency path cho host không
|
|
114
|
+
có `cryptography` cài sẵn (như Fusion). Chỉ đúng entry `_vendor` trong
|
|
115
|
+
`sys.path` được giữ lúc verifier nạp; mọi đường dẫn con khác trong bundle bị
|
|
116
|
+
cách ly. Vì thư viện mật mã vendored phải chạy trước khi Ed25519 có thể tự kiểm
|
|
117
|
+
chứng bundle, `_vendor` là một phần bootstrap được tin cậy theo thiết kế của
|
|
118
|
+
runtime Python thuần; chỉ đóng gói wheel lấy từ nguồn tin cậy.
|
|
119
|
+
|
|
120
|
+
Các tùy chọn chính:
|
|
121
|
+
|
|
122
|
+
- `-o`, `--output`: thư mục output; phải chưa tồn tại hoặc hoàn toàn rỗng.
|
|
123
|
+
- `-e`, `--entry`: dotted name của entry module/package.
|
|
124
|
+
- `--exclude GLOB`: loại đường dẫn khớp glob; có thể lặp.
|
|
125
|
+
- `--no-resources`: không sao chép file không phải Python trong source tree.
|
|
126
|
+
- `--keep-docstrings`: giữ docstring.
|
|
127
|
+
- `--optimize {0,1,2}`: mức tối ưu CPython.
|
|
128
|
+
- `--expires YYYY-MM-DD`: từ chối chạy sau ngày UTC đã cho.
|
|
129
|
+
- `--launcher PATH`: dùng launcher `.py` tùy biến.
|
|
130
|
+
- `--support SOURCE DEST`: sao chép, ký và key-bind file/cây hỗ trợ.
|
|
131
|
+
- `--rename-locals`: đổi tên metadata của local không phải tham số.
|
|
132
|
+
- `--allow-extra-data`: cho host tạo thêm file/thư mục dữ liệu không thực thi;
|
|
133
|
+
file đã ký và Python/native artifact lạ vẫn bị kiểm tra.
|
|
134
|
+
|
|
135
|
+
`--rename-locals` là opt-in vì code dùng alias động của `locals()`, đọc
|
|
136
|
+
`frame.f_locals`, debugger hoặc một số framework introspection có thể cần tên
|
|
137
|
+
local gốc. Argument, closure, line table và exception table luôn được giữ.
|
|
138
|
+
|
|
139
|
+
## Launcher tùy biến và host nhúng
|
|
140
|
+
|
|
141
|
+
Launcher được sao chép nguyên byte, ký và tham gia dẫn xuất khóa. Nó phải đặt:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
import sys
|
|
145
|
+
sys.dont_write_bytecode = True
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
trước mọi import không phải bootstrap. Builder kiểm tra điều kiện tối thiểu này.
|
|
149
|
+
Một launcher ship an toàn cũng phải kiểm tra `pyencode_runtime` không chứa
|
|
150
|
+
`__pycache__`, `.pyc` hoặc file lạ **trước** khi import package; launcher mặc
|
|
151
|
+
định và pipeline Weldments đã có preflight đầy đủ.
|
|
152
|
+
|
|
153
|
+
Host như Fusion có thể nạp entry mà không ghi tên module rõ trong stub:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
import sys
|
|
157
|
+
sys.dont_write_bytecode = True
|
|
158
|
+
|
|
159
|
+
# Thực hiện preflight pyencode_runtime tại đây.
|
|
160
|
+
from pyencode_runtime import load_entry
|
|
161
|
+
|
|
162
|
+
implementation = load_entry()
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`install()` cố ý trả `None`. `load_entry()` trả module entry cho embedding host;
|
|
166
|
+
`run()` thực thi entry với ngữ nghĩa `__main__`.
|
|
167
|
+
|
|
168
|
+
## Output
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
dist/app/
|
|
172
|
+
├── .pyencode-manifest.json
|
|
173
|
+
├── myapp/
|
|
174
|
+
│ ├── 14a0...f91c.pye
|
|
175
|
+
│ ├── 7b82...bc33.pye
|
|
176
|
+
│ └── assets/config.json
|
|
177
|
+
├── pyencode_runtime/
|
|
178
|
+
│ ├── __init__.py
|
|
179
|
+
│ ├── _build.py
|
|
180
|
+
│ ├── _mp_main.py
|
|
181
|
+
│ └── _runtime.py
|
|
182
|
+
├── requirements.txt
|
|
183
|
+
└── run.py
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Resource không được mã hóa, nhưng byte/path của resource build-time được ký và
|
|
187
|
+
key-bind. Mặc định toàn bộ cây output là bất biến: runtime từ chối mọi file hoặc
|
|
188
|
+
thư mục được thêm, xóa hay đổi byte.
|
|
189
|
+
|
|
190
|
+
Với host tự tạo metadata/cache cạnh add-in, `--allow-extra-data` cho phép thêm
|
|
191
|
+
file và thư mục dữ liệu mà không làm bundle chết. Chế độ này không cho sửa/xóa
|
|
192
|
+
file đã ký, không cho thêm `.py`, `.pyc`, native library hoặc `.pye` lạ, và vẫn
|
|
193
|
+
từ chối symlink/reparse point. Không dùng data bổ sung này làm đầu vào tin cậy
|
|
194
|
+
cho license hay quyết định bảo mật. Pipeline Weldments bật chế độ tương thích
|
|
195
|
+
này để Fusion có thể tạo `.vscode`, log hoặc cache khác nhau giữa các máy.
|
|
196
|
+
|
|
197
|
+
`__file__` của protected module là đường dẫn thật tới artifact ngẫu nhiên. Vì
|
|
198
|
+
vậy `Path(__file__).parent`, `pkgutil.get_data()` và `importlib.resources` kể cả
|
|
199
|
+
resource lồng thư mục vẫn hoạt động; code phụ thuộc basename/stem của
|
|
200
|
+
`__file__` sẽ thấy token ngẫu nhiên.
|
|
201
|
+
|
|
202
|
+
Import theo fullname, relative import, circular import, namespace package,
|
|
203
|
+
Unicode module, reload, `runpy.run_module()` và `multiprocessing` spawn của app
|
|
204
|
+
chạy qua launcher mặc định đều có test tích hợp. `pkgutil.iter_modules()` và
|
|
205
|
+
`walk_packages()` không thể tự khám phá tên protected child vì tên đó cố ý nằm
|
|
206
|
+
trong index mã hóa; import theo fullname đã biết vẫn hoạt động.
|
|
207
|
+
|
|
208
|
+
Với embedding host gọi `load_entry()`, spawn hoạt động nếu child cũng chạy lại
|
|
209
|
+
bootstrap của host. Trường hợp host chỉ bootstrap bên trong
|
|
210
|
+
`if __name__ == '__main__'` rồi spawn trực tiếp một target protected cần launcher
|
|
211
|
+
riêng; bootstrap spawn tự động hiện tập trung vào app chạy qua `run()`.
|
|
212
|
+
|
|
213
|
+
Phiên bản 0.3 dùng một namespace runtime cố định trong mỗi process. Không nạp
|
|
214
|
+
hai distribution PyEncode độc lập vào cùng một interpreter; chạy chúng ở hai
|
|
215
|
+
process riêng. Runtime namespace ngẫu nhiên/đa-bundle là hạng mục tương thích
|
|
216
|
+
trước khi công bố API ổn định 1.0.
|
|
217
|
+
|
|
218
|
+
## Giới hạn bảo mật
|
|
219
|
+
|
|
220
|
+
PyEncode làm khó phân tích tĩnh nhưng không thể giữ bí mật tuyệt đối trên máy do
|
|
221
|
+
người khác toàn quyền kiểm soát:
|
|
222
|
+
|
|
223
|
+
- Chế độ mặc định là offline nên key material vẫn phải nằm trong bundle. Người
|
|
224
|
+
có kinh nghiệm có thể đọc runtime và tái tạo quá trình dẫn xuất khóa.
|
|
225
|
+
- Code object phải xuất hiện trong bộ nhớ khi thực thi. Tracing, debugger,
|
|
226
|
+
monkeypatch hoặc hook native vẫn có thể quan sát code/runtime state.
|
|
227
|
+
- Integrity interlock chặn sửa file, tráo artifact, chèn code và re-sign đơn
|
|
228
|
+
giản; nó không biến Python thuần thành trust anchor native.
|
|
229
|
+
- Tên module/entry được che khi nằm yên, nhưng xuất hiện lúc import trong
|
|
230
|
+
`sys.modules`, traceback và runtime state. Thư mục package/resource vẫn có thể
|
|
231
|
+
tiết lộ một phần cấu trúc.
|
|
232
|
+
- `--expires` dựa trên đồng hồ hệ thống và không thay thế hệ thống license.
|
|
233
|
+
|
|
234
|
+
Đừng đặt API key/private key dài hạn trong client. Bước nâng cấp tạo khác biệt
|
|
235
|
+
bảo mật thực sự cho sản phẩm giá trị cao là external/envelope key lấy từ license
|
|
236
|
+
server hoặc keyring; native runtime chủ yếu tăng thêm chi phí phân tích.
|
|
237
|
+
|
|
238
|
+
## Kiểm thử
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
python -m unittest discover -v
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
CI chạy trên Windows và Ubuntu với CPython 3.10–3.15. Bộ test bao phủ PYE2,
|
|
245
|
+
module index mã hóa, KDF vectors, tamper/re-sign, resource/support integrity,
|
|
246
|
+
opaque filenames, import/package/namespace/Unicode, `runpy`, multiprocessing
|
|
247
|
+
spawn, launcher thường không có `-B`, policy hết hạn và output atomic.
|
|
248
|
+
|
|
249
|
+
## Phát hành
|
|
250
|
+
|
|
251
|
+
Quy trình phát hành production bằng GitHub OIDC được ghi tại
|
|
252
|
+
[RELEASING.md](RELEASING.md). Workflow chỉ upload lên PyPI khi push tag khớp
|
|
253
|
+
chính xác version, ví dụ `v0.3.0`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyencode-protector"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "A small cross-platform Python module protector"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "PyEncode contributors" }]
|
|
13
|
+
keywords = ["python", "code-protection", "obfuscation", "encryption"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: Microsoft :: Windows",
|
|
19
|
+
"Operating System :: POSIX :: Linux",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
22
|
+
"Topic :: Security :: Cryptography",
|
|
23
|
+
"Topic :: Software Development :: Build Tools",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"cryptography>=42",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/VanThanBK/pyencode"
|
|
31
|
+
Repository = "https://github.com/VanThanBK/pyencode"
|
|
32
|
+
Issues = "https://github.com/VanThanBK/pyencode/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
pyencode = "pyencode.cli:main"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools]
|
|
38
|
+
package-dir = { "" = "src" }
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|