VM Obfuscation
What is VM Obfuscation?
VM obfuscation is the most advanced form of code protection. It transforms your JavaScript functions into custom bytecode that runs on a virtual machine embedded in the output. The original logic is completely hidden — no JavaScript to reverse-engineer.
Targeting Specific Functions
For optimal performance, VM-obfuscate only your most sensitive code using vmTargetFunctionsMode.
Strict Mode Compatibility
VM obfuscation needs to know at compile time whether code runs in strict mode. If your code relies on strict mode behavior, you must explicitly declare it.
Identifier Preprocessing
The vmPreprocessIdentifiers option (enabled by default) renames all non-global identifiers to unique hexadecimal names before VM obfuscation. This step eliminates variable shadowing that can cause scope resolution issues in the VM bytecode.
Debug Protection
The vmDebugProtection option adds anti-debugging measures to the VM runtime, making it significantly harder to reverse-engineer your protected code.
More anti-debugging techniques will be added in future releases.
VM Options Reference
Using the NPM Package
You can use VM obfuscation in your build pipeline via the javascript-obfuscator npm package. The obfuscatePro() method connects to the obfuscator.io cloud service for VM-based bytecode obfuscation.
Installation
npm install --save-dev javascript-obfuscator
Usage
const JavaScriptObfuscator = require('javascript-obfuscator');
const sourceCode = `
function calculatePrice(qty, price) {
const discount = 0.15;
return qty * price * (1 - discount);
}
`;
const result = await JavaScriptObfuscator.obfuscatePro(
sourceCode,
{
vmObfuscation: true,
vmObfuscationThreshold: 1,
compact: true
},
{
apiToken: process.env.OBFUSCATOR_API_TOKEN
}
);
console.log(result.getObfuscatedCode());How VM Transforms Code
With vmTargetFunctionsMode: 'root' (default), VM obfuscation transforms the body of each root-level function into bytecode (depending on vmObfuscationThreshold), but keeps the function name unchanged.
// Input
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// Output - function NAME is preserved, BODY is VM-transformed
function fibonacci(b) { // name kept as-is
return vm.call(...); // body converted to bytecode
}This design maintains compatibility with code that calls these functions from the global scope or expects them on the global object. The function body is fully protected, but the name remains visible.
Hiding sensitive function names:
If a function name reveals what the code does (e.g., validateLicense, decryptData), use one of these approaches:
Option 1: Wrap in an IIFE (Recommended)
Place sensitive functions inside an IIFE or any other function body. Functions inside other functions are not root-level, so they get fully VM-transformed including their names.
// Wrap sensitive code in IIFE (function() { function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } // Use fibonacci here... })(); // "fibonacci" name is completely hidden in outputOption 2: Rename globals + encoding (Use with caution)
Enable
renameGlobals: true,vmPreprocessIdentifiers: true,vmBytecodeEncoding: true, andvmBytecodeArrayEncoding: true. This renames root-level identifiers and encrypts them in the bytecode.
Protection Against LLM Analysis
Modern LLMs (Large Language Models) like ChatGPT and Gemini in Thinking modes are capable of analyzing code patterns. On small input code, LLMs may attempt to predict what code does based on certain factors.
Factors that enable LLM analysis (VM Low preset):
Visible root-level names: Root-level function and variable names are preserved by default to maintain compatibility with code that relies on calling these from the global scope.
Unencrypted constant pool: Without bytecode encoding, the constant pool containing function names is stored in plain text.
Predictable VM structure: Without opcode/bytecode instructions shuffle, encryption, and other hardening options, LLMs can attempt to traverse small VM code and make assumptions.
How to prevent LLM analysis:
Hide global-level names: Wrap sensitive code in an IIFE or use
renameGlobals. See How VM Transforms Code for details.Enable bytecode encryption: Use
vmBytecodeEncoding: trueandvmBytecodeArrayEncoding: truewithvmBytecodeArrayEncodingKeyandvmBytecodeArrayEncodingKeyGetterto encrypt the constant pool and bytecode array.Enable hardening options: Use
vmInstructionShuffle,vmOpcodeShuffle,vmDecoyOpcodes,vmDeadCodeInjection, and other options available in VM Medium preset and above.
Troubleshooting VM Obfuscation Issues
VM obfuscation completely transforms your code into custom bytecode. Ideally, this transformation should handle all possible code patterns and edge cases seamlessly. However, due to the complexity of VM obfuscation, there are still edge cases that may not be fully supported. This means that VM-obfuscated code must be carefully tested in at least all happy-path scenarios before deployment.
If your code doesn't work after VM obfuscation:
1. Ensure identifier preprocessing is enabled
Check that
vmPreprocessIdentifiersis enabled (default: true). This option eliminates variable shadowing issues that can cause scope resolution errors in VM bytecode. If it's disabled, try enabling it first.2. Narrow down the problematic code
Use
vmTargetFunctionsMode: 'comment'to selectively VM-obfuscate specific functions. Add the/* javascript-obfuscator:vm */comment only to individual functions and test incrementally to identify which function causes the issue.3. Contact support with details
Email support@obfuscator.io with as much information as possible:
- Error message and stack trace
- Environment (browser/Node.js version)
- Obfuscator options/preset used
- Obfuscator version (shown in the bottom-right corner of the editor)
- Minimal code sample that reproduces the issue (ideally the narrowed-down problematic function)
