A JavaScript deobfuscation tool that reconstructs strings and simplifies complex logic.
REstringer automatically detects obfuscation patterns and applies targeted deobfuscation techniques to restore readable JavaScript code. It handles various obfuscation methods while respecting scope limitations and maintaining code functionality.
🌐 Try it online: restringer.tech
📧 Contact: For questions and suggestions, open an issue or find me on Twitter / X - Ben Baryo - @ctrl__esc
✨ Automatic Obfuscation Detection: Uses Obfuscation Detector to identify specific obfuscation types
🔧 Modular Architecture: 40+ deobfuscation modules organized into safe and unsafe categories
🛡️ Safe Execution: Unsafe modules use a sandbox isolated-vm for secure code evaluation
🎯 Targeted Processing: Specialized processors for common obfuscators (obfuscator.io, Caesar Plus, etc.)
⚡ Performance Optimized: Match/transform patterns and performance improvements throughout
🔍 Comprehensive Coverage: Handles string reconstruction, dead code removal, control flow simplification, and more
- Node.js v20+ (v22+ recommended)
npm install -g restringernpm install restringergit clone https://github.com/HumanSecurity/restringer.git
cd restringer
npm installUsage: restringer input_filename [-h] [-c] [-q | -v] [-m M] [-o [output_filename]]
positional arguments:
input_filename The obfuscated JavaScript file
optional arguments:
-h, --help Show this help message and exit
-c, --clean Remove dead nodes after deobfuscation (unsafe)
-q, --quiet Suppress output to stdout
-v, --verbose Show debug messages during deobfuscation
-m, --max-iterations M Maximum deobfuscation iterations (must be > 0)
-o, --output [filename] Write output to file (default: <input>-deob.js)
Basic deobfuscation (print to stdout):
restringer obfuscated.jsSave to specific file:
restringer obfuscated.js -o clean-code.jsVerbose output with iteration limit:
restringer obfuscated.js -v -m 10 -o output.jsQuiet mode (no console output):
restringer obfuscated.js -q -o output.jsRemove dead code (potentially unsafe):
restringer obfuscated.js -c -o output.jsimport {REstringer} from 'restringer';
const obfuscatedCode = `
const _0x4c2a = ['hello', 'world'];
const _0x3f1b = _0x4c2a[0] + ' ' + _0x4c2a[1];
console.log(_0x3f1b);
`;
const restringer = new REstringer(obfuscatedCode);
if (restringer.deobfuscate()) {
console.log('✅ Deobfuscation successful!');
console.log(restringer.script);
// Output: console.log('hello world');
} else {
console.log('❌ No changes made');
}Create targeted deobfuscators using REstringer's modular system:
import {applyIteratively} from 'flast';
import {safe, unsafe} from 'restringer';
// Import specific modules
const normalizeComputed = safe.normalizeComputed.default;
const removeRedundantBlockStatements = safe.removeRedundantBlockStatements.default;
const resolveDefiniteBinaryExpressions = unsafe.resolveDefiniteBinaryExpressions.default;
const resolveLocalCalls = unsafe.resolveLocalCalls.default;
let script = 'your obfuscated code here';
// Define custom deobfuscation pipeline
const customModules = [
resolveDefiniteBinaryExpressions, // Resolve literal math operations
resolveLocalCalls, // Inline function calls
normalizeComputed, // Convert obj['prop'] to obj.prop
removeRedundantBlockStatements, // Clean up unnecessary blocks
];
// Apply modules iteratively
script = applyIteratively(script, customModules);
console.log(script);Use candidate filters to target specific nodes:
import {unsafe} from 'restringer';
import {applyIteratively} from 'flast';
const {resolveLocalCalls} = unsafe;
function resolveGlobalScopeCalls(arb) {
// Only process calls in global scope
return resolveLocalCalls(arb, n => n.parentNode?.type === 'Program');
}
function resolveSpecificFunctions(arb) {
// Only process calls to functions with specific names
return resolveLocalCalls(arb, n => {
const callee = n.callee;
return callee.type === 'Identifier' &&
['decode', 'decrypt', 'transform'].includes(callee.name);
});
}
const script = applyIteratively(code, [
resolveGlobalScopeCalls,
resolveSpecificFunctions
]);Replace or customize built-in methods:
import fs from 'node:fs';
import {REstringer} from 'restringer';
const code = fs.readFileSync('obfuscated.js', 'utf-8');
const restringer = new REstringer(code);
// Find and replace a specific method
const targetMethod = restringer.unsafeMethods.find(m =>
m.name === 'resolveLocalCalls'
);
if (targetMethod) {
let processedCount = 0;
const maxProcessing = 5;
// Custom implementation with limits
const customMethod = function limitedResolveLocalCalls(arb) {
return targetMethod(arb, () => processedCount++ < maxProcessing);
};
// Replace the method
const index = restringer.unsafeMethods.indexOf(targetMethod);
restringer.unsafeMethods[index] = customMethod;
}
restringer.deobfuscate();Safe Modules (src/modules/safe/):
- Perform transformations without code evaluation
- No risk of executing malicious code
- Examples: String normalization, syntax simplification, dead code removal
Unsafe Modules (src/modules/unsafe/):
- Use
eval()in an isolated sandbox for dynamic analysis - Can resolve complex expressions and function calls
- Secured using isolated-vm
- Detection: Identify obfuscation type using pattern recognition
- Preprocessing: Apply obfuscation-specific preparations
- Core Deobfuscation: Run safe and unsafe modules iteratively
- Postprocessing: Clean up and optimize the result
- Validation: Ensure output correctness
Specialized processors handle specific obfuscation patterns:
- Match/Transform Pattern: Separate identification and modification logic
- Performance Optimized: Pre-compiled patterns and efficient algorithms
- Configurable: Support for custom filtering and targeting
restringer/
├── src/
│ ├── modules/
│ │ ├── safe/ # Safe deobfuscation modules
│ │ ├── unsafe/ # Unsafe deobfuscation modules
│ │ └── utils/ # Utility functions
│ ├── processors/ # Obfuscation-specific processors
│ └── restringer.js # Main REstringer class
├── tests/ # Comprehensive test suites
└── docs/ # Documentation
# Quick test suite (without testing against samples)
npm run test:quick
# Watch mode for development (quick tests)
npm run test:quick:watch
# Full test suite with samples
npm testWe welcome contributions! Please see our Contributing Guide for detailed guidelines on:
- Setting up the development environment
- Code standards and best practices
- Module and processor development
- Testing requirements
- Pull request process
- 📖 Processors Guide - Detailed processor documentation
- 🤝 Contributing Guide - How to contribute to REstringer
- 🔍 Obfuscation Detector - Automatic obfuscation detection
- 🌳 flAST - AST manipulation utilities
The REstringer Tri(b)logy:
- 📝 The Far Point of a Static Encounter - Part 1: Understanding static analysis challenges
- 🔧 Automating Skimmer Deobfuscation - Part 2: Automated deobfuscation techniques
- 🛡️ Defeating JavaScript Obfuscation - Part 3: The story of REstringer
Additional Resources:
- 🔐 Caesar Plus Deobfuscation - Deep dive into Caesar cipher obfuscation
- 💬 GitHub Issues - Bug reports and feature requests
- 🐦 Twitter @ctrl__esc - Updates and discussions
- 🌐 Online Tool - Try REstringer in your browser
This project is licensed under the MIT License.
Made with ❤️ by HUMAN Security