PyProtect-v3 3.0.0__tar.gz → 3.2.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.
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PKG-INFO +1 -1
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PyProtect_v3.egg-info/PKG-INFO +1 -1
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PyProtect_v3.egg-info/SOURCES.txt +0 -1
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyproject.toml +1 -1
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/__main__.py +0 -2
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/engine.c +242 -6
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/obfuscator.py +11 -74
- pyprotect_v3-3.0.0/pyprotect/_key.py +0 -2
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/MANIFEST.in +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PyProtect_v3.egg-info/dependency_links.txt +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PyProtect_v3.egg-info/entry_points.txt +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PyProtect_v3.egg-info/requires.txt +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/PyProtect_v3.egg-info/top_level.txt +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/README.md +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/__init__.py +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/anti_debug.py +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/crypto.py +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/engine.py +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/pyprotect/hooks.py +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/setup.cfg +0 -0
- {pyprotect_v3-3.0.0 → pyprotect_v3-3.2.0}/setup.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "PyProtect-v3"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.2.0"
|
|
8
8
|
description = "Python script obfuscation library with AES-256-GCM encryption and C extension acceleration"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -15,7 +15,6 @@ def cmd_encrypt(args):
|
|
|
15
15
|
)
|
|
16
16
|
out_path = obf.encrypt_file(args.input)
|
|
17
17
|
print(f"[+] Encrypted: {args.input} -> {out_path}")
|
|
18
|
-
_copy_engine(args.output_dir)
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
def cmd_encrypt_dir(args):
|
|
@@ -29,7 +28,6 @@ def cmd_encrypt_dir(args):
|
|
|
29
28
|
print(f"[+] Encrypted {len(out_files)} files from {args.input}")
|
|
30
29
|
for f in out_files:
|
|
31
30
|
print(f" -> {f}")
|
|
32
|
-
_copy_engine(args.output_dir)
|
|
33
31
|
|
|
34
32
|
|
|
35
33
|
def cmd_build_engine(args):
|
|
@@ -298,6 +298,86 @@ static int deep_anti_debug(void) {
|
|
|
298
298
|
return detected;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
/* ------------------------------------------------------------------ */
|
|
302
|
+
/* Anti-hook — detect debugger trace, profile, suspicious import hooks */
|
|
303
|
+
/* ------------------------------------------------------------------ */
|
|
304
|
+
|
|
305
|
+
static int deep_anti_hook(void) {
|
|
306
|
+
int detected = 0;
|
|
307
|
+
PyObject *sys_mod = PyImport_AddModule("sys");
|
|
308
|
+
if (!sys_mod) return 0;
|
|
309
|
+
PyObject *f = PyObject_GetAttrString(sys_mod, "gettrace");
|
|
310
|
+
if (f) {
|
|
311
|
+
PyObject *v = PyObject_CallNoArgs(f);
|
|
312
|
+
if (v && v != Py_None) detected = 1;
|
|
313
|
+
Py_XDECREF(v); Py_DECREF(f);
|
|
314
|
+
}
|
|
315
|
+
f = PyObject_GetAttrString(sys_mod, "getprofile");
|
|
316
|
+
if (f) {
|
|
317
|
+
PyObject *v = PyObject_CallNoArgs(f);
|
|
318
|
+
if (v && v != Py_None) detected = 1;
|
|
319
|
+
Py_XDECREF(v); Py_DECREF(f);
|
|
320
|
+
}
|
|
321
|
+
/* Check meta_path for known hostile importers */
|
|
322
|
+
PyObject *meta_path = PySys_GetObject("meta_path");
|
|
323
|
+
if (meta_path && PyList_Check(meta_path)) {
|
|
324
|
+
Py_ssize_t n = PyList_Size(meta_path);
|
|
325
|
+
for (Py_ssize_t i = 0; i < n; i++) {
|
|
326
|
+
PyObject *item = PyList_GetItem(meta_path, i);
|
|
327
|
+
if (!item) continue;
|
|
328
|
+
PyObject *name = PyObject_GetAttrString(item, "__class__");
|
|
329
|
+
if (!name) { PyErr_Clear(); continue; }
|
|
330
|
+
PyObject *cname = PyObject_GetAttrString(name, "__name__");
|
|
331
|
+
Py_DECREF(name);
|
|
332
|
+
if (!cname) { PyErr_Clear(); continue; }
|
|
333
|
+
const char *s = PyUnicode_AsUTF8(cname);
|
|
334
|
+
if (s) {
|
|
335
|
+
if (strstr(s, "Hook") || strstr(s, "Redirect") ||
|
|
336
|
+
strstr(s, "Monitor") || strstr(s, "Interceptor") ||
|
|
337
|
+
strstr(s, "Tracer") || strstr(s, "Proxy") ||
|
|
338
|
+
strstr(s, "Debug") || strstr(s, "Inject")) {
|
|
339
|
+
detected = 1;
|
|
340
|
+
Py_DECREF(cname);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
Py_DECREF(cname);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return detected;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* ------------------------------------------------------------------ */
|
|
351
|
+
/* Anti-decode — block decompilers, profilers, debugger modules */
|
|
352
|
+
/* ------------------------------------------------------------------ */
|
|
353
|
+
|
|
354
|
+
static int deep_anti_decode(void) {
|
|
355
|
+
int detected = 0;
|
|
356
|
+
PyObject *sys_mod = PyImport_AddModule("sys");
|
|
357
|
+
if (sys_mod) {
|
|
358
|
+
PyObject *modules = PyObject_GetAttrString(sys_mod, "modules");
|
|
359
|
+
if (modules && PyDict_Check(modules)) {
|
|
360
|
+
const char *sus[] = {
|
|
361
|
+
"decompyle3","uncompyle6","decompyle","frida",
|
|
362
|
+
"pyrasite","pydevd","pdb","ipdb",
|
|
363
|
+
"pyinstrument","cProfile","line_profiler",NULL
|
|
364
|
+
};
|
|
365
|
+
for (int i = 0; sus[i]; i++) {
|
|
366
|
+
PyObject *k = PyUnicode_FromString(sus[i]);
|
|
367
|
+
if (k) {
|
|
368
|
+
if (PyDict_GetItem(modules, k)) { detected = 1; Py_DECREF(k); break; }
|
|
369
|
+
Py_DECREF(k);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
Py_XDECREF(modules);
|
|
374
|
+
}
|
|
375
|
+
if (getenv("PYTHONDEVMODE")) detected = 1;
|
|
376
|
+
if (getenv("PYTHONINSPECT")) detected = 1;
|
|
377
|
+
if (getenv("PYSEC")) detected = 1;
|
|
378
|
+
return detected;
|
|
379
|
+
}
|
|
380
|
+
|
|
301
381
|
/* ------------------------------------------------------------------ */
|
|
302
382
|
/* Memory protection: re-encrypt buffer after use */
|
|
303
383
|
/* ------------------------------------------------------------------ */
|
|
@@ -382,6 +462,77 @@ static int _decrypt_payload(const uint8_t *data, size_t data_len,
|
|
|
382
462
|
return 0;
|
|
383
463
|
}
|
|
384
464
|
|
|
465
|
+
/* ------------------------------------------------------------------ */
|
|
466
|
+
/* Frame evaluation hook — anti-tamper check on every function call */
|
|
467
|
+
/* We use the CPython 3.11+ internal API to intercept frame evaluation */
|
|
468
|
+
/* ------------------------------------------------------------------ */
|
|
469
|
+
|
|
470
|
+
/* These are declared in cpython/pystate.h and cpython/ceval.h
|
|
471
|
+
which are included by Python.h */
|
|
472
|
+
|
|
473
|
+
static _PyFrameEvalFunction original_eval_frame = NULL;
|
|
474
|
+
static PyInterpreterState *hooked_interp = NULL;
|
|
475
|
+
|
|
476
|
+
static PyObject* protected_eval_frame(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag) {
|
|
477
|
+
if (deep_anti_debug() || deep_anti_hook() || deep_anti_decode()) {
|
|
478
|
+
PyErr_SetString(PyExc_RuntimeError, "Tamper detected");
|
|
479
|
+
return NULL;
|
|
480
|
+
}
|
|
481
|
+
return _PyEval_EvalFrameDefault(tstate, frame, throwflag);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
static void install_frame_hook(void) {
|
|
485
|
+
if (original_eval_frame) return;
|
|
486
|
+
PyInterpreterState *interp = PyInterpreterState_Get();
|
|
487
|
+
if (!interp) return;
|
|
488
|
+
_PyFrameEvalFunction cur = _PyInterpreterState_GetEvalFrameFunc(interp);
|
|
489
|
+
if (cur == protected_eval_frame) return;
|
|
490
|
+
original_eval_frame = cur;
|
|
491
|
+
hooked_interp = interp;
|
|
492
|
+
_PyInterpreterState_SetEvalFrameFunc(interp, protected_eval_frame);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
static void restore_frame_hook(void) {
|
|
496
|
+
if (!original_eval_frame || !hooked_interp) return;
|
|
497
|
+
_PyInterpreterState_SetEvalFrameFunc(hooked_interp, original_eval_frame);
|
|
498
|
+
original_eval_frame = NULL;
|
|
499
|
+
hooked_interp = NULL;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/* ------------------------------------------------------------------ */
|
|
503
|
+
/* Wipe code tree — recursively clear all code objects' bytecodes */
|
|
504
|
+
/* ------------------------------------------------------------------ */
|
|
505
|
+
|
|
506
|
+
static void wipe_code_tree(PyObject *co) {
|
|
507
|
+
if (!co || !PyCode_Check(co)) return;
|
|
508
|
+
PyCodeObject *code = (PyCodeObject*)co;
|
|
509
|
+
|
|
510
|
+
PyObject *bc = PyCode_GetCode(code);
|
|
511
|
+
if (bc && PyBytes_Check(bc)) {
|
|
512
|
+
Py_ssize_t sz;
|
|
513
|
+
char *buf;
|
|
514
|
+
if (PyBytes_AsStringAndSize(bc, &buf, &sz) == 0 && buf && sz > 0) {
|
|
515
|
+
memset(buf, 0, (size_t)sz);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
Py_XDECREF(bc);
|
|
519
|
+
|
|
520
|
+
PyObject *consts = code->co_consts;
|
|
521
|
+
if (consts && PyTuple_Check(consts)) {
|
|
522
|
+
Py_ssize_t n = PyTuple_Size(consts);
|
|
523
|
+
for (Py_ssize_t i = 0; i < n; i++) {
|
|
524
|
+
PyObject *item = PyTuple_GetItem(consts, i);
|
|
525
|
+
if (item && PyCode_Check(item)) {
|
|
526
|
+
wipe_code_tree(item);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* ------------------------------------------------------------------ */
|
|
533
|
+
/* Decrypt and exec */
|
|
534
|
+
/* ------------------------------------------------------------------ */
|
|
535
|
+
|
|
385
536
|
static PyObject* engine_decrypt_and_exec(PyObject *self, PyObject *args) {
|
|
386
537
|
const uint8_t *data;
|
|
387
538
|
Py_ssize_t data_len;
|
|
@@ -389,8 +540,8 @@ static PyObject* engine_decrypt_and_exec(PyObject *self, PyObject *args) {
|
|
|
389
540
|
if (!PyArg_ParseTuple(args, "y#", &data, &data_len))
|
|
390
541
|
return NULL;
|
|
391
542
|
|
|
392
|
-
if (deep_anti_debug()) {
|
|
393
|
-
PyErr_SetString(PyExc_RuntimeError, "
|
|
543
|
+
if (deep_anti_debug() || deep_anti_hook() || deep_anti_decode()) {
|
|
544
|
+
PyErr_SetString(PyExc_RuntimeError, "Tamper detected");
|
|
394
545
|
return NULL;
|
|
395
546
|
}
|
|
396
547
|
|
|
@@ -462,15 +613,25 @@ static PyObject* engine_decrypt_and_exec(PyObject *self, PyObject *args) {
|
|
|
462
613
|
return NULL;
|
|
463
614
|
}
|
|
464
615
|
|
|
616
|
+
/* Install frame eval hook */
|
|
617
|
+
install_frame_hook();
|
|
618
|
+
|
|
465
619
|
PyObject *builtins = PyEval_GetBuiltins();
|
|
466
|
-
if (!builtins) { Py_DECREF(code_obj); return NULL; }
|
|
620
|
+
if (!builtins) { restore_frame_hook(); Py_DECREF(code_obj); return NULL; }
|
|
467
621
|
|
|
468
622
|
PyObject *globals = PyDict_New();
|
|
469
|
-
if (!globals) { Py_DECREF(code_obj); return NULL; }
|
|
623
|
+
if (!globals) { restore_frame_hook(); Py_DECREF(code_obj); return NULL; }
|
|
470
624
|
|
|
471
625
|
PyDict_SetItemString(globals, "__builtins__", builtins);
|
|
472
626
|
|
|
473
627
|
PyObject *result = PyEval_EvalCode((PyObject*)code_obj, globals, globals);
|
|
628
|
+
|
|
629
|
+
/* Restore frame hook */
|
|
630
|
+
restore_frame_hook();
|
|
631
|
+
|
|
632
|
+
/* Wipe all decrypted bytecodes from memory */
|
|
633
|
+
wipe_code_tree(code_obj);
|
|
634
|
+
|
|
474
635
|
Py_DECREF(code_obj);
|
|
475
636
|
Py_DECREF(globals);
|
|
476
637
|
|
|
@@ -489,6 +650,11 @@ static PyObject* engine_decrypt_and_get_code(PyObject *self, PyObject *args) {
|
|
|
489
650
|
if (!PyArg_ParseTuple(args, "y#", &data, &data_len))
|
|
490
651
|
return NULL;
|
|
491
652
|
|
|
653
|
+
if (deep_anti_debug() || deep_anti_hook() || deep_anti_decode()) {
|
|
654
|
+
PyErr_SetString(PyExc_RuntimeError, "Tamper detected");
|
|
655
|
+
return NULL;
|
|
656
|
+
}
|
|
657
|
+
|
|
492
658
|
uint8_t iv[12];
|
|
493
659
|
uint8_t *plain = NULL;
|
|
494
660
|
size_t plain_len = 0;
|
|
@@ -549,19 +715,89 @@ static PyObject* engine_decrypt_and_get_code(PyObject *self, PyObject *args) {
|
|
|
549
715
|
return code_obj;
|
|
550
716
|
}
|
|
551
717
|
|
|
718
|
+
/* ------------------------------------------------------------------ */
|
|
719
|
+
/* Encrypt — used by Python obfuscator so key stays in .so only */
|
|
720
|
+
/* ------------------------------------------------------------------ */
|
|
721
|
+
|
|
722
|
+
static PyObject* engine_encrypt(PyObject *self, PyObject *args) {
|
|
723
|
+
const uint8_t *plain;
|
|
724
|
+
Py_ssize_t plain_len;
|
|
725
|
+
|
|
726
|
+
if (!PyArg_ParseTuple(args, "y#", &plain, &plain_len))
|
|
727
|
+
return NULL;
|
|
728
|
+
|
|
729
|
+
uint8_t iv[12];
|
|
730
|
+
size_t ct_len = (size_t)plain_len;
|
|
731
|
+
size_t out_len = 12 + ct_len + 16; /* iv + ct + tag */
|
|
732
|
+
uint8_t *out = (uint8_t*)malloc(out_len);
|
|
733
|
+
if (!out) return PyErr_NoMemory();
|
|
734
|
+
|
|
735
|
+
/* Generate random IV */
|
|
736
|
+
#if defined(PLATFORM_WINDOWS)
|
|
737
|
+
HCRYPTPROV hProv = 0;
|
|
738
|
+
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
|
|
739
|
+
CryptGenRandom(hProv, 12, iv);
|
|
740
|
+
CryptReleaseContext(hProv, 0);
|
|
741
|
+
#else
|
|
742
|
+
FILE *urandom = fopen("/dev/urandom", "rb");
|
|
743
|
+
if (!urandom) { free(out); PyErr_SetString(PyExc_RuntimeError, "No /dev/urandom"); return NULL; }
|
|
744
|
+
fread(iv, 1, 12, urandom);
|
|
745
|
+
fclose(urandom);
|
|
746
|
+
#endif
|
|
747
|
+
|
|
748
|
+
memcpy(out, iv, 12); /* prepend iv */
|
|
749
|
+
|
|
750
|
+
#if defined(USE_EMBEDDED_AES)
|
|
751
|
+
/* For embedded AES, we'd need aes_gcm_encrypt. Use OpenSSL fallback. */
|
|
752
|
+
free(out);
|
|
753
|
+
PyErr_SetString(PyExc_RuntimeError, "Encryption requires OpenSSL");
|
|
754
|
+
return NULL;
|
|
755
|
+
#else
|
|
756
|
+
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
|
757
|
+
if (!ctx) { free(out); PyErr_NoMemory(); return NULL; }
|
|
758
|
+
|
|
759
|
+
int len, ct_written = 0;
|
|
760
|
+
if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) != 1) goto err;
|
|
761
|
+
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL) != 1) goto err;
|
|
762
|
+
if (EVP_EncryptInit_ex(ctx, NULL, NULL, EMBEDDED_KEY, iv) != 1) goto err;
|
|
763
|
+
if (EVP_EncryptUpdate(ctx, NULL, &len, (const uint8_t*)"pyprotect", 9) != 1) goto err; /* AAD */
|
|
764
|
+
if (EVP_EncryptUpdate(ctx, out + 12, &len, plain, (int)ct_len) != 1) goto err;
|
|
765
|
+
ct_written = len;
|
|
766
|
+
if (EVP_EncryptFinal_ex(ctx, out + 12 + ct_written, &len) != 1) goto err;
|
|
767
|
+
ct_written += len;
|
|
768
|
+
|
|
769
|
+
uint8_t tag[16];
|
|
770
|
+
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag) != 1) goto err;
|
|
771
|
+
memcpy(out + 12 + ct_len, tag, 16); /* append tag */
|
|
772
|
+
|
|
773
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
774
|
+
PyObject *result = PyBytes_FromStringAndSize((const char*)out, (Py_ssize_t)out_len);
|
|
775
|
+
free(out);
|
|
776
|
+
return result;
|
|
777
|
+
|
|
778
|
+
err:
|
|
779
|
+
ERR_print_errors_fp(stderr);
|
|
780
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
781
|
+
free(out);
|
|
782
|
+
PyErr_SetString(PyExc_RuntimeError, "Encryption failed");
|
|
783
|
+
return NULL;
|
|
784
|
+
#endif
|
|
785
|
+
}
|
|
786
|
+
|
|
552
787
|
/* ------------------------------------------------------------------ */
|
|
553
788
|
/* Module definition */
|
|
554
789
|
/* ------------------------------------------------------------------ */
|
|
555
790
|
|
|
556
791
|
static PyMethodDef EngineMethods[] = {
|
|
557
|
-
{"decrypt_and_exec", engine_decrypt_and_exec, METH_VARARGS, "Decrypt
|
|
792
|
+
{"decrypt_and_exec", engine_decrypt_and_exec, METH_VARARGS, "Decrypt+execute with frame hook + anti-tamper + memory wipe."},
|
|
558
793
|
{"decrypt_and_get_code", engine_decrypt_and_get_code, METH_VARARGS, "Decrypt and return code object."},
|
|
794
|
+
{"encrypt", engine_encrypt, METH_VARARGS, "Encrypt payload with embedded key."},
|
|
559
795
|
{NULL, NULL, 0, NULL}
|
|
560
796
|
};
|
|
561
797
|
|
|
562
798
|
static struct PyModuleDef enginemodule = {
|
|
563
799
|
PyModuleDef_HEAD_INIT, "engine_c",
|
|
564
|
-
"pyprotect-v4 engine -
|
|
800
|
+
"pyprotect-v4 engine - frame hook, anti-tamper, anti-dump",
|
|
565
801
|
-1, EngineMethods
|
|
566
802
|
};
|
|
567
803
|
|
|
@@ -14,43 +14,16 @@ import shutil
|
|
|
14
14
|
import ast
|
|
15
15
|
import base64
|
|
16
16
|
|
|
17
|
-
from pyprotect import
|
|
18
|
-
try:
|
|
19
|
-
from pyprotect._key import EMBEDDED_KEY
|
|
20
|
-
except ImportError:
|
|
21
|
-
EMBEDDED_KEY = b'\xa1\xb2\xc3\xd4\xe5\xf6\x07\x18\x29\x3a\x4b\x5c\x6d\x7e\x8f\x90\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10'
|
|
17
|
+
from pyprotect.engine_c import encrypt
|
|
22
18
|
|
|
23
19
|
|
|
24
|
-
BOOTSTRAP_TEMPLATE = '''
|
|
25
|
-
import
|
|
26
|
-
import os
|
|
27
|
-
|
|
28
|
-
{payload_var} = {payload!r}
|
|
29
|
-
|
|
30
|
-
def _exec_protected():
|
|
31
|
-
import importlib.util
|
|
32
|
-
_d = os.path.dirname(os.path.abspath(__file__))
|
|
33
|
-
for _f in os.listdir(_d):
|
|
34
|
-
if _f.startswith('engine_c') and any(_f.endswith(e) for e in ('.so', '.pyd', '.dll')):
|
|
35
|
-
_spec = importlib.util.spec_from_file_location('engine_c', os.path.join(_d, _f))
|
|
36
|
-
if _spec:
|
|
37
|
-
_mod = importlib.util.module_from_spec(_spec)
|
|
38
|
-
_spec.loader.exec_module(_mod)
|
|
39
|
-
return _mod.decrypt_and_exec({payload_var})
|
|
40
|
-
from pyprotect.engine_c import decrypt_and_exec as _e
|
|
41
|
-
return _e({payload_var})
|
|
42
|
-
|
|
43
|
-
if __name__ == "__main__":
|
|
44
|
-
try:
|
|
45
|
-
_exec_protected()
|
|
46
|
-
except BaseException:
|
|
47
|
-
sys.stderr.write("[-] C engine not available. Install pyprotect-v3 with C extension.\\n")
|
|
48
|
-
sys.exit(1)
|
|
49
|
-
'''
|
|
20
|
+
BOOTSTRAP_TEMPLATE = '''# PyProtect-v3 | github.com/PyProtect/PyProtect
|
|
21
|
+
from pyprotect.engine_c import decrypt_and_exec; decrypt_and_exec({payload!r})'''
|
|
50
22
|
|
|
51
23
|
MULTI_BOOTSTRAP_TEMPLATE = '''"""Protected package - generated by pyprotect-v3 v{version}."""
|
|
52
24
|
import sys
|
|
53
25
|
import os
|
|
26
|
+
|
|
54
27
|
import importlib.abc
|
|
55
28
|
import importlib.machinery
|
|
56
29
|
import types
|
|
@@ -288,41 +261,9 @@ def _add_junk_imports(tree):
|
|
|
288
261
|
# ---------------------------------------------------------------------------
|
|
289
262
|
|
|
290
263
|
def _generate_polymorphic_stub(payload_expr: str) -> str:
|
|
291
|
-
"""Generate a
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
stub = f'''"""Protected - {base64.b64encode(os.urandom(8)).decode()}"""
|
|
296
|
-
import sys
|
|
297
|
-
import os
|
|
298
|
-
|
|
299
|
-
{pname} = {payload_expr}
|
|
300
|
-
|
|
301
|
-
if __name__ == "__main__":
|
|
302
|
-
try:
|
|
303
|
-
import importlib.util
|
|
304
|
-
_d = os.path.dirname(os.path.abspath(__file__))
|
|
305
|
-
_ok = False
|
|
306
|
-
for _f in os.listdir(_d):
|
|
307
|
-
if _f.startswith('engine_c') and any(_f.endswith(e) for e in ('.so', '.pyd', '.dll')):
|
|
308
|
-
_spec = importlib.util.spec_from_file_location('engine_c', os.path.join(_d, _f))
|
|
309
|
-
if _spec:
|
|
310
|
-
_mod = importlib.util.module_from_spec(_spec)
|
|
311
|
-
_spec.loader.exec_module(_mod)
|
|
312
|
-
_mod.decrypt_and_exec({pname})
|
|
313
|
-
_ok = True
|
|
314
|
-
break
|
|
315
|
-
if not _ok:
|
|
316
|
-
from pyprotect.engine_c import decrypt_and_exec as _e
|
|
317
|
-
_e({pname})
|
|
318
|
-
_ok = True
|
|
319
|
-
except SystemExit:
|
|
320
|
-
raise
|
|
321
|
-
except BaseException:
|
|
322
|
-
if not _ok:
|
|
323
|
-
sys.stderr.write("[-] C engine not available. Install pyprotect-v3 with C extension.\\n")
|
|
324
|
-
sys.exit(1)
|
|
325
|
-
'''
|
|
264
|
+
"""Generate a compact stub. No key in stub — key is in .so binary."""
|
|
265
|
+
stub = f'''# PyProtect-v3 | github.com/PyProtect/PyProtect
|
|
266
|
+
from pyprotect.engine_c import decrypt_and_exec; decrypt_and_exec({payload_expr})'''
|
|
326
267
|
return stub
|
|
327
268
|
|
|
328
269
|
# ---------------------------------------------------------------------------
|
|
@@ -375,7 +316,7 @@ class Obfuscator:
|
|
|
375
316
|
payload = _marshal_and_compress(code, self.compress)
|
|
376
317
|
|
|
377
318
|
iv = os.urandom(12)
|
|
378
|
-
encrypted =
|
|
319
|
+
encrypted = encrypt(payload)
|
|
379
320
|
|
|
380
321
|
filename = os.path.basename(input_path)
|
|
381
322
|
output = self._build_stub(encrypted, filename)
|
|
@@ -429,12 +370,12 @@ class Obfuscator:
|
|
|
429
370
|
|
|
430
371
|
code = _compile_code(source, os.path.basename(py_file))
|
|
431
372
|
payload = _marshal_and_compress(code, self.compress)
|
|
432
|
-
encrypted =
|
|
373
|
+
encrypted = encrypt(payload)
|
|
433
374
|
modules[module_name] = encrypted
|
|
434
375
|
|
|
435
376
|
# Generate multi bootstrap
|
|
436
377
|
bootstrap_source = MULTI_BOOTSTRAP_TEMPLATE.format(
|
|
437
|
-
version="3.
|
|
378
|
+
version="3.2.0",
|
|
438
379
|
modules=modules,
|
|
439
380
|
)
|
|
440
381
|
|
|
@@ -467,14 +408,12 @@ class Obfuscator:
|
|
|
467
408
|
)
|
|
468
409
|
os.makedirs(output_dir, exist_ok=True)
|
|
469
410
|
|
|
470
|
-
payload_var = random.choice(string.ascii_letters) + "_" + base64.b16encode(os.urandom(6)).decode()
|
|
471
411
|
if self.polymorphic:
|
|
472
412
|
stub_source = _generate_polymorphic_stub(repr(encrypted))
|
|
473
413
|
else:
|
|
474
414
|
stub_source = BOOTSTRAP_TEMPLATE.format(
|
|
475
|
-
version="3.
|
|
415
|
+
version="3.2.0",
|
|
476
416
|
payload=encrypted,
|
|
477
|
-
payload_var=f"_{payload_var}",
|
|
478
417
|
)
|
|
479
418
|
|
|
480
419
|
stub_name = original_filename.replace(".py", "_protected.py")
|
|
@@ -485,8 +424,6 @@ class Obfuscator:
|
|
|
485
424
|
|
|
486
425
|
os.chmod(stub_path, 0o755)
|
|
487
426
|
|
|
488
|
-
self._copy_engine(output_dir)
|
|
489
|
-
|
|
490
427
|
return stub_path
|
|
491
428
|
|
|
492
429
|
def _copy_engine(self, output_dir: str):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|