Created
June 11, 2025 19:36
-
-
Save bobby-tablez/bb1f13c10231192a8e0ebc58548951d3 to your computer and use it in GitHub Desktop.
Kramer Python Deobfuscator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This Python script decrypts Kramer obfuscation by reversing its obfuscation and bruteforces the key. | |
# Detects and uses al CPU threads, so your milage may vary with how long it takes. | |
# Defaults to key ranges from 3-1000000 as generated in kramer.py | |
# If the key is found it will print the result to stdout | |
# Obfuscator: https://github.com/billythegoat356/Kramer | |
import sys | |
import marshal | |
import types | |
import dis | |
from multiprocessing import Pool, cpu_count | |
from binascii import unhexlify | |
# === Core Kramer Decryption Logic === | |
strings = "abcdefghijklmnopqrstuvwxyz0123456789" | |
def dkyrie(text): | |
result = "" | |
for c in text: | |
if c in strings: | |
i = strings.index(c) + 1 | |
if i >= len(strings): | |
i = 0 | |
result += strings[i] | |
else: | |
result += c | |
return result | |
def decrypt_kramer(blob, key): | |
try: | |
lines = [unhexlify(line).decode('utf-8', errors='ignore') for line in blob.split('/') if line] | |
joined = ''.join(lines).replace('ζ', '\n') | |
shifted = ''.join(chr(ord(c) - key) if c != '\n' else '\n' for c in joined) | |
return dkyrie(shifted) | |
except Exception: | |
return None | |
# === PYC Parsing Helpers === | |
def load_pyc(filename): | |
with open(filename, 'rb') as f: | |
f.seek(16) # Skip magic number, flags and header | |
return marshal.load(f) | |
def extract_large_constants(code_obj, min_size=1000): | |
if isinstance(code_obj, types.CodeType): | |
for const in code_obj.co_consts: | |
if isinstance(const, str) and len(const) > min_size: | |
yield const | |
elif isinstance(const, types.CodeType): | |
yield from extract_large_constants(const, min_size) | |
# === Brute-force Logic === | |
def attempt_key(args): | |
blob, key = args | |
result = decrypt_kramer(blob, key) | |
if result and "import" in result and "def" in result: | |
return key, result | |
return None | |
keymin = 3 | |
keymax = 1000000 | |
def run_brute_force(pyc_path, fmin, fmax): | |
print(f"[+] Loading {pyc_path}") | |
code = load_pyc(pyc_path) | |
candidates = list(extract_large_constants(code)) | |
if not candidates: | |
print("[-] No large string constants found.") | |
return | |
print(f"[+] Found {len(candidates)} large candidate string(s).") | |
blob = candidates[0] | |
print(f"[*] Trying keys from {fmin} to {fmax} using {cpu_count()} processes...") | |
with Pool(cpu_count()) as pool: | |
for i, result in enumerate(pool.imap_unordered(attempt_key, ((blob, k) for k in range(fmin, fmax)), chunksize=500)): | |
if result: | |
key, decrypted = result | |
print(f"\n[+] Decryption success with key: {key}") | |
print(decrypted) | |
return | |
if (i+1) % 1000 == 0: | |
print(f"[.] Tried {i+1} keys...") | |
print("[-] Decryption failed with current key space.") | |
# === Entry Point === | |
if __name__ == "__main__": | |
if len(sys.argv) != 2: | |
print(f"Usage: python {sys.argv[0]} <input.pyc>") | |
sys.exit(1) | |
run_brute_force(sys.argv[1], fmin=keymin, fmax=keymax) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment