Claude Code two obfuscated JS files

Cyberizm

Member
Joined
21 May 2026
Messages
22
Reaction score
56
Points
13
We gave Claude Code two obfuscated JS files. No hints. Just “recover the source.” Here’s what happened.



1/12


At Cyberizm, we’re always testing where the line between “security” and “performance theater” actually sits. So we ran a small experiment: two obfuscated JavaScript files — one open-source, one commercial — handed to Claude Code with a short, four-paragraph prompt. No clues about which obfuscator produced which file. No manual reverse-engineering.











2/12


The results?


- Open-source obfuscator (javascript-obfuscator demo): clean source in ~10 minutes.


- Commercial obfuscator (Jscrambler demo): clean source in ~20 minutes.





Both starting from tiny 13-line programs that had been bloated ~200x to demonstrate “strong obfuscation.” In real production, that 200x factor is rare — which actually makes Claude’s job easier , not harder.











3/12


Let’s start with Target 1 (open-source). Input: 1,587 lines, 68 KB — originally a `calculatePrice` function. Claude returned a clean, working reconstruction. But the time wasn’t the story. The layers were.











4/12


Nine distinct defenses stacked in that one file. And Claude identified + undid all nine without us pointing to a single one. The prompt boiled down to: “Here’s a JS file. Give me the source.”





That’s it.











5/12


First attempt: Claude wrote an 883-line static reimplementation — RC4 decryption, base64, binary deserializer, recovered ~500 encrypted strings and the env fingerprint. Then it hit a custom zigzag-varint bytecode format. Instead of giving up, it pivoted: wrote a 48-line instrumentation script that splices logging hooks into the VM at runtime.





From that trace, it recovered:


- function name, params, locals


- constants `[0.15, 100, 1, "calculatePrice"]`


- three keys (blockKey=54, jumpKey=9643, seKey=4168320119)


- the 22-instruction bytecode stream


- and then disassembled opcodes (PUSH_CONST, LOAD_ARG, MUL, GT, JMP_FALSE, RETURN…)





Final output:


js


function calculatePrice(price, quantity) {


const taxRate = 0.15;


const threshold = 100;


let total = price quantity;


if (total > threshold) total = total (1 - taxRate);


return total;


}





Names and argument order not recovered (inferred from behavior), but all literal numbers exact. Behavior matches original.











6/12


The nine layers Claude unwrapped (and why each failed):





1. String array + RC4 – statically decryptable.


2. String array rotation – runs on startup; just read the result.


3. Env fingerprint – XOR with `.length` values, identical on all JS engines. Cosmetic.


4. RC4-encrypted bytecode – key from step 3 → one line.


5. Custom binary serialization – don’t reimplement, instrument it.


6. Opcode shuffling – seed + PRNG reconstructs the table.


7. Operand XOR – one known plaintext recovers blockKey/jumpKey.


8. Stack-value XOR – integers only; floats/strings/objects in plaintext.


9. Anti-debug timing + 1,500-line VM – timing checks corrupt opcodes if you step slowly. Run full-speed with instrumentation and they never trip.





Nine layers. Zero manual intervention.











7/12


Target 2 (commercial — Jscrambler’s public demo). Input: 24,620 bytes. Original: a 5-line BACKGROUND sprite-atlas object. Claude returned clean source in ~20 minutes. This time, static analysis alone — no need for runtime instrumentation.











8/12


Different machinery, same outcome:


- Control-flow flattening (switch state-machine)


- URL-encoded XOR string blob with cyclic key


- Self-replacing decoder (primes for 8 calls, then degrades to direct lookup)


- 3D index table of opaque integer tags


- No bytecode VM





Jscrambler has a public blog post claiming AI can’t reverse their obfuscation. That’s true for a chatbot that can’t execute code. It’s not true for an agent that can.











9/12


So what does this mean for obfuscation?





One year ago: a week of focused human work.


Today: one prompt.





The real question we’re sitting with at Cyberizm: Is client-side obfuscation finished as a serious defense? Or has it just shifted from “secret” to “friction” — still useful against static analyzers and casual scrapers, but not against agents?











10/12


What’s left that an agent genuinely can’t unwrap?


- Anti-tamper integrity checks


- Environment-bound execution (hardware/context)


- Runtime self-modifying code with external dependencies


- Short-lived, per-session keys that never appear in the static bundle





We’re not convinced obfuscation is dead. But “stack more layers” is clearly not a winning strategy anymore.











11/12


If you’ve thrown an agent (Claude, GPT, or other) at obfuscated code — especially in production apps, not demos — what didn’t it crack? What actually held up?





That’s the signal we’re looking for.




12/12


We’ve got a longer write-up with exact prompts, inputs, and outputs. This thread is the TL;DR. Drop a comment or DM if you want the full breakdown — or better, if you have a counterexample where the agent failed hard.





Obfuscation isn’t dead. But the bar just moved. Again.





— Cyberizm team