jh-pyenc 0.0.3__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.
- jh_pyenc-0.0.3/LICENSE +15 -0
- jh_pyenc-0.0.3/MANIFEST.in +3 -0
- jh_pyenc-0.0.3/PKG-INFO +157 -0
- jh_pyenc-0.0.3/README.md +127 -0
- jh_pyenc-0.0.3/jh_pyenc/__init__.py +2 -0
- jh_pyenc-0.0.3/jh_pyenc/__main__.py +3 -0
- jh_pyenc-0.0.3/jh_pyenc/build_pyd.py +90 -0
- jh_pyenc-0.0.3/jh_pyenc/cli.py +210 -0
- jh_pyenc-0.0.3/jh_pyenc/encryptor.py +236 -0
- jh_pyenc-0.0.3/jh_pyenc/runtime_template.py +3 -0
- jh_pyenc-0.0.3/jh_pyenc.egg-info/PKG-INFO +157 -0
- jh_pyenc-0.0.3/jh_pyenc.egg-info/SOURCES.txt +16 -0
- jh_pyenc-0.0.3/jh_pyenc.egg-info/dependency_links.txt +1 -0
- jh_pyenc-0.0.3/jh_pyenc.egg-info/entry_points.txt +2 -0
- jh_pyenc-0.0.3/jh_pyenc.egg-info/requires.txt +3 -0
- jh_pyenc-0.0.3/jh_pyenc.egg-info/top_level.txt +1 -0
- jh_pyenc-0.0.3/setup.cfg +4 -0
- jh_pyenc-0.0.3/setup.py +37 -0
jh_pyenc-0.0.3/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
jh_pyenc-0.0.3/PKG-INFO
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jh_pyenc
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Python代码处理工具 - 简单加密处理
|
|
5
|
+
Author: JiJiahui
|
|
6
|
+
Author-email: tn-jh@foxmail.com
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Description-Content-Type: text/plain
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: cryptography>=3.4.7
|
|
19
|
+
Requires-Dist: cython>=0.29
|
|
20
|
+
Requires-Dist: setuptools>=45
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: requires-dist
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# jh_pyenc - Python Code Processing Tool (Ultimate Hardened Edition)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
```bash
|
|
35
|
+
pip install jh_pyenc
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Dependencies
|
|
39
|
+
- cryptography
|
|
40
|
+
- cython
|
|
41
|
+
- setuptools
|
|
42
|
+
- C compiler required (Visual Studio Build Tools / Xcode CLT / build-essential)
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
```bash
|
|
46
|
+
jh_pyenc ./myproject -p "my_password"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Commands
|
|
50
|
+
```bash
|
|
51
|
+
jh_pyenc <directory> [options]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Options:
|
|
55
|
+
| Option | Description |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| `-p, --password` | Password (default: default_key_2024) |
|
|
58
|
+
| `--whl` | Package runtime as .whl file |
|
|
59
|
+
| `--no-backup` | Do not backup original files |
|
|
60
|
+
|
|
61
|
+
### Security Options:
|
|
62
|
+
| Option | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `--expire DATE` | Expiration date YYYY-MM-DD |
|
|
65
|
+
| `--mac MAC` | Bind to MAC address (can specify multiple times) |
|
|
66
|
+
| `--remote URL` | Remote validation URL |
|
|
67
|
+
| `--no-anti-debug` | Disable anti-debugging |
|
|
68
|
+
| `--no-junk` | Disable junk code insertion |
|
|
69
|
+
| `--debug-threshold` | Anti-debugging time threshold (default: 0.3 seconds) |
|
|
70
|
+
| `--trap MODE` | Handler mode: random/exit/hang/error/silent |
|
|
71
|
+
| `--env-bind` | Enable environment binding |
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
```bash
|
|
75
|
+
# Basic processing
|
|
76
|
+
jh_pyenc ./myproject -p "secret"
|
|
77
|
+
|
|
78
|
+
# With expiration
|
|
79
|
+
jh_pyenc ./myproject -p "secret" --expire 2025-12-31
|
|
80
|
+
|
|
81
|
+
# With MAC binding
|
|
82
|
+
jh_pyenc ./myproject -p "secret" --mac aa:bb:cc:dd:ee:ff
|
|
83
|
+
|
|
84
|
+
# Package as wheel
|
|
85
|
+
jh_pyenc ./myproject -p "secret" --whl
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Important Notes
|
|
89
|
+
- Please keep your password safe and secure
|
|
90
|
+
- Original files are automatically backed up to `xxx_bak/` directory
|
|
91
|
+
- After processing, the corresponding pyruntime is required to run; can be placed in site-packages for convenient access
|
|
92
|
+
- This is a simple obfuscation tool and does not meet production-grade security standards
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 中文说明
|
|
97
|
+
|
|
98
|
+
### 安装
|
|
99
|
+
```bash
|
|
100
|
+
pip install jh_pyenc-0.0.3-py3-none-any.whl
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 依赖
|
|
104
|
+
- cryptography
|
|
105
|
+
- cython
|
|
106
|
+
- setuptools
|
|
107
|
+
- 需要 C 编译器 (Visual Studio Build Tools / Xcode CLT / build-essential)
|
|
108
|
+
|
|
109
|
+
### 快速开始
|
|
110
|
+
```bash
|
|
111
|
+
jh_pyenc ./myproject -p "my_password"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 命令
|
|
115
|
+
```bash
|
|
116
|
+
jh_pyenc <目录> [选项]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 选项:
|
|
120
|
+
| 选项 | 说明 |
|
|
121
|
+
|------|------|
|
|
122
|
+
| `-p, --password` | 密码 (默认: default_key_2024) |
|
|
123
|
+
| `--whl` | 打包运行时为 .whl 文件 |
|
|
124
|
+
| `--no-backup` | 不备份原文件 |
|
|
125
|
+
|
|
126
|
+
### 安全选项:
|
|
127
|
+
| 选项 | 说明 |
|
|
128
|
+
|------|------|
|
|
129
|
+
| `--expire DATE` | 过期日期 YYYY-MM-DD |
|
|
130
|
+
| `--mac MAC` | 绑定MAC地址 (可多次指定) |
|
|
131
|
+
| `--remote URL` | 远程验证URL |
|
|
132
|
+
| `--no-anti-debug` | 关闭反调试 |
|
|
133
|
+
| `--no-junk` | 关闭垃圾代码 |
|
|
134
|
+
| `--debug-threshold` | 反调试时间阈值 (默认: 0.3秒) |
|
|
135
|
+
| `--trap MODE` | 处理模式: random/exit/hang/error/silent |
|
|
136
|
+
| `--env-bind` | 启用环境绑定 |
|
|
137
|
+
|
|
138
|
+
### 示例
|
|
139
|
+
```bash
|
|
140
|
+
# 基础处理
|
|
141
|
+
jh_pyenc ./myproject -p "secret"
|
|
142
|
+
|
|
143
|
+
# 带过期时间
|
|
144
|
+
jh_pyenc ./myproject -p "secret" --expire 2025-12-31
|
|
145
|
+
|
|
146
|
+
# 绑定MAC地址
|
|
147
|
+
jh_pyenc ./myproject -p "secret" --mac aa:bb:cc:dd:ee:ff
|
|
148
|
+
|
|
149
|
+
# 打包为wheel
|
|
150
|
+
jh_pyenc ./myproject -p "secret" --whl
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 注意事项
|
|
154
|
+
- 请妥善保管处理密码
|
|
155
|
+
- 原文件自动备份到 `xxx_bak/` 目录
|
|
156
|
+
- 处理后需要相应的 pyruntime 才能运行,可以放到 site-packages 方便调用
|
|
157
|
+
- 只是简单的混淆处理,无法达到生产环境的安全水准
|
jh_pyenc-0.0.3/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# jh_pyenc - Python Code Processing Tool (Ultimate Hardened Edition)
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
```bash
|
|
5
|
+
pip install jh_pyenc
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
## Dependencies
|
|
9
|
+
- cryptography
|
|
10
|
+
- cython
|
|
11
|
+
- setuptools
|
|
12
|
+
- C compiler required (Visual Studio Build Tools / Xcode CLT / build-essential)
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
```bash
|
|
16
|
+
jh_pyenc ./myproject -p "my_password"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
```bash
|
|
21
|
+
jh_pyenc <directory> [options]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Options:
|
|
25
|
+
| Option | Description |
|
|
26
|
+
|--------|-------------|
|
|
27
|
+
| `-p, --password` | Password (default: default_key_2024) |
|
|
28
|
+
| `--whl` | Package runtime as .whl file |
|
|
29
|
+
| `--no-backup` | Do not backup original files |
|
|
30
|
+
|
|
31
|
+
### Security Options:
|
|
32
|
+
| Option | Description |
|
|
33
|
+
|--------|-------------|
|
|
34
|
+
| `--expire DATE` | Expiration date YYYY-MM-DD |
|
|
35
|
+
| `--mac MAC` | Bind to MAC address (can specify multiple times) |
|
|
36
|
+
| `--remote URL` | Remote validation URL |
|
|
37
|
+
| `--no-anti-debug` | Disable anti-debugging |
|
|
38
|
+
| `--no-junk` | Disable junk code insertion |
|
|
39
|
+
| `--debug-threshold` | Anti-debugging time threshold (default: 0.3 seconds) |
|
|
40
|
+
| `--trap MODE` | Handler mode: random/exit/hang/error/silent |
|
|
41
|
+
| `--env-bind` | Enable environment binding |
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
```bash
|
|
45
|
+
# Basic processing
|
|
46
|
+
jh_pyenc ./myproject -p "secret"
|
|
47
|
+
|
|
48
|
+
# With expiration
|
|
49
|
+
jh_pyenc ./myproject -p "secret" --expire 2025-12-31
|
|
50
|
+
|
|
51
|
+
# With MAC binding
|
|
52
|
+
jh_pyenc ./myproject -p "secret" --mac aa:bb:cc:dd:ee:ff
|
|
53
|
+
|
|
54
|
+
# Package as wheel
|
|
55
|
+
jh_pyenc ./myproject -p "secret" --whl
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Important Notes
|
|
59
|
+
- Please keep your password safe and secure
|
|
60
|
+
- Original files are automatically backed up to `xxx_bak/` directory
|
|
61
|
+
- After processing, the corresponding pyruntime is required to run; can be placed in site-packages for convenient access
|
|
62
|
+
- This is a simple obfuscation tool and does not meet production-grade security standards
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 中文说明
|
|
67
|
+
|
|
68
|
+
### 安装
|
|
69
|
+
```bash
|
|
70
|
+
pip install jh_pyenc-0.0.3-py3-none-any.whl
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 依赖
|
|
74
|
+
- cryptography
|
|
75
|
+
- cython
|
|
76
|
+
- setuptools
|
|
77
|
+
- 需要 C 编译器 (Visual Studio Build Tools / Xcode CLT / build-essential)
|
|
78
|
+
|
|
79
|
+
### 快速开始
|
|
80
|
+
```bash
|
|
81
|
+
jh_pyenc ./myproject -p "my_password"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 命令
|
|
85
|
+
```bash
|
|
86
|
+
jh_pyenc <目录> [选项]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 选项:
|
|
90
|
+
| 选项 | 说明 |
|
|
91
|
+
|------|------|
|
|
92
|
+
| `-p, --password` | 密码 (默认: default_key_2024) |
|
|
93
|
+
| `--whl` | 打包运行时为 .whl 文件 |
|
|
94
|
+
| `--no-backup` | 不备份原文件 |
|
|
95
|
+
|
|
96
|
+
### 安全选项:
|
|
97
|
+
| 选项 | 说明 |
|
|
98
|
+
|------|------|
|
|
99
|
+
| `--expire DATE` | 过期日期 YYYY-MM-DD |
|
|
100
|
+
| `--mac MAC` | 绑定MAC地址 (可多次指定) |
|
|
101
|
+
| `--remote URL` | 远程验证URL |
|
|
102
|
+
| `--no-anti-debug` | 关闭反调试 |
|
|
103
|
+
| `--no-junk` | 关闭垃圾代码 |
|
|
104
|
+
| `--debug-threshold` | 反调试时间阈值 (默认: 0.3秒) |
|
|
105
|
+
| `--trap MODE` | 处理模式: random/exit/hang/error/silent |
|
|
106
|
+
| `--env-bind` | 启用环境绑定 |
|
|
107
|
+
|
|
108
|
+
### 示例
|
|
109
|
+
```bash
|
|
110
|
+
# 基础处理
|
|
111
|
+
jh_pyenc ./myproject -p "secret"
|
|
112
|
+
|
|
113
|
+
# 带过期时间
|
|
114
|
+
jh_pyenc ./myproject -p "secret" --expire 2025-12-31
|
|
115
|
+
|
|
116
|
+
# 绑定MAC地址
|
|
117
|
+
jh_pyenc ./myproject -p "secret" --mac aa:bb:cc:dd:ee:ff
|
|
118
|
+
|
|
119
|
+
# 打包为wheel
|
|
120
|
+
jh_pyenc ./myproject -p "secret" --whl
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 注意事项
|
|
124
|
+
- 请妥善保管处理密码
|
|
125
|
+
- 原文件自动备份到 `xxx_bak/` 目录
|
|
126
|
+
- 处理后需要相应的 pyruntime 才能运行,可以放到 site-packages 方便调用
|
|
127
|
+
- 只是简单的混淆处理,无法达到生产环境的安全水准
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""编译pyruntime.py为.pyd并打包whl"""
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
def build(runtime_dir):
|
|
9
|
+
"""编译pyruntime.py为.pyd"""
|
|
10
|
+
os.chdir(runtime_dir)
|
|
11
|
+
|
|
12
|
+
for d in ['build', 'dist', '_whl_build']:
|
|
13
|
+
if Path(d).exists():
|
|
14
|
+
shutil.rmtree(d, ignore_errors=True)
|
|
15
|
+
for f in list(Path('.').glob('*.pyd')) + list(Path('.').glob('*.c')):
|
|
16
|
+
f.unlink()
|
|
17
|
+
|
|
18
|
+
print(" [1/2] Cython .py -> .c ...")
|
|
19
|
+
r = subprocess.run(
|
|
20
|
+
[sys.executable, '-m', 'cython', '-3', 'pyruntime.py', '-o', 'pyruntime.c'],
|
|
21
|
+
capture_output=True, text=True
|
|
22
|
+
)
|
|
23
|
+
if r.returncode != 0:
|
|
24
|
+
print(f" Error: {r.stderr}")
|
|
25
|
+
return False
|
|
26
|
+
print(" OK")
|
|
27
|
+
|
|
28
|
+
print(" [2/2] Compile .c -> .pyd ...")
|
|
29
|
+
code = """
|
|
30
|
+
from distutils.core import setup, Extension
|
|
31
|
+
import shutil
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
setup(
|
|
34
|
+
name='pyruntime',
|
|
35
|
+
ext_modules=[Extension('pyruntime', ['pyruntime.c'])],
|
|
36
|
+
script_args=['build_ext', '--inplace']
|
|
37
|
+
)
|
|
38
|
+
for p in Path('build').rglob('*.pyd'):
|
|
39
|
+
shutil.copy2(p, 'pyruntime.pyd')
|
|
40
|
+
"""
|
|
41
|
+
r = subprocess.run([sys.executable, '-c', code], capture_output=True, text=True)
|
|
42
|
+
|
|
43
|
+
pyd = Path('pyruntime.pyd')
|
|
44
|
+
if pyd.exists():
|
|
45
|
+
for d in ['build', 'dist']:
|
|
46
|
+
if Path(d).exists():
|
|
47
|
+
shutil.rmtree(d, ignore_errors=True)
|
|
48
|
+
for f in Path('.').glob('*.c'):
|
|
49
|
+
f.unlink()
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
print(" Error: .pyd not generated")
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
def build_whl(runtime_dir, output_dir):
|
|
56
|
+
"""打包pyd为whl"""
|
|
57
|
+
pkg_dir = runtime_dir / '_whl_build' / 'pyruntime'
|
|
58
|
+
pkg_dir.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
|
|
60
|
+
shutil.copy2(runtime_dir / 'pyruntime.pyd', pkg_dir / 'pyruntime.pyd')
|
|
61
|
+
|
|
62
|
+
(runtime_dir / '_whl_build' / 'setup.py').write_text('''from setuptools import setup
|
|
63
|
+
setup(
|
|
64
|
+
name='pyruntime',
|
|
65
|
+
version='1.0.0',
|
|
66
|
+
description='Python encrypted code runtime',
|
|
67
|
+
packages=['pyruntime'],
|
|
68
|
+
package_data={'pyruntime': ['*.pyd']},
|
|
69
|
+
include_package_data=True,
|
|
70
|
+
install_requires=['cryptography>=3.4.7'],
|
|
71
|
+
python_requires='>=3.7',
|
|
72
|
+
)
|
|
73
|
+
''')
|
|
74
|
+
(pkg_dir / '__init__.py').write_text('from .pyruntime import run\n')
|
|
75
|
+
|
|
76
|
+
r = subprocess.run(
|
|
77
|
+
[sys.executable, 'setup.py', 'bdist_wheel'],
|
|
78
|
+
capture_output=True, text=True,
|
|
79
|
+
cwd=str(runtime_dir / '_whl_build')
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
whl_files = list((runtime_dir / '_whl_build' / 'dist').glob('*.whl'))
|
|
83
|
+
if whl_files:
|
|
84
|
+
output_dir = Path(output_dir)
|
|
85
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
target = output_dir / whl_files[0].name
|
|
87
|
+
shutil.copy2(whl_files[0], target)
|
|
88
|
+
shutil.rmtree(runtime_dir / '_whl_build', ignore_errors=True)
|
|
89
|
+
return target
|
|
90
|
+
return None
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""命令行入口"""
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from .encryptor import make_key_iv, encrypt_source, generate_runtime_code
|
|
8
|
+
from .build_pyd import build as compile_pyd, build_whl as pack_whl
|
|
9
|
+
|
|
10
|
+
def should_encrypt(file_path, src_dir):
|
|
11
|
+
try:
|
|
12
|
+
rel = file_path.relative_to(src_dir)
|
|
13
|
+
except ValueError:
|
|
14
|
+
return False
|
|
15
|
+
for part in rel.parts:
|
|
16
|
+
if part.startswith('.') or part.startswith('__'):
|
|
17
|
+
return False
|
|
18
|
+
return file_path.suffix == '.py'
|
|
19
|
+
|
|
20
|
+
def main():
|
|
21
|
+
import argparse
|
|
22
|
+
|
|
23
|
+
parser = argparse.ArgumentParser(
|
|
24
|
+
prog='jh_pyenc',
|
|
25
|
+
description='Python代码保护工具 - 终极加固版',
|
|
26
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
27
|
+
epilog='''
|
|
28
|
+
示例:
|
|
29
|
+
jh_pyenc ./myproject -p "mypass" 基本加密
|
|
30
|
+
jh_pyenc ./myproject -p "mypass" --whl 加密+打包whl
|
|
31
|
+
jh_pyenc ./myproject -p "mypass" --expire 2025-12-31 过期时间
|
|
32
|
+
jh_pyenc ./myproject -p "mypass" --mac aa:bb:cc:dd:ee:ff 绑定MAC
|
|
33
|
+
jh_pyenc ./myproject -p "mypass" --remote https://x.com/verify 远程验证
|
|
34
|
+
jh_pyenc ./myproject -p "mypass" --no-anti-debug 关闭反调试
|
|
35
|
+
jh_pyenc ./myproject -p "mypass" --trap exit 陷阱模式
|
|
36
|
+
'''
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
parser.add_argument('target', help='要加密的项目目录')
|
|
40
|
+
parser.add_argument('-p', '--password', default='default_key_2024', help='加密密码')
|
|
41
|
+
parser.add_argument('--whl', action='store_true', help='打包运行时为whl')
|
|
42
|
+
parser.add_argument('--no-backup', action='store_true', help='不备份原文件')
|
|
43
|
+
|
|
44
|
+
g = parser.add_argument_group('安全配置')
|
|
45
|
+
g.add_argument('--expire', help='过期日期 YYYY-MM-DD')
|
|
46
|
+
g.add_argument('--mac', action='append', help='绑定MAC地址(可多次指定)')
|
|
47
|
+
g.add_argument('--remote', help='远程验证URL')
|
|
48
|
+
g.add_argument('--no-anti-debug', action='store_true', help='关闭反调试')
|
|
49
|
+
g.add_argument('--no-junk', action='store_true', help='关闭垃圾代码')
|
|
50
|
+
g.add_argument('--debug-threshold', type=float, default=0.3, help='反调试时间阈值(秒)')
|
|
51
|
+
g.add_argument('--trap', choices=['random','exit','hang','error','silent'],
|
|
52
|
+
default='random', help='陷阱行为模式')
|
|
53
|
+
g.add_argument('--env-bind', action='store_true', help='启用环境绑定')
|
|
54
|
+
|
|
55
|
+
parser.add_argument('-v', '--version', action='version', version='jh_pyenc 2.0.0')
|
|
56
|
+
|
|
57
|
+
if len(sys.argv) == 1:
|
|
58
|
+
parser.print_help()
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
args = parser.parse_args()
|
|
62
|
+
|
|
63
|
+
target_dir = Path(args.target).absolute()
|
|
64
|
+
if not target_dir.exists():
|
|
65
|
+
print(f"Error: {target_dir} 不存在")
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
if not target_dir.is_dir():
|
|
68
|
+
print(f"Error: {target_dir} 不是目录")
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
print(f"密码: {args.password}")
|
|
72
|
+
print(f"目标: {target_dir}")
|
|
73
|
+
print(f"反调试: {'关闭' if args.no_anti_debug else '开启'}")
|
|
74
|
+
print(f"垃圾代码: {'关闭' if args.no_junk else '开启'}")
|
|
75
|
+
print(f"陷阱模式: {args.trap}")
|
|
76
|
+
if args.expire:
|
|
77
|
+
print(f"过期时间: {args.expire}")
|
|
78
|
+
if args.mac:
|
|
79
|
+
print(f"MAC绑定: {args.mac}")
|
|
80
|
+
if args.remote:
|
|
81
|
+
print(f"远程验证: {args.remote}")
|
|
82
|
+
|
|
83
|
+
key, iv = make_key_iv(args.password)
|
|
84
|
+
pkg_dir = Path(__file__).parent.absolute()
|
|
85
|
+
|
|
86
|
+
tmpdir = tempfile.mkdtemp()
|
|
87
|
+
tmp = Path(tmpdir)
|
|
88
|
+
|
|
89
|
+
encrypted = 0
|
|
90
|
+
backed = 0
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# 1. 生成runtime
|
|
94
|
+
print("\n[1/4] 生成运行时...")
|
|
95
|
+
runtime_code = generate_runtime_code(
|
|
96
|
+
key=key, iv=iv,
|
|
97
|
+
expire_date=args.expire,
|
|
98
|
+
mac_list=args.mac,
|
|
99
|
+
debug_threshold=args.debug_threshold,
|
|
100
|
+
anti_debug=not args.no_anti_debug,
|
|
101
|
+
env_bind=args.env_bind,
|
|
102
|
+
remote_url=args.remote,
|
|
103
|
+
junk_code=not args.no_junk,
|
|
104
|
+
trap_mode=args.trap,
|
|
105
|
+
)
|
|
106
|
+
(tmp / 'pyruntime.py').write_text(runtime_code, encoding='utf-8')
|
|
107
|
+
shutil.copy2(pkg_dir / 'build_pyd.py', tmp / 'build_pyd.py')
|
|
108
|
+
print(" OK")
|
|
109
|
+
|
|
110
|
+
# 2. 编译
|
|
111
|
+
print("[2/4] 编译运行时...")
|
|
112
|
+
if not compile_pyd(tmp):
|
|
113
|
+
print("\n编译失败,请检查:")
|
|
114
|
+
print(" 1. pip install cython setuptools")
|
|
115
|
+
print(" 2. 安装 C 编译器")
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
print(" OK")
|
|
118
|
+
|
|
119
|
+
# 3. whl或复制pyd
|
|
120
|
+
if args.whl:
|
|
121
|
+
print("[3/4] 打包whl...")
|
|
122
|
+
whl = pack_whl(tmp, target_dir)
|
|
123
|
+
if whl:
|
|
124
|
+
print(f" OK: {whl.name}")
|
|
125
|
+
else:
|
|
126
|
+
print(" whl打包失败")
|
|
127
|
+
sys.exit(1)
|
|
128
|
+
else:
|
|
129
|
+
print("[3/4] 复制运行时...")
|
|
130
|
+
shutil.copy2(tmp / 'pyruntime.pyd', target_dir / 'pyruntime.pyd')
|
|
131
|
+
print(f" OK: pyruntime.pyd -> {target_dir.name}")
|
|
132
|
+
|
|
133
|
+
# 4. 加密
|
|
134
|
+
print("[4/4] 加密文件...")
|
|
135
|
+
|
|
136
|
+
backup_dir = None
|
|
137
|
+
if not args.no_backup:
|
|
138
|
+
backup_dir = target_dir.parent / (target_dir.name + '_bak')
|
|
139
|
+
print(f" 备份到: {backup_dir}")
|
|
140
|
+
|
|
141
|
+
py_files = []
|
|
142
|
+
skipped = []
|
|
143
|
+
for f in target_dir.rglob('*.py'):
|
|
144
|
+
if should_encrypt(f, target_dir):
|
|
145
|
+
py_files.append(f)
|
|
146
|
+
elif f.suffix == '.py':
|
|
147
|
+
skipped.append(f)
|
|
148
|
+
|
|
149
|
+
if skipped:
|
|
150
|
+
print(f" 跳过 {len(skipped)} 个文件 (以 . 或 __ 开头)")
|
|
151
|
+
print(f" 加密 {len(py_files)} 个文件")
|
|
152
|
+
|
|
153
|
+
errors = 0
|
|
154
|
+
|
|
155
|
+
for f in py_files:
|
|
156
|
+
rel = f.relative_to(target_dir)
|
|
157
|
+
|
|
158
|
+
if backup_dir:
|
|
159
|
+
bkp = backup_dir / rel
|
|
160
|
+
if not bkp.exists():
|
|
161
|
+
bkp.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
shutil.copy2(f, bkp)
|
|
163
|
+
backed += 1
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
src = f.read_text(encoding='utf-8')
|
|
167
|
+
enc = encrypt_source(src, key, iv)
|
|
168
|
+
f.write_text(
|
|
169
|
+
f'import pyruntime\npyruntime.run({repr(enc)}, globals())\n',
|
|
170
|
+
encoding='utf-8'
|
|
171
|
+
)
|
|
172
|
+
encrypted += 1
|
|
173
|
+
print(f" OK: {rel}")
|
|
174
|
+
except Exception as e:
|
|
175
|
+
errors += 1
|
|
176
|
+
print(f" ERR: {rel} - {e}")
|
|
177
|
+
|
|
178
|
+
if errors:
|
|
179
|
+
print(f"\n 警告: {errors} 个文件加密失败")
|
|
180
|
+
|
|
181
|
+
finally:
|
|
182
|
+
try:
|
|
183
|
+
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
184
|
+
except:
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
print(f"\n{'='*60}")
|
|
188
|
+
print("加密完成!")
|
|
189
|
+
print(f"{'='*60}")
|
|
190
|
+
print(f" 加密文件: {encrypted} 个")
|
|
191
|
+
if backup_dir:
|
|
192
|
+
print(f" 备份文件: {backed} 个 -> {backup_dir}")
|
|
193
|
+
|
|
194
|
+
if args.whl:
|
|
195
|
+
print(f"\n安装运行时:")
|
|
196
|
+
print(f" pip install {target_dir / whl.name}")
|
|
197
|
+
else:
|
|
198
|
+
print(f"\n使用:")
|
|
199
|
+
print(f" cd {target_dir}")
|
|
200
|
+
print(f" python main.py")
|
|
201
|
+
|
|
202
|
+
print(f"\n注意:")
|
|
203
|
+
print(f" 加密文件需要 pyruntime 才能运行")
|
|
204
|
+
if args.expire:
|
|
205
|
+
print(f" 许可证过期时间: {args.expire}")
|
|
206
|
+
print(f" 原文件备份在 {target_dir.name}_bak/")
|
|
207
|
+
print(f"{'='*60}")
|
|
208
|
+
|
|
209
|
+
if __name__ == '__main__':
|
|
210
|
+
main()
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""加密核心"""
|
|
2
|
+
import hashlib
|
|
3
|
+
import random
|
|
4
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
5
|
+
from cryptography.hazmat.primitives import padding
|
|
6
|
+
|
|
7
|
+
def make_key_iv(passphrase):
|
|
8
|
+
h = hashlib.sha256(passphrase.encode()).digest()
|
|
9
|
+
return h[:32], h[16:32]
|
|
10
|
+
|
|
11
|
+
def encrypt_source(source_code, key, iv):
|
|
12
|
+
padder = padding.PKCS7(128).padder()
|
|
13
|
+
padded = padder.update(source_code.encode('utf-8')) + padder.finalize()
|
|
14
|
+
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
|
15
|
+
return cipher.encryptor().update(padded) + cipher.encryptor().finalize()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# jh_pyenc/encryptor.py - generate_runtime_code 函数替换为:
|
|
19
|
+
|
|
20
|
+
def generate_runtime_code(key, iv, expire_date=None, mac_list=None, debug_threshold=0.3,
|
|
21
|
+
anti_debug=True, env_bind=False, remote_url=None,
|
|
22
|
+
junk_code=True, trap_mode='random'):
|
|
23
|
+
xor_k = random.randint(0x10000000, 0xFFFFFFFF)
|
|
24
|
+
xor_i = random.randint(0x10000000, 0xFFFFFFFF)
|
|
25
|
+
|
|
26
|
+
key_parts = []
|
|
27
|
+
for i in range(8):
|
|
28
|
+
chunk = key[i * 4:(i + 1) * 4]
|
|
29
|
+
val = int.from_bytes(chunk, 'big')
|
|
30
|
+
stored = (val ^ xor_k ^ (i * 0x1010101)) & 0xFFFFFFFF
|
|
31
|
+
key_parts.append(stored)
|
|
32
|
+
|
|
33
|
+
iv_parts = []
|
|
34
|
+
for i in range(4):
|
|
35
|
+
chunk = iv[i * 4:(i + 1) * 4]
|
|
36
|
+
val = int.from_bytes(chunk, 'big')
|
|
37
|
+
stored = (val ^ xor_i ^ (i * 0x2020202)) & 0xFFFFFFFF
|
|
38
|
+
iv_parts.append(stored)
|
|
39
|
+
|
|
40
|
+
junk_seed = random.randint(10000, 99999)
|
|
41
|
+
junk_count = random.randint(500, 1500)
|
|
42
|
+
|
|
43
|
+
code = f'''import sys
|
|
44
|
+
import os
|
|
45
|
+
import ctypes
|
|
46
|
+
import hashlib
|
|
47
|
+
import marshal
|
|
48
|
+
import time
|
|
49
|
+
import uuid
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
52
|
+
from cryptography.hazmat.primitives import padding
|
|
53
|
+
|
|
54
|
+
# 所有内部实现放入闭包,外部无法访问
|
|
55
|
+
def _make_runner():
|
|
56
|
+
# 配置常量
|
|
57
|
+
_cfg = {{
|
|
58
|
+
'anti_debug': {str(anti_debug)},
|
|
59
|
+
'env_bind': {str(env_bind)},
|
|
60
|
+
'junk_code': {str(junk_code)},
|
|
61
|
+
'trap_mode': "{trap_mode}",
|
|
62
|
+
'debug_threshold': {debug_threshold},
|
|
63
|
+
'expire_date': {repr(expire_date)},
|
|
64
|
+
'mac_list': {repr(mac_list)},
|
|
65
|
+
'remote_url': {repr(remote_url)},
|
|
66
|
+
'junk_seed': {junk_seed},
|
|
67
|
+
'junk_count': {junk_count},
|
|
68
|
+
'kp': {key_parts},
|
|
69
|
+
'ip': {iv_parts},
|
|
70
|
+
'xk': {xor_k},
|
|
71
|
+
'xi': {xor_i},
|
|
72
|
+
}}
|
|
73
|
+
|
|
74
|
+
def _build_key():
|
|
75
|
+
parts = []
|
|
76
|
+
for i, p in enumerate(_cfg['kp']):
|
|
77
|
+
val = (p ^ _cfg['xk'] ^ (i * 0x1010101)) & 0xFFFFFFFF
|
|
78
|
+
parts.append(val.to_bytes(4, 'big'))
|
|
79
|
+
return b''.join(parts)
|
|
80
|
+
|
|
81
|
+
def _build_iv():
|
|
82
|
+
parts = []
|
|
83
|
+
for i, p in enumerate(_cfg['ip']):
|
|
84
|
+
val = (p ^ _cfg['xi'] ^ (i * 0x2020202)) & 0xFFFFFFFF
|
|
85
|
+
parts.append(val.to_bytes(4, 'big'))
|
|
86
|
+
return b''.join(parts)
|
|
87
|
+
|
|
88
|
+
def _is_debugged():
|
|
89
|
+
if not _cfg['anti_debug']:
|
|
90
|
+
return False
|
|
91
|
+
checks = 0
|
|
92
|
+
if sys.gettrace() is not None:
|
|
93
|
+
checks += 2
|
|
94
|
+
if os.name == 'nt':
|
|
95
|
+
try:
|
|
96
|
+
if ctypes.windll.kernel32.IsDebuggerPresent():
|
|
97
|
+
checks += 2
|
|
98
|
+
except:
|
|
99
|
+
pass
|
|
100
|
+
try:
|
|
101
|
+
t1 = time.perf_counter()
|
|
102
|
+
s = 0
|
|
103
|
+
for i in range(300000):
|
|
104
|
+
s += i
|
|
105
|
+
t2 = time.perf_counter()
|
|
106
|
+
if (t2 - t1) > _cfg['debug_threshold']:
|
|
107
|
+
checks += 1
|
|
108
|
+
except:
|
|
109
|
+
pass
|
|
110
|
+
_bad = ['frida', 'ida', 'x64dbg', 'ollydbg', 'immunity', 'debugger']
|
|
111
|
+
loaded = str(list(sys.modules.keys())).lower()
|
|
112
|
+
for b in _bad:
|
|
113
|
+
if b in loaded:
|
|
114
|
+
checks += 3
|
|
115
|
+
return checks >= 3
|
|
116
|
+
|
|
117
|
+
def _check_environment():
|
|
118
|
+
if not _cfg['env_bind']:
|
|
119
|
+
return True
|
|
120
|
+
if _cfg['mac_list']:
|
|
121
|
+
mac = ':'.join([f'{{(uuid.getnode() >> (i*8)) & 0xFF:02x}}' for i in range(6)])
|
|
122
|
+
if mac not in _cfg['mac_list']:
|
|
123
|
+
return False
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
def _check_expire():
|
|
127
|
+
if _cfg['expire_date'] is None:
|
|
128
|
+
return True
|
|
129
|
+
from datetime import datetime
|
|
130
|
+
try:
|
|
131
|
+
expire = datetime.strptime(_cfg['expire_date'], "%Y-%m-%d")
|
|
132
|
+
if datetime.now() > expire:
|
|
133
|
+
return False
|
|
134
|
+
except:
|
|
135
|
+
pass
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
def _remote_verify():
|
|
139
|
+
if _cfg['remote_url'] is None:
|
|
140
|
+
return True
|
|
141
|
+
try:
|
|
142
|
+
import urllib.request
|
|
143
|
+
import json
|
|
144
|
+
data = json.dumps({{'machine': str(uuid.getnode())}}).encode()
|
|
145
|
+
req = urllib.request.Request(
|
|
146
|
+
_cfg['remote_url'], data=data,
|
|
147
|
+
headers={{'Content-Type': 'application/json'}},
|
|
148
|
+
method='POST'
|
|
149
|
+
)
|
|
150
|
+
resp = urllib.request.urlopen(req, timeout=5)
|
|
151
|
+
return json.loads(resp.read()).get('valid', True)
|
|
152
|
+
except:
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
def _trap():
|
|
156
|
+
import random
|
|
157
|
+
mode = _cfg['trap_mode']
|
|
158
|
+
if mode == 'exit':
|
|
159
|
+
os._exit(1)
|
|
160
|
+
elif mode == 'hang':
|
|
161
|
+
while True:
|
|
162
|
+
time.sleep(60)
|
|
163
|
+
elif mode == 'error':
|
|
164
|
+
raise RuntimeError("Internal protection error")
|
|
165
|
+
elif mode == 'silent':
|
|
166
|
+
pass
|
|
167
|
+
else:
|
|
168
|
+
r = random.randint(0, 3)
|
|
169
|
+
if r == 0:
|
|
170
|
+
os._exit(1)
|
|
171
|
+
elif r == 1:
|
|
172
|
+
while True:
|
|
173
|
+
time.sleep(30)
|
|
174
|
+
elif r == 2:
|
|
175
|
+
raise RuntimeError("Fatal error")
|
|
176
|
+
|
|
177
|
+
def _decrypt(data):
|
|
178
|
+
try:
|
|
179
|
+
key = _build_key()
|
|
180
|
+
iv = _build_iv()
|
|
181
|
+
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
|
182
|
+
decryptor = cipher.decryptor()
|
|
183
|
+
padded = decryptor.update(data) + decryptor.finalize()
|
|
184
|
+
unpadder = padding.PKCS7(128).unpadder()
|
|
185
|
+
result = unpadder.update(padded) + unpadder.finalize()
|
|
186
|
+
key = iv = None
|
|
187
|
+
return result
|
|
188
|
+
except Exception:
|
|
189
|
+
raise RuntimeError("Decryption failed - wrong key or corrupted data")
|
|
190
|
+
|
|
191
|
+
def _junk():
|
|
192
|
+
if not _cfg['junk_code']:
|
|
193
|
+
return
|
|
194
|
+
x = _cfg['junk_seed']
|
|
195
|
+
for _ in range(_cfg['junk_count']):
|
|
196
|
+
x = (x * 1103515245 + 12345) & 0x7FFFFFFF
|
|
197
|
+
|
|
198
|
+
def run(data, namespace=None):
|
|
199
|
+
for _ in range(3):
|
|
200
|
+
if _is_debugged():
|
|
201
|
+
_trap()
|
|
202
|
+
return
|
|
203
|
+
time.sleep(0.005)
|
|
204
|
+
if not _check_environment():
|
|
205
|
+
_trap()
|
|
206
|
+
return
|
|
207
|
+
if not _check_expire():
|
|
208
|
+
raise RuntimeError("License expired: " + str(_cfg['expire_date']))
|
|
209
|
+
if not _remote_verify():
|
|
210
|
+
_trap()
|
|
211
|
+
return
|
|
212
|
+
_junk()
|
|
213
|
+
source = _decrypt(data).decode('utf-8')
|
|
214
|
+
code = compile(source, '<encrypted>', 'exec')
|
|
215
|
+
source = None
|
|
216
|
+
if namespace is None:
|
|
217
|
+
f = sys._getframe(0)
|
|
218
|
+
while f:
|
|
219
|
+
if 'pyruntime' not in f.f_globals.get('__name__', ''):
|
|
220
|
+
namespace = f.f_globals
|
|
221
|
+
break
|
|
222
|
+
f = f.f_back
|
|
223
|
+
if namespace is None:
|
|
224
|
+
namespace = {{'__name__': '__main__'}}
|
|
225
|
+
_junk()
|
|
226
|
+
exec(code, namespace)
|
|
227
|
+
_junk()
|
|
228
|
+
|
|
229
|
+
return run
|
|
230
|
+
|
|
231
|
+
# 唯一的导出函数
|
|
232
|
+
run = _make_runner()
|
|
233
|
+
# 清除工厂函数
|
|
234
|
+
_make_runner = None
|
|
235
|
+
'''
|
|
236
|
+
return code
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jh_pyenc
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Python代码处理工具 - 简单加密处理
|
|
5
|
+
Author: JiJiahui
|
|
6
|
+
Author-email: tn-jh@foxmail.com
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Description-Content-Type: text/plain
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: cryptography>=3.4.7
|
|
19
|
+
Requires-Dist: cython>=0.29
|
|
20
|
+
Requires-Dist: setuptools>=45
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: requires-dist
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# jh_pyenc - Python Code Processing Tool (Ultimate Hardened Edition)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
```bash
|
|
35
|
+
pip install jh_pyenc
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Dependencies
|
|
39
|
+
- cryptography
|
|
40
|
+
- cython
|
|
41
|
+
- setuptools
|
|
42
|
+
- C compiler required (Visual Studio Build Tools / Xcode CLT / build-essential)
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
```bash
|
|
46
|
+
jh_pyenc ./myproject -p "my_password"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Commands
|
|
50
|
+
```bash
|
|
51
|
+
jh_pyenc <directory> [options]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Options:
|
|
55
|
+
| Option | Description |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| `-p, --password` | Password (default: default_key_2024) |
|
|
58
|
+
| `--whl` | Package runtime as .whl file |
|
|
59
|
+
| `--no-backup` | Do not backup original files |
|
|
60
|
+
|
|
61
|
+
### Security Options:
|
|
62
|
+
| Option | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `--expire DATE` | Expiration date YYYY-MM-DD |
|
|
65
|
+
| `--mac MAC` | Bind to MAC address (can specify multiple times) |
|
|
66
|
+
| `--remote URL` | Remote validation URL |
|
|
67
|
+
| `--no-anti-debug` | Disable anti-debugging |
|
|
68
|
+
| `--no-junk` | Disable junk code insertion |
|
|
69
|
+
| `--debug-threshold` | Anti-debugging time threshold (default: 0.3 seconds) |
|
|
70
|
+
| `--trap MODE` | Handler mode: random/exit/hang/error/silent |
|
|
71
|
+
| `--env-bind` | Enable environment binding |
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
```bash
|
|
75
|
+
# Basic processing
|
|
76
|
+
jh_pyenc ./myproject -p "secret"
|
|
77
|
+
|
|
78
|
+
# With expiration
|
|
79
|
+
jh_pyenc ./myproject -p "secret" --expire 2025-12-31
|
|
80
|
+
|
|
81
|
+
# With MAC binding
|
|
82
|
+
jh_pyenc ./myproject -p "secret" --mac aa:bb:cc:dd:ee:ff
|
|
83
|
+
|
|
84
|
+
# Package as wheel
|
|
85
|
+
jh_pyenc ./myproject -p "secret" --whl
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Important Notes
|
|
89
|
+
- Please keep your password safe and secure
|
|
90
|
+
- Original files are automatically backed up to `xxx_bak/` directory
|
|
91
|
+
- After processing, the corresponding pyruntime is required to run; can be placed in site-packages for convenient access
|
|
92
|
+
- This is a simple obfuscation tool and does not meet production-grade security standards
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 中文说明
|
|
97
|
+
|
|
98
|
+
### 安装
|
|
99
|
+
```bash
|
|
100
|
+
pip install jh_pyenc-0.0.3-py3-none-any.whl
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 依赖
|
|
104
|
+
- cryptography
|
|
105
|
+
- cython
|
|
106
|
+
- setuptools
|
|
107
|
+
- 需要 C 编译器 (Visual Studio Build Tools / Xcode CLT / build-essential)
|
|
108
|
+
|
|
109
|
+
### 快速开始
|
|
110
|
+
```bash
|
|
111
|
+
jh_pyenc ./myproject -p "my_password"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 命令
|
|
115
|
+
```bash
|
|
116
|
+
jh_pyenc <目录> [选项]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 选项:
|
|
120
|
+
| 选项 | 说明 |
|
|
121
|
+
|------|------|
|
|
122
|
+
| `-p, --password` | 密码 (默认: default_key_2024) |
|
|
123
|
+
| `--whl` | 打包运行时为 .whl 文件 |
|
|
124
|
+
| `--no-backup` | 不备份原文件 |
|
|
125
|
+
|
|
126
|
+
### 安全选项:
|
|
127
|
+
| 选项 | 说明 |
|
|
128
|
+
|------|------|
|
|
129
|
+
| `--expire DATE` | 过期日期 YYYY-MM-DD |
|
|
130
|
+
| `--mac MAC` | 绑定MAC地址 (可多次指定) |
|
|
131
|
+
| `--remote URL` | 远程验证URL |
|
|
132
|
+
| `--no-anti-debug` | 关闭反调试 |
|
|
133
|
+
| `--no-junk` | 关闭垃圾代码 |
|
|
134
|
+
| `--debug-threshold` | 反调试时间阈值 (默认: 0.3秒) |
|
|
135
|
+
| `--trap MODE` | 处理模式: random/exit/hang/error/silent |
|
|
136
|
+
| `--env-bind` | 启用环境绑定 |
|
|
137
|
+
|
|
138
|
+
### 示例
|
|
139
|
+
```bash
|
|
140
|
+
# 基础处理
|
|
141
|
+
jh_pyenc ./myproject -p "secret"
|
|
142
|
+
|
|
143
|
+
# 带过期时间
|
|
144
|
+
jh_pyenc ./myproject -p "secret" --expire 2025-12-31
|
|
145
|
+
|
|
146
|
+
# 绑定MAC地址
|
|
147
|
+
jh_pyenc ./myproject -p "secret" --mac aa:bb:cc:dd:ee:ff
|
|
148
|
+
|
|
149
|
+
# 打包为wheel
|
|
150
|
+
jh_pyenc ./myproject -p "secret" --whl
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 注意事项
|
|
154
|
+
- 请妥善保管处理密码
|
|
155
|
+
- 原文件自动备份到 `xxx_bak/` 目录
|
|
156
|
+
- 处理后需要相应的 pyruntime 才能运行,可以放到 site-packages 方便调用
|
|
157
|
+
- 只是简单的混淆处理,无法达到生产环境的安全水准
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
jh_pyenc/__init__.py
|
|
6
|
+
jh_pyenc/__main__.py
|
|
7
|
+
jh_pyenc/build_pyd.py
|
|
8
|
+
jh_pyenc/cli.py
|
|
9
|
+
jh_pyenc/encryptor.py
|
|
10
|
+
jh_pyenc/runtime_template.py
|
|
11
|
+
jh_pyenc.egg-info/PKG-INFO
|
|
12
|
+
jh_pyenc.egg-info/SOURCES.txt
|
|
13
|
+
jh_pyenc.egg-info/dependency_links.txt
|
|
14
|
+
jh_pyenc.egg-info/entry_points.txt
|
|
15
|
+
jh_pyenc.egg-info/requires.txt
|
|
16
|
+
jh_pyenc.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jh_pyenc
|
jh_pyenc-0.0.3/setup.cfg
ADDED
jh_pyenc-0.0.3/setup.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open('README.md', 'r', encoding='utf-8') as f:
|
|
4
|
+
long_description = f.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name='jh_pyenc',
|
|
8
|
+
version='0.0.3',
|
|
9
|
+
author='JiJiahui',
|
|
10
|
+
author_email='tn-jh@foxmail.com',
|
|
11
|
+
description='Python代码处理工具 - 简单加密处理',
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type='text/plain',
|
|
14
|
+
packages=find_packages(),
|
|
15
|
+
package_data={'jh_pyenc': ['*.py']},
|
|
16
|
+
install_requires=[
|
|
17
|
+
'cryptography>=3.4.7',
|
|
18
|
+
'cython>=0.29',
|
|
19
|
+
'setuptools>=45',
|
|
20
|
+
],
|
|
21
|
+
python_requires='>=3.7',
|
|
22
|
+
entry_points={
|
|
23
|
+
'console_scripts': [
|
|
24
|
+
'jh_pyenc=jh_pyenc.cli:main',
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
classifiers=[
|
|
28
|
+
'Development Status :: 4 - Beta',
|
|
29
|
+
'Programming Language :: Python :: 3',
|
|
30
|
+
'Programming Language :: Python :: 3.7',
|
|
31
|
+
'Programming Language :: Python :: 3.8',
|
|
32
|
+
'Programming Language :: Python :: 3.9',
|
|
33
|
+
'Programming Language :: Python :: 3.10',
|
|
34
|
+
'Programming Language :: Python :: 3.11',
|
|
35
|
+
'Programming Language :: Python :: 3.12',
|
|
36
|
+
],
|
|
37
|
+
)
|