Skip to content

About

A multi-stage toolkit that transforms readable JavaScript into obfuscated code using encoding, chunking, randomization, and injected dead code.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

24 Commits

Folders and files

Repository files navigation

ObfuscateJS

ObfuscateJS is a multi-step JavaScript obfuscation toolkit designed to transform clean, readable JS into heavily distorted and unreadable code. It uses a layered pipeline of base64 encoding, chunking, string escaping, random declarations, and injected dead code to maximize entropy and minimize human readability. This project is perfect for those who want to experiment with advanced JS obfuscation techniques or protect their scripts from reverse engineering.

Pipeline stages

# Stage Script What it does
1 base64 JsToBase64.py Wraps the payload in eval(atob("..."))
2 chunks AtobToChunks.py Re-encodes and splits the payload into a random chunk array inside an IIFE
3 varsplit AtobChunkVariableSplitter.py Gives every chunk its own randomly named variable
4 strings StringEscaper.py (new) Rewrites string literals as random \xHH / \uHHHH escapes
5 comments AddRandomComments.py Inserts gibberish comments, especially above functions
6 declarations AddRandomDeclarations.py Injects N random unused variable declarations
7 deadcode AddDeadCode.py Injects never-executing code caves mined from ./deadcode

Usage

One command (new, recommended)

python3 obfuscate.py --file main_payload.js -o obfuscated.js --seed 42 \
    --declarations 1500 --caves 420

Per-stage byte sizes and entropy are printed as it runs. Useful options:

--skip strings comments     # skip stages by name
--seed 42                   # fully reproducible output
--string-ratio 0.8          # fraction of strings to escape
--comment-probability 0.15  # per-line comment probability
--deadcode-dir ./deadcode   # corpus directory for code caves

Classic chained commands

python3 JsToBase64.py --file main_payload.js > 2.js;
python3 AtobToChunks.py --file 2.js > 3.js;
python3 AtobChunkVariableSplitter.py --file 3.js > 4.js;
python3 StringEscaper.py --file 4.js > 4b.js;
python3 AddRandomComments.py --file 4b.js > 5.js;
python3 AddRandomDeclarations.py --file 5.js --count 1500 > 6.js;
python3 AddDeadCode.py --file 6.js --caves 420 > 7.js;
cp 7.js replacement.js;
python3 make_html.py --output obf_test_page.html

Every stage accepts --file/-f or --code/-c, optional --output/-o, and --seed for reproducible output.

Measuring entropy

python3 entropy.py replacement.js --compare main_payload.js   # cross-platform
./entropy.sh replacement.js                                   # original bash version

Web UI

A terminal-style web interface (webui/) runs the entire pipeline client-side (webui/pipeline.js is a full JS port of the Python stages, with a seeded PRNG for reproducible output). It includes per-stage toggles, parameter controls, live byte/entropy stats, an engine toggle (browser / server), and a sandboxed verify command that executes both the original and the obfuscated payload in isolated iframes and proves their console output matches.

python3 server.py          # http://localhost:32552

server.py is zero-dependency (stdlib only): it serves the static UI and the REST API. The port defaults to 32552 and is configured in config.ini ([server] port = ...) or overridden by OBFUSCATE_PORT.

REST API

Method Route Description
GET /api/health {"status": "ok"}
GET /api/config host, port, stage list, corpus size
GET /api/corpus?n=8 random dead-code snippets + corpus size
POST /api/obfuscate run the pipeline server-side

POST /api/obfuscate body (all fields except code optional):

{"code": "console.log('hi');", "seed": 42, "stages": ["base64", "chunks"],
 "declarations": 1500, "caves": 420, "stringRatio": 0.8, "commentProbability": 0.15}

Example:

curl -X POST http://localhost:32552/api/obfuscate \
    -H 'Content-Type: application/json' \
    -d '{"code": "console.log(\"hi\");", "seed": 42}'

Counts are clamped (declarations ≤ 20000, caves ≤ 5000) and payloads over 512 KB are rejected. A fixed seed makes the response fully deterministic.

Docker

docker build -t obfuscatejs:latest .
docker run --rm -p 32552:32552 obfuscatejs:latest     # web UI + API on :32552

The image is based on python:3.12-slim, installs nothing (stdlib only), and runs as an unprivileged user with a /api/health healthcheck. CLI stages work too:

docker run --rm -v "$PWD:/work" obfuscatejs:latest \
    python3 obfuscate.py --file /work/payload.js -o /work/obfuscated.js --seed 42

docker-compose.yml is included as a convenience (docker compose up --build).

Helper scripts

Script What it does
bash start.sh Builds the image if missing and (re)starts the obfuscatejs container on the configured port (--build forces a rebuild)
bash test.sh Builds the test image target (adds pytest + Node) and runs the entire suite inside Docker; extra args are forwarded to pytest
sudo bash install.sh Installs to /opt/obfuscatejs, registers and enables a systemd service, and starts it — the container survives reboots via Restart=always + --restart unless-stopped

Uninstall: sudo systemctl disable --now obfuscatejs && sudo rm /etc/systemd/system/obfuscatejs.service && sudo rm -rf /opt/obfuscatejs

Suggested tags

Tag When to use
obfuscatejs:latest Mutable pointer to the current stable release
obfuscatejs:2.1.0 Immutable semantic-version release (REST API + scripts)
obfuscatejs:2.1, obfuscatejs:2 Floating minor/major tags that track compatible releases
obfuscatejs:test Test image built from the test target (pytest + Node inside)
obfuscatejs:git-<short-sha> CI builds, e.g. obfuscatejs:git-3c8e5f1
ghcr.io/<owner>/obfuscatejs:<tag> Same tags prefixed when pushing to a registry

Build once, tag many:

docker build -t obfuscatejs:2.1.0 -t obfuscatejs:2.1 -t obfuscatejs:2 -t obfuscatejs:latest .

Running the tests

The project ships with a pytest suite covering every stage, the JS pipeline port, the REST API, config loading, and the web server — including Node.js execution-equivalence checks that prove the obfuscated output behaves identically to the original payload:

pip install pytest
python3 -m pytest tests/

Or run the entire suite inside Docker (test image includes pytest + Node):

bash test.sh

Tests that execute JavaScript require a node binary; they are skipped automatically when Node is not installed (the Docker test target installs Node, so nothing skips there).

Side notes

  • JsToBase64 is a prerequisite for AtobToChunks
  • AtobToChunks is a prerequisite for AtobChunkVariableSplitter to work
  • AddDeadCode mines JS files from ./deadcode (the more the better); if the corpus is missing or empty it falls back to built-in snippets instead of crashing

Changelog

  • New: REST API — POST /api/obfuscate (validated, seeded, clamped), GET /api/config; web UI gained a browser/server engine toggle
  • New: config.ini — server config, default port 32552, env-var overrides
  • New: test.sh (full suite inside Docker via the test image target), start.sh (build + run on configured port), install.sh (/opt/obfuscatejs + systemd, survives reboots)
  • New: webui/ — terminal-style web interface; full pipeline ported to browser JS (pipeline.js), sandboxed equivalence verification, live entropy stats
  • New: server.py — zero-dependency stdlib server for the web UI with /api/health and /api/corpus
  • New: Docker support — Dockerfile (slim, unprivileged, healthcheck), .dockerignore, docker-compose.yml
  • New: StringEscaper.py stage — hex/unicode-escapes string literals (both quote styles)
  • New: obfuscate.py — run the entire pipeline in one command, with --seed, --skip, and per-stage stats
  • New: entropy.py — cross-platform Python port of entropy.sh, with --compare
  • New: full pytest unit-test suite in tests/ (109 tests, incl. Node execution-equivalence)
  • Fix: AddDeadCode no longer crashes when ./deadcode is empty or missing, and its typeof window === 'undefined' guard is now always-false in Node as well as browsers
  • Fix: AddRandomDeclarations/AddDeadCode now honor --count/--caves exactly (previously capped by the input's line count), and injected names are guaranteed unique
  • Fix: AtobChunkVariableSplitter no longer uses eval() on input and locates the chunk array by pattern instead of a hardcoded line number
  • Fix: make_html.py is now a proper CLI (--template, --replacement, --output) instead of crashing on import

Mad respect to paopao2 for his repo of JS leetcode, I have added snippets of homie's code.

About

A multi-stage toolkit that transforms readable JavaScript into obfuscated code using encoding, chunking, randomization, and injected dead code.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages