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 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.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include jh_pyenc *.py
@@ -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,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,2 @@
1
+ """jh_pyenc - Python代码保护工具"""
2
+ __version__ = "0.0.3"
@@ -0,0 +1,3 @@
1
+ """入口"""
2
+ from .cli import main
3
+ main()
@@ -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,3 @@
1
+ # 运行时模板 - build时替换密钥后编译为.pyd
2
+ _KEY = None # 占位,build时替换
3
+ _IV = None # 占位,build时替换
@@ -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,2 @@
1
+ [console_scripts]
2
+ jh_pyenc = jh_pyenc.cli:main
@@ -0,0 +1,3 @@
1
+ cryptography>=3.4.7
2
+ cython>=0.29
3
+ setuptools>=45
@@ -0,0 +1 @@
1
+ jh_pyenc
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ )