:::::::.. ... ::: :::. . : ;;;;``;;;; ;; ;;; ;;`;; ;;,. ;;; [[[,/[[[' [[' [[[ ,[[ '[[, [[[[, ,[[[[, $$$$$$c $$ $$$c$$$cc$$$c $$$$$$$$"$$$ 888b "88bo,88 .d888 888 888,888 Y88" 888o MMMM "W" "YmmMMMM"" YMM ""` MMM M' "MMM
Virtualization-Based JavaScript Obfuscation
Compiles JavaScript functions into custom bytecode executed by an embedded virtual machine.
No deobfuscator exists for RuamVM bytecode.
Installation · Quick Start · How It Works · Presets · API
Most JavaScript obfuscators apply surface-level transformations — renaming variables, encoding strings, inserting dead code. A motivated attacker can undo these with off-the-shelf tools, logic analysis, or by patching functions at runtime.
Ruam takes a fundamentally different approach. It compiles your JavaScript into a custom bytecode instruction set and replaces the original source with a compact virtual machine that executes an unintelligible instruction stream. The original code is destroyed — it does not exist anywhere in the output.
Source JS → Transformed JS (still JS)
- Same language, same semantics
- AST-reversible transformations
- Automated deobfuscation tools exist
Source JS → Custom Bytecode + Embedded VM
- Original source is destroyed
- Must reverse-engineer the VM itself
- No deobfuscator exists
| 300+ opcode open-source ISA | Full-coverage instruction set spanning 26 categories — stack, arithmetic, bitwise, comparison, control flow, property access, scoping, calls, classes, iterators, destructuring, async/await, generators, and more. |
| Per-build polymorphism | Every build produces structurally unique output. No two builds share the same encoding, identifier names, or internal structure — even from the same source. |
| Multi-layer encryption | Bytecode is encrypted with multiple independent layers. Keys are derived implicitly from the output's own structure — no key material appears in plaintext. |
| Anti-tamper binding | The VM's decryption logic is entangled with its own source. Modifying the interpreter to add logging or breakpoints corrupts all decryption — the bytecode becomes unrecoverable. |
| Runtime metamorphism | The instruction set mutates during execution. The same opcode byte maps to different operations at different points in the program. Static disassembly produces incorrect results. |
| Optimizing compiler | Multi-tier optimization pipeline minimizes the performance cost of virtualization. Fused instructions, register promotion, and inline operations keep overhead competitive for a JS-in-JS interpreter. |
| Thousands of tests | Comprehensive test suite covering core JS semantics, stress/edge cases, security properties, and integration scenarios — including randomized fuzz tests. |
npm install ruam# Obfuscate a file in-place
ruam app.js
# Obfuscate to a new file
ruam app.js -o app.obf.js
# Obfuscate a directory with medium preset
ruam dist/ --preset medium
# Maximum protection
ruam dist/ --preset max
# Interactive wizard (or just run `ruam` with no args)
ruam -Iimport { obfuscateCode, obfuscateFile, runVmObfuscation } from "ruam";
// Synchronous — obfuscate a code string
const result = obfuscateCode('function hello() { return "world"; }');
// Async — obfuscate a file
await obfuscateFile("src/app.js", "dist/app.js");
// Async — obfuscate a directory with options
await runVmObfuscation("dist/", {
include: ["**/*.js"],
exclude: ["**/node_modules/**"],
options: { preset: "max" },
});Not every function needs virtualization. Use comment mode to protect only what matters:
/* ruam:vm */
function sensitiveLogic() {
// → compiled to bytecode
}
function publicHelper() {
// → untouched, no overhead
}ruam app.js -m commentRuam applies multiple independent protection layers that compound the difficulty of reverse engineering. Each layer forces an attacker to solve a distinct problem before they can make progress on the next.
| Layer | Description |
|---|---|
| Virtualization | Original JS is compiled to a custom bytecode ISA. The source code is destroyed — an attacker must reverse-engineer the entire VM to recover any logic. |
| Polymorphic encoding | The instruction encoding, identifiers, and internal structure are randomized per build. Reversing one build provides zero reusable knowledge about any other build. |
| Instruction encryption | Every instruction is individually encrypted. The key is derived from properties of the output itself — no key material is stored in plaintext. Sequential decryption is required; you cannot jump into the middle of a bytecode stream. |
| Integrity binding | The decryption process is entangled with the VM interpreter's own source. Modifying the VM in any way (adding logging, setting breakpoints, patching behavior) silently corrupts all decryption. |
| VM shielding | Each function can receive its own isolated micro-interpreter with unique encoding, encryption keys, and internal structure. Reversing one function's interpreter does not help with any other. |
| String encoding | All string constants in the bytecode are independently encrypted. No plaintext strings survive compilation — not variable names, property keys, or literal values. |
| Anti-debug | Multi-layered runtime detection with escalating response. No eval(), new Function(), debugger statements, or console calls — fully compatible with strict CSP environments including Chrome extensions. |
| Arithmetic obfuscation | Arithmetic and bitwise operations within the interpreter are replaced with mathematically equivalent but opaque compound expressions, making the interpreter logic harder to follow. |
| String atomization | All string literals in the interpreter — property names, method names, internal labels — are replaced with encoded table lookups. Zero hardcoded strings survive in the output. |
| Block permutation | Bytecode basic blocks are randomly reordered within each compiled function. Control flow is preserved through explicit jumps. The bytecode stream no longer reflects the original program structure. |
| Key scattering | Cryptographic key materials are split into fragments distributed across multiple closure scopes in the output. Recovering any single key requires tracing the entire scope chain. |
Additional hardening options include dead bytecode injection, stack value encryption, decoy opcode handlers, handler fragmentation, runtime opcode mutation, and polymorphic string decoding — all configurable independently or via presets.
Three built-in presets provide escalating protection. Explicit options always override preset values.
| Preset | What's enabled | Use case |
|---|---|---|
low |
VM compilation only | Development, debugging, basic IP protection |
medium |
+ identifier renaming, bytecode encryption, instruction encryption, decoy & dynamic opcodes, string atomization, key scattering | Production — balanced protection and size |
max |
Everything — all encryption layers, VM shielding, debug protection, integrity binding, arithmetic obfuscation, dead code, stack encoding, block permutation, string atomization, key scattering | High-value targets — maximum protection |
ruam dist/ --preset maxobfuscateCode(source, {
preset: "medium",
debugProtection: true, // override: add debug protection to medium
});Virtualization inherently adds overhead — this is the tradeoff for protection that surface-level transforms cannot provide. Ruam's multi-tier optimization pipeline minimizes the cost.
Typical overhead: ~38–45x native speed on compute-heavy benchmarks, competitive for a pure JS-in-JS interpreter.
Use selective obfuscation (-m comment) to protect only sensitive functions and leave hot paths running as native JS.
ruam <input> [options]
Presets:
--preset <name> Apply a preset: low, medium, max
Output:
-o, --output <path> Output file or directory (default: overwrite input)
Compilation:
-m, --mode <mode> Target mode: "root" (default) or "comment"
-e, --encrypt Enable bytecode encryption
-p, --preprocess Rename all identifiers before compilation
Security:
-d, --debug-protection Enable anti-debugger protection
--no-debug-protection Disable anti-debugger (overrides preset)
--rolling-cipher Enable instruction encryption
--integrity-binding Bind decryption to interpreter integrity
--vm-shielding Per-function isolated micro-interpreters
Hardening:
--dynamic-opcodes Filter unused opcodes from the interpreter
--decoy-opcodes Add fake opcode handlers
--dead-code Inject dead bytecode sequences
--stack-encoding Encrypt values on the VM stack
--mba Arithmetic obfuscation (mixed boolean arithmetic)
--handler-fragmentation Split handler logic into interleaved fragments
--string-atomization Replace interpreter strings with encoded lookups
--polymorphic-decoder Per-build randomized string decoding chain
--scattered-keys Fragment key materials across closure scopes
--block-permutation Shuffle bytecode basic block order
--opcode-mutation Insert runtime handler table mutations
Environment:
--target <env> Target environment: node, browser (default), browser-extension
File Selection:
--include <glob> File glob for directory mode (default: "**/*.js")
--exclude <glob> Exclude glob (default: "**/node_modules/**")
Other:
--debug-logging Inject verbose VM trace logging
-I, --interactive Launch interactive configuration wizard
-h, --help Show help
-v, --version Show versionSynchronously obfuscates a JavaScript source string. Returns the obfuscated code as a string.
import { obfuscateCode } from "ruam";
const output = obfuscateCode(source, {
preset: "medium",
targetMode: "root",
});Reads a file, obfuscates it, and writes the result. Returns a Promise<void>.
import { obfuscateFile } from "ruam";
await obfuscateFile("src/app.js", "dist/app.js", { preset: "max" });Obfuscates all matching files in a directory. Returns a Promise<void>.
import { runVmObfuscation } from "ruam";
await runVmObfuscation("dist/", {
include: ["**/*.js"],
exclude: ["**/node_modules/**"],
options: { preset: "medium" },
});| Option | Type | Default | Description |
|---|---|---|---|
preset |
"low" | "medium" | "max" |
— | Apply a preset configuration |
targetMode |
"root" | "comment" |
"root" |
"root": all top-level functions. "comment": only /* ruam:vm */ annotated |
threshold |
number |
1.0 |
Probability (0–1) that an eligible function is compiled |
target |
"node" | "browser" | "browser-extension" |
"browser" |
Target execution environment |
preprocessIdentifiers |
boolean |
false |
Rename all local identifiers before compilation |
encryptBytecode |
boolean |
false |
Encrypt bytecode using an environment fingerprint key |
rollingCipher |
boolean |
false |
Per-instruction encryption with implicit key derivation |
integrityBinding |
boolean |
false |
Bind decryption to interpreter source integrity (auto-enables rollingCipher) |
vmShielding |
boolean |
false |
Per-function micro-interpreters with unique encoding (auto-enables rollingCipher) |
debugProtection |
boolean |
false |
Multi-layered anti-debugger with escalating response |
dynamicOpcodes |
boolean |
false |
Filter unused opcodes from the interpreter |
decoyOpcodes |
boolean |
false |
Add fake opcode handlers to the interpreter |
deadCodeInjection |
boolean |
false |
Inject unreachable bytecode sequences |
stackEncoding |
boolean |
false |
Encrypt values on the VM stack at runtime |
mixedBooleanArithmetic |
boolean |
false |
Replace arithmetic/bitwise ops with opaque MBA expressions |
handlerFragmentation |
boolean |
false |
Split opcode handlers into interleaved fragments |
stringAtomization |
boolean |
false |
Replace all interpreter string literals with encoded table lookups (auto-enables polymorphicDecoder) |
polymorphicDecoder |
boolean |
false |
Per-build randomized byte-operation chain for string decoding |
scatteredKeys |
boolean |
false |
Fragment and scatter key materials across closure scopes |
blockPermutation |
boolean |
false |
Shuffle bytecode basic block order within compiled functions |
opcodeMutation |
boolean |
false |
Insert runtime handler table mutations (auto-enables rollingCipher) |
debugLogging |
boolean |
false |
Inject verbose trace logging into the interpreter |
Ruam compiles the full range of modern JavaScript:
- Functions (declarations, expressions, arrows, generators, async, async generators)
- Classes (inheritance, constructors, methods, getters/setters, computed properties, static members,
super) - Control flow (
if,for,for-in,for-of,while,do-while,switch, labeled statements) - Exception handling (
try/catch/finally,throw) - Destructuring (array and object patterns, defaults, rest elements, nested)
- Spread/rest (
...argsin calls, arrays, and object literals) - Closures and lexical scoping (
let/constwith proper TDZ, per-iteration bindings) - Async/await, generators (
yield,yield*), async generators - Template literals, tagged templates, optional chaining, nullish coalescing
- Computed property names, shorthand properties/methods, symbol keys
Use --target to optimize output for your deployment environment:
| Target | Description |
|---|---|
browser |
Plain <script> tags. Default. |
node |
Node.js (CJS or ESM modules). |
browser-extension |
Chrome extension MAIN world content scripts. Wraps output to avoid TrustedScript CSP errors. |
ruam content-script.js --target browser-extension --preset max- Node.js >= 18
- ESM (
"type": "module")