Deobfuscate Python without executing it. A webcrack for Python.
Project description
snakecharm
Python De-Obfuscation
Unwraps obfuscated Python — exec/eval/compile chains over base64, hex,
zlib/gzip/bz2/lzma, XOR, marshal, reversal and chr/ord reconstruction —
without executing the sample.
Requires Python 3.11+. Pure Python; depends on typer, rich, pyyaml.
Install
pip install snakecharm
uv tool install snakecharm # isolated, puts snakecharm on PATH
Commands
snakecharm crack FILE [-o DIR] [--json] [--clean]
snakecharm detect FILE [--json]
snakecharm inspect FILE [--target-python X.Y]
snakecharm version
crack |
Unwrap. Recovered source to stdout, diagnostics to stderr. |
detect |
Fingerprint the obfuscator family; do not unwrap. |
inspect |
Disassemble a .pyc or bare marshal stream. |
version |
Print the version. |
FILE may be - to read from stdin.
Cleanup is a flag on crack (--clean), not a separate command.
crack options
| Flag | Default | Meaning |
|---|---|---|
-o, --output DIR |
— | Write the full artifact tree instead of stdout. |
--json |
off | Emit report.json to stdout. |
--clean |
off | Also emit a cleaned view (see Cleanup). |
--target-python X.Y |
host | Version to assume for marshal payloads. |
--decompiler NAME |
auto | Force a backend instead of routing by version. |
--max-layers N |
512 | Layer cap; 0 for no limit. |
--max-memory MiB |
1024 | Total decoded output; 0 for no limit. |
--max-fold MiB |
256 | Largest single decoded value; 0 for no limit. |
--timeout SEC |
300 | Wall clock; 0 for no limit. |
--no-peel |
off | Skip unwrapping; report detection only. |
--no-detect |
off | Skip fingerprinting. |
--plugins |
off | Load third-party ingest adapters. |
-q, --quiet |
off | Suppress the stderr summary. |
Exit codes: 0 complete, 1 incomplete, 2 input could not be processed.
Identifying the obfuscator does not affect the exit code.
Output
$ snakecharm crack sample.py
import os
import sys
MESSAGE = "recovered exactly"
LIMIT = 42
...
31 layers, depth 30, 431,204 bytes folded in 0.006s
detected: freecodingtools (100%)
With -o:
$ snakecharm crack sample.py -o out/
wrote out/
2 layers, depth 1, 1,966 bytes folded in 0.001s
detected: hyperion (100%), freecodingtools (67%)
$ find out -type f
out/deobfuscated.py
out/layers/00_depth0.py
out/layers/01_depth1.py
out/report.json
--clean adds out/cleaned.py. layers/ stays byte-exact.
Each layer file carries its derivation:
# ---------------------------------------------------------------
# snakecharm layer 1 (depth 1, source)
# provenance : zlib <- b64decode
# from : exec() at line 13, col 0 of layer 0
# sha256 : 58dda7518dfd60b35d3b522a654ad6c8532b076846f3a37d98340b05f2131acd
# ---------------------------------------------------------------
When a site cannot be resolved it is reported, not skipped:
$ snakecharm crack script.py
not_reached : could not fold the argument of exec(): not reached during
interpretation, and folding it in isolation also failed: unbound name 'handle'
incomplete: 1 site(s) could not be resolved statically
Library
import snakecharm
result = snakecharm.crack(open("sample.py", "rb").read())
result.code # deepest recovered source
result.complete # bool
result.unresolved # [Unresolved(reason, message, layer, line, col)]
result.layers # [Layer(...)], each with .provenance and .digest
result.detections # [Detection(family, confidence, evidence)]
result.stats # {'layers': 31, 'max_depth': 30, 'bytes_folded': 431204, ...}
result.cleaned # str | None, when Options(clean=True)
result.save("out/") # writes the tree above
snakecharm.Options mirrors the CLI flags. snakecharm.Budget holds the
resource limits.
To run the cleanup passes on their own, without peeling:
from snakecharm.clean import clean
result = clean("X = 1 + 2\n")
result.code # 'X = 3'
result.changed, result.refused, result.rolled_back
Handles
exec/eval/compilechains nested to arbitrary depth (default cap 512).- Wrappers:
try/except,with, class bodies,if __name__ == "__main__",async def, decorators. .pycfiles and bare marshal streams, Python 3.8–3.14, from any host version.- PEP 263 source encodings, decoded before analysis and recorded in the provenance chain.
- Family fingerprinting: freecodingtools/pyobfuscate.com, Hyperion, Berserker,
Kramer, BlankOBF, Jawbreaker, PlusOBF, Vare, Anubis, Mr.X, PyArmor, Intensio,
Nuitka, PyInstaller, python-minifier. Detection is additive — an unrecognised
sample is still unwrapped. Signatures not confirmed against real output are
marked
[unvalidated signature].
Nothing in the default path executes sample code. Analysing live malware in a disposable VM is still advisable.
Cleanup
--clean runs, in order: constant folding, getattr(o, "lit") → o.lit,
removal of branches whose condition folds to a constant, and removal of unused
function locals.
Module-level globals and uncalled functions are never removed. A round that fails to parse or compile is rolled back whole.
Does not handle
| Accurate 3.11+ decompilation | No released decompiler handles it. Falls back to annotated disassembly, labelled as such. |
| Identifier renaming | Mangled names are returned unchanged. |
| Packed executables | PyInstaller / cx_Freeze / py2exe are not unpacked. detect still names the packer. |
| Nuitka | Native machine code; no .pyc exists in the binary. Named, not recovered. |
| PyArmor BCC / RFT | Native C, and one-way identifier destruction, respectively. |
| MBA obfuscation | Arithmetic seeded with live in-scope variables defeats static folding. |
| Runtime-fetched payloads | Not present in the file. |
Decompiler backends (uncompyle6, decompyle3, pycdc) are used if found on
PATH. They are GPL, so they are never bundled, never imported, and invoked
only as subprocesses; without them, .pyc input renders as disassembly.
MIT. Created and provided by veryserious.systems.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file snakecharm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: snakecharm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 140.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e747032b17b289f19ea4fdf3533dac7a08c2cab1211120eef0f21ae6e631a38
|
|
| MD5 |
a4e11bf6cf90a012c639d8af14c5f551
|
|
| BLAKE2b-256 |
28220e0af0de46a483b31fd4cb273b38c8d1fd17a2513f8383fabd91c9c8a2a4
|