Skip to content

techlinn/WhatThePy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python 3.8+ Platform License

WhatThePy

People kept telling me my code looked bad. So I wrote a tool to make it worse (on purpose)


What Is This?

WhatThePy obfuscates Python code using compression, a custom SHA256-based stream cipher, dynamic key generation, and runtime execution. The result is compact, messy code that still runs fine but is much harder to read and analyze.

Important: This is for fun and learning. It deters casual viewers but will not stop someone who knows what they are doing.


Before & After

Your Code After WhatThePy
def greet(name):
    message = f"Hello, {name}!"
    print(message)
    return message

if __name__ == "__main__":
    greet("World")
...
洪ΠОΖНСΔΘΜΖФ鷹=lambda:_СΖΙ虎ΣΓФЙТХВГ(_Θ宙鷹荒鷹日ЙΖΥХДΑ(_У荒ЛΡ荒黃ΣНПΘР豹,_ДΒПΘΞНΠΡ麟麟Γ玄,_РК虎Π宙玄ΕНКΛ龍Ж,_麒鳳ΞБΕГОП宙БΞ獅),_ΥДΓ日Ζ地УΙΤ麒熊麒)
def _日И月黃宇ОНΖ麟龍麒洪():return bytes(_洪ΠОΖНСΔΘΜΖФ鷹())
_다허يεمα地러ص서Т風=_日И月黃宇ОНΖ麟龍麒洪()
def _ذרгΚЛΤ조さ洪터βש(_바かたצדαΜח春غР秋,_צカישىこضФオמП寒):
 _אנЮ天라ΘШנ火昃הן=bytes(_v^186 for _v in [210, 219, 201, 210, 214, 211, 216]).decode()
 _木סкעل寒бえいΚ머Φ=bytes(_v^109 for _v in [30, 5, 12, 95, 88, 91]).decode()
 _川거黃麒جウ宙머辰けиף=__import__(_אנЮ天라ΘШנ火昃הן);_ךえ黃لظ허אס昃타麒霜=getattr(_川거黃麒جウ宙머辰けиף,_木סкעل寒бえいΚ머Φ)
 _너さИر雨חר雪러Х퍼風=bytearray(len(_צカישىこضФオמП寒));_玄СβחΓ辰ΡΧסתقケ=32
 for _소ד河そえΑض冬ЫてДذ in range(0,len(_צカישىこضФオמП寒),_玄СβחΓ辰ΡΧסתقケ):
  _رΘЙЙ日ТРЫЪב山Γ=(_소ד河そえΑض冬ЫてДذ//_玄СβחΓ辰ΡΧסתقケ).to_bytes(8,'little')
  _لפへζひחيטΩたカΩ=_ךえ黃لظ허אס昃타麒霜(_바かたצדαΜח春غР秋+_رΘЙЙ日ТРЫЪב山Γ).digest()
  _قて차あ차Э雪ث黃ضΗ雪=_צカישىこضФオמП寒[_소ד河そえΑض冬ЫてДذ:_소ד河そえΑض冬ЫてДذ+_玄СβחΓ辰ΡΧסתقケ]
  for _Яו暑火ءФعجПΣいق,_כ카ΧΣ터γΗכدすלר in enumerate(_قて차あ차Э雪ث黃ضΗ雪):_너さИر雨חר雪러Х퍼風[_소ד河そえΑض冬ЫてДذ+_Яו暑火ءФعجПΣいق]=_כ카ΧΣ터γΗכدすלר^_لפへζひחيטΩたカΩ[_Яו暑火ءФعجПΣいق]
 return bytes(_너さИر雨חר雪러Х퍼風)
def _ع雪כΨ麒麟лΨα盈霜暑(_머머天로ט盈صלΡ커ط저,_머חتתРΥפ타ΨУへت):
 _كМへしう가電けキ荒בね=bytes(_v^35 for _v in [75, 66, 80, 75, 79, 74, 65]).decode()
 _クア風ΣЛд宿火玄Эи저=bytes(_v^237 for _v in [158, 133, 140, 223, 216, 219]).decode()
 _л더פ虎ほ電Кδ冬다Τو=__import__(_كМへしう가電けキ荒בね);_き퍼水暑노זאΗ사لىט=getattr(_л더פ虎ほ電Кδ冬다Τو,_クア風ΣЛд宿火玄Эи저)
 return _き퍼水暑노זאΗ사لىט(_머חتתРΥפ타ΨУへت+_머머天로ט盈صלΡ커ط저).digest()
def _火دね카ζ列УبلてבЗ(_저ЛЭ河로ם오רעخΣ카,_נ黃تعえカعظさΜ川エ):
 _شסすけγЦВεزΞΞ코=_저ЛЭ河로ם오רעخΣ카[:16];_고زつ玄г宇ЦγЮע霜す=_저ЛЭ河로ם오רעخΣ카[16:]
 _פ코ג자طاい코Б秋ىכ=_ع雪כΨ麒麟лΨα盈霜暑(_شסすけγЦВεزΞΞ코,_נ黃تعえカعظさΜ川エ)
 continues...

Installation

git clone https://github.com/techlinn/WhatThePy.git
cd WhatThePy
pip install -r requirements.txt

Requirements: Python 3.8+, rich, pyfiglet


Usage

python -m whatthepy [file.py] [flags]

Flags:

  • --strip-docs — strip module/class/function docstrings. Off by default so frameworks that read __doc__ at runtime (FastAPI endpoint descriptions, click/Typer help, sphinx, help()) keep working. Pass this only if you don't need docstrings and want the extra obfuscation.
  • --self-contained — embed everything in a single .py (no .dat sidecar). Default is split mode (stub + .dat).
  • --level {low,high}high (default) encrypts string literals into a runtime-decrypted table; low skips that for a faster, weaker pass.

WhatThePy is single-file: it obfuscates one .py at a time and does not rewrite cross-file imports. See Compatibility & Limitations below.

Works with PyInstaller (split mode):

pyinstaller --onefile --clean --add-data "yourfile_payload.dat;." yourfile_obfuscated.py

Add --hidden-import=NAME for any module your code loads dynamically (importlib.import_module(...) / __import__(...) with non-literal names) — static imports are already visible to PyInstaller because the stub re-emits them at the top.


Compatibility & Limitations

WhatThePy is designed so an obfuscated file runs identically and compiles with PyInstaller without runtime errors. Verified against f-strings, match/case literal patterns, PEP 695 type/generics, from __future__ import annotations + dataclass forward refs, TypedDict, Enum, __all__, bytes literals, decorator string args, typing.get_type_hints, default-string args, walrus, closures, __slots__, try/except messages, struct.pack, asyncio, multiprocessing (spawn), __init_subclass__, metaclasses, generators, and function-local imports.

Things to know:

  • Single-file only. No batch/package mode and no cross-file import rewriting. Obfuscate each entry script individually; sibling modules are imported as-is.
  • Relative imports work when the obfuscated file is imported as part of its package (the import machinery sets __package__). Running an obfuscated package module directly as a top-level script will fail relative imports — same as plain Python.
  • Docstrings are kept by default (so FastAPI/click/sphinx/help() work). Use --strip-docs to remove them.
  • inspect.getsource returns the obfuscated stub source (not your original) rather than raising; inspect.getfile returns the stub path. This is expected.
  • Co-located data files (assets next to your source) are not bundled automatically. With PyInstaller, add each via --add-data and read them from sys._MEIPASS at runtime (fall back to os.path.dirname(__file__) when not frozen).
  • Python version pin. The stub checks importlib.util.MAGIC_NUMBER and refuses to run on a different CPython bytecode version than the one it was built with. Build and run on the same Python interpreter. With PyInstaller this is automatic (the bundled interpreter matches).

Protection Level

Threat Protected?
Script kiddies Yes
Casual copy-paste Yes
Quick glances Yes
Basic static analysis Yes
Motivated attackers Partially
Good reverse engineers Maybe

Good for: Learning, CTF challenges, deterring casual viewers Not for: Protecting secrets, valuable IP, or anything critical


How It Works

Source Code
    |
    v
[1] Compress (zlib)
    |
    v
[2] Encrypt (SHA256 stream cipher + custom KDF)
    |
    v
[3] Either:
      -> Store payload in .dat file (split mode)
      -> Or encode using randomized Unicode characters
    |
    v
[4] Build loader stub
    |
    v
[5] Runtime:
      - Rebuild key
      - Decrypt payload
      - Decompress
      - Execute

Disclaimer

This is a fun project for learning and experimentation. It makes code harder to casually read but doesn't provide serious protection. Use it for fun, learning, or light deterrence - not for anything critical.

Made this as a weekend project and please.. Don't use it for serious security needs!


License

MIT License - Do whatever you want with it, just don't blame me!


Made with questionable decisions and too much redbull😭

About

People kept telling me my code looked bad. So I wrote a tool to make it worse (on purpose)

Topics

Resources

License

Stars

50 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages