### Install REstringer for Development Source: https://github.com/humansecurity/restringer/blob/main/README.md Clones the repository and installs development dependencies for contributing to REstringer. ```bash git clone https://github.com/HumanSecurity/restringer.git cd restringer npm install ``` -------------------------------- ### Install REstringer Globally (CLI) Source: https://github.com/humansecurity/restringer/blob/main/README.md Installs the REstringer command-line interface globally on your system. ```bash npm install -g restringer ``` -------------------------------- ### Clone and Set Up REstringer Project Source: https://github.com/humansecurity/restringer/blob/main/docs/CONTRIBUTING.md Follow these steps to clone the REstringer repository, navigate into the directory, and install project dependencies. ```bash git clone https://github.com/your-username/restringer.git cd restringer npm install ``` -------------------------------- ### Install REstringer Locally (Module) Source: https://github.com/humansecurity/restringer/blob/main/README.md Installs the REstringer package for use as a module in your Node.js projects. ```bash npm install restringer ``` -------------------------------- ### REstringer CLI Usage Source: https://context7.com/humansecurity/restringer/llms.txt Examples of how to use the REstringer command-line interface for deobfuscating JavaScript files. ```APIDOC ## CLI Usage The `restringer` CLI reads an obfuscated JavaScript file, deobfuscates it, and writes or prints the result. ```bash # Install globally npm install -g restringer # Basic: deobfuscate and print to stdout restringer obfuscated.js # Save to a specific output file restringer obfuscated.js -o clean.js # Auto-named output file (-deob.js) restringer obfuscated.js -o # Remove dead code after deobfuscation (uses unsafe removeDeadNodes) restringer obfuscated.js -c -o clean.js # Limit iterations (useful for large files or debugging) restringer obfuscated.js -m 10 -o clean.js # Verbose mode (debug output) restringer obfuscated.js -v -o clean.js # Quiet mode (suppress all logs, output only to file) restringer obfuscated.js -q -o clean.js ``` ``` -------------------------------- ### Function to Array Pattern Example Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Illustrates the transformation of code where an array is generated by a function call into a direct array assignment. ```javascript // Before: function getArray() { return ['a', 'b', 'c']; } const data = getArray(); console.log(data[0]); // Complex array access // After: function getArray() { return ['a', 'b', 'c']; } const data = ['a', 'b', 'c']; // Direct array assignment console.log('a'); // Resolved access ``` -------------------------------- ### Create Custom Deobfuscation Module Source: https://context7.com/humansecurity/restringer/llms.txt Define custom deobfuscation modules following the match/transform pattern. This example resolves calls to a specific XOR decoder function. ```javascript import { evalInVm } from 'restringer/src/modules/utils/evalInVm.js'; import { applyIteratively } from 'flast'; // Custom module: resolve calls to a specific XOR decoder function export function resolveXorCallsMatch(arb, candidateFilter = () => true) { const matches = []; const calls = arb.ast[0].typeMap.CallExpression || []; for (let i = 0; i < calls.length; i++) { const n = calls[i]; if (n.callee?.name === 'xorDecode' && n.arguments.length === 2 && n.arguments.every(a => a.type === 'Literal') && candidateFilter(n)) { matches.push(n); } } return matches; } export function resolveXorCallsTransform(arb, matches) { for (let i = 0; i < matches.length; i++) { const n = matches[i]; const src = `(function xorDecode(s, k) { return s.split('').map((c, i) => String.fromCharCode(c.charCodeAt(0) ^ k.charCodeAt(i % k.length))).join(''); })(${JSON.stringify(n.arguments[0].value)}, ${JSON.stringify(n.arguments[1].value)})`; const result = evalInVm(src); if (result !== evalInVm.BAD_VALUE) { arb.markNode(n, result); } } return arb; } export default function resolveXorCalls(arb, candidateFilter = () => true) { const matches = resolveXorCallsMatch(arb, candidateFilter); return resolveXorCallsTransform(arb, matches); } // Use in a pipeline const script = applyIteratively(obfuscatedCode, [resolveXorCalls]); ``` -------------------------------- ### Manually Apply Obfuscation Processors Source: https://context7.com/humansecurity/restringer/llms.txt Apply preprocessors and postprocessors iteratively to handle specific obfuscation patterns like array shuffling. This example uses the augmentedArrayProcessor. ```javascript const augmentedArrayProcessor = await import('./src/processors/augmentedArray.js'); let script = " const arr = ['a', 'b', 'c', 'd', 'e']; (function(a, n) { const fn = function() { while (--n) { a.push(a.shift()); } }; fn(); })(arr, 3); console.log(arr[0]); // Should be 'd' after 3 rotations "; // Apply preprocessors (resolves the array shuffle at parse time) script = applyIteratively(script, augmentedArrayProcessor.preprocessors); // Apply postprocessors (cleanup) script = applyIteratively(script, augmentedArrayProcessor.postprocessors); console.log(script); // const arr = ['d', 'e', 'a', 'b', 'c']; // console.log(arr[0]); // 'd' ``` -------------------------------- ### Augmented Array Transformation Example Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Demonstrates how an IIFE that manipulates an array by shifting and pushing elements is transformed into a pre-computed array. ```javascript // Before: const arr = [1, 2, 3, 4, 5]; (function(targetArray, shifts) { for (let i = 0; i < shifts; i++) { targetArray.push(targetArray.shift()); } })(arr, 2); // After: const arr = [3, 4, 5, 1, 2]; // Pre-computed result ``` -------------------------------- ### Integrate Custom Methods with REstringer Source: https://github.com/humansecurity/restringer/blob/main/README.md Replace or customize built-in methods of the REstringer class. This example demonstrates replacing 'resolveLocalCalls' with a custom implementation that limits processing. ```javascript 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(); ``` -------------------------------- ### REstringer Module Match/Transform Pattern Source: https://github.com/humansecurity/restringer/blob/main/docs/CONTRIBUTING.md Example demonstrating the match/transform pattern for REstringer modules. The `moduleNameMatch` function identifies target nodes, and `moduleNameTransform` modifies them. The default export orchestrates this process. ```javascript // Match function - identifies target nodes export function moduleNameMatch(arb, candidateFilter = () => true) { const matches = []; const candidates = arb.ast[0].typeMap.TargetNodeType .concat(arb.ast[0].typeMap.AnotherTargetNodeType); for (let i = 0; i < candidates.length; i++) { const node = candidates[i]; if (matchesCriteria(node) && candidateFilter(node)) { matches.push(node); } } return matches; } // Transform function - modifies matched nodes export function moduleNameTransform(arb, node) { // Apply transformations performTransformation(node); return arb; // Must explicitly return arb } // Main function - orchestrates match and transform export default function moduleName(arb, candidateFilter = () => true) { const matches = moduleNameMatch(arb, candidateFilter); for (let i = 0; i < matches.length; i++) { arb = moduleNameTransform(arb, matches[i]); // Capture returned arb } return arb; } ``` -------------------------------- ### Customize REstringer Deobfuscation Pipelines Source: https://context7.com/humansecurity/restringer/llms.txt Modify `safeMethods` and `unsafeMethods` arrays to customize the deobfuscation pipeline. This example filters `resolveLocalCalls` to target only 'decode' functions and restricts safe methods. ```javascript import { REstringer } from 'restringer'; import { safe, unsafe } from 'restringer'; const code = ` function decode(x) { return String.fromCharCode(x - 10); } var msg = decode(114) + decode(111) + decode(121); console.log(msg); `; const restringer = new REstringer(code); // Replace resolveLocalCalls with a filtered version targeting only 'decode' const originalResolveLocalCalls = restringer.unsafeMethods.find( m => m.name === 'resolveLocalCalls' ); const idx = restringer.unsafeMethods.indexOf(originalResolveLocalCalls); restringer.unsafeMethods[idx] = function resolveDecodeCalls(arb) { return originalResolveLocalCalls(arb, n => n.callee?.name === 'decode' ); }; // Restrict safe methods to only what's needed restringer.safeMethods = [ safe.normalizeComputed.default, safe.replaceIdentifierWithFixedAssignedValue.default, ]; restringer.deobfuscate(); console.log(restringer.script); // Output: console.log('h' + 'e' + 'l'); (or resolved further) ``` -------------------------------- ### Build Custom Deobfuscation Pipelines with applyIteratively Source: https://context7.com/humansecurity/restringer/llms.txt Use `applyIteratively` from `flast` to create lightweight, targeted deobfuscation pipelines without the full `REstringer` class. This example demonstrates a focused pipeline for evaluating expressions, inlining calls, normalizing computed properties, and removing redundant blocks. ```javascript import { applyIteratively } from 'flast'; import { safe, unsafe } from 'restringer'; const normalizeComputed = safe.normalizeComputed.default; const resolveDefiniteBinaryExpressions = unsafe.resolveDefiniteBinaryExpressions.default; const resolveLocalCalls = unsafe.resolveLocalCalls.default; const removeRedundantBlockStatements = safe.removeRedundantBlockStatements.default; let script = ` var a = 2 * 3 + 1; var b = "hel" + "lo"; console["log"](b); `; // Build a focused pipeline const pipeline = [ resolveDefiniteBinaryExpressions, // 2 * 3 + 1 → 7 resolveLocalCalls, // Inline simple local function calls normalizeComputed, // console['log'] → console.log removeRedundantBlockStatements, // Clean up unnecessary {} ]; script = applyIteratively(script, pipeline); console.log(script); // Output: // var a = 7; // var b = 'hello'; // console.log(b); ``` -------------------------------- ### Target Specific Nodes with Candidate Filters Source: https://github.com/humansecurity/restringer/blob/main/README.md Use candidate filters to target specific nodes during deobfuscation. This example shows how to resolve calls in the global scope or calls to specific function names. ```javascript 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 ]); ``` -------------------------------- ### JavaScript Obfuscator Anti-Debugging Example Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Illustrates anti-debugging code patterns found in JavaScript Obfuscator, such as infinite loop traps and execution freezing. The processed code replaces these mechanisms with bypass strings. ```javascript // Detects and neutralizes patterns like: // 'newState' -> triggers anti-debug check // 'removeCookie' -> triggers protection mechanism // Before processing: if (funcTest.toString().match(/function.*\{.*\}/)) { while(true) {} // Infinite loop trap } // After processing: // Protection mechanisms replaced with bypass strings ``` -------------------------------- ### Run REstringer Test Suites Source: https://github.com/humansecurity/restringer/blob/main/docs/CONTRIBUTING.md Commands to run the test suite. Use `npm test` for the full suite and `npm run test:quick` for faster development cycles. `npm run test:quick:watch` enables watch mode. ```bash # Full test suite with sample files npm test # Quick test suite (recommended for development) npm run test:quick # Watch mode during development (quick tests) npm run test:quick:watch ``` -------------------------------- ### Run Quick Test Suite Source: https://github.com/humansecurity/restringer/blob/main/README.md Execute a quick test suite without testing against samples. This is useful for rapid development cycles. ```bash npm run test:quick ``` -------------------------------- ### Run Full Test Suite Source: https://github.com/humansecurity/restringer/blob/main/README.md Execute the complete test suite, including tests against samples, to ensure comprehensive coverage. ```bash npm test ``` -------------------------------- ### Custom Processor Integration with REstringer Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Shows how to integrate custom processors with the REstringer class, including disabling automatic detection and manually applying pre/postprocessors. ```javascript import {REstringer} from 'restringer'; import {applyIteratively} from 'flast'; const restringer = new REstringer(code); // Apply specific processors only restringer.detectObfuscationType = false; // Manually apply processors const obfuscatorIoProcessor = await import('./processors/obfuscator.io.js'); // Apply preprocessors before main deobfuscation restringer.script = applyIteratively(restringer.script, obfuscatorIoProcessor.preprocessors); // Run main deobfuscation restringer.deobfuscate(); // Apply postprocessors after main deobfuscation restringer.script = applyIteratively(restringer.script, obfuscatorIoProcessor.postprocessors); ``` -------------------------------- ### Enable Debug Logging (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Shows how to enable debug logging for the 'flast' logger to gain detailed insights into processor execution, including node processing, transformations, and performance metrics. ```javascript import {logger} from 'flast'; // Enable detailed logging logger.setLogLevelDebug(); // Your processor will now show detailed information about: // - Nodes being processed // - Transformations applied // - Performance metrics ``` -------------------------------- ### Using Individual Processors with applyIteratively Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Demonstrates how to import and apply specific processors (preprocessors and postprocessors) to a code string using the `applyIteratively` function. ```javascript import {applyIteratively} from 'flast'; import Arborist from 'arborist'; // Import specific processor const targetProcessors = await import('./augmentedArray.js'); const code = "\nconst arr = [1, 2, 3];\n(function(a, n) { for(let i = 0; i < n; i++) a.push(a.shift()); })(arr, 1);\n"; // Processors export preprocessors and postprocessors arrays let script = code; script = applyIteratively(script, targetProcessors.preprocessors); script = applyIteratively(script, targetProcessors.postprocessors); console.log(script); // Output: const arr = [2, 3, 1]; (pre-computed) ``` -------------------------------- ### Testing Custom Processors with applyIteratively Source: https://github.com/humansecurity/restringer/blob/main/docs/CONTRIBUTING.md Demonstrates how to apply preprocessors and postprocessors iteratively using the `applyIteratively` function. Ensure preprocessors are applied before postprocessors. ```javascript import assert from 'node:assert'; import {describe, it} from 'node:test'; import {applyIteratively} from 'flast'; describe('Custom Processor Tests', async () => { const targetProcessors = await import('./customProcessor.js'); it('TP-1: Should transform basic pattern', () => { const code = `/* obfuscated pattern */`; const expected = `/* expected result */`; // Apply preprocessors let script = applyIteratively(code, targetProcessors.preprocessors); // Apply postprocessors script = applyIteratively(script, targetProcessors.postprocessors); assert.strictEqual(script, expected); }); it('TN-1: Should not transform invalid pattern', () => { const code = `/* non-matching pattern */`; const originalScript = code; // Apply preprocessors let script = applyIteratively(code, targetProcessors.preprocessors); // Apply postprocessors script = applyIteratively(script, targetProcessors.postprocessors); assert.strictEqual(script, originalScript); }); }); ``` -------------------------------- ### Create a Feature Branch Source: https://github.com/humansecurity/restringer/blob/main/docs/CONTRIBUTING.md After setting up the project, create a new branch for your feature development. ```bash git checkout -b feature-name ``` -------------------------------- ### Basic Test Structure for Processors (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Sets up a basic testing structure for custom processors using `node:test` and `node:assert`. It includes tests for transformations that should occur (TP) and those that should not (TN). ```javascript import assert from 'node:assert'; import {describe, it} from 'node:test'; import {applyIteratively} from 'flast'; describe('Custom Processor Tests', () => { const targetProcessors = await import('./customProcessor.js'); it('TP-1: Should transform basic pattern', () => { const code = `/* obfuscated pattern */`; const expected = `/* expected result */`; let script = applyIteratively(code, targetProcessors.preprocessors); script = applyIteratively(script, targetProcessors.postprocessors); assert.strictEqual(script, expected); }); it('TN-1: Should not transform invalid pattern', () => { const code = `/* non-matching pattern */`; const originalScript = code; let script = applyIteratively(code, targetProcessors.preprocessors); script = applyIteratively(script, targetProcessors.postprocessors); assert.strictEqual(script, originalScript); }); }); ``` -------------------------------- ### new REstringer(script, normalize?) Source: https://context7.com/humansecurity/restringer/llms.txt Constructor for the REstringer class. Initializes the deobfuscation process with the provided JavaScript code. ```APIDOC ## `new REstringer(script, normalize?)` The main class for programmatic deobfuscation. Accepts obfuscated JavaScript source as a string. The `normalize` flag (default `true`) runs code formatting after deobfuscation. ```javascript import { REstringer } from 'restringer'; const obfuscatedCode = ` var _0x1a2b = ['hello', 'world', 'log']; var _0x3c4d = function(_0x5e6f, _0x7a8b) { return _0x1a2b[_0x5e6f]; }; console[_0x3c4d(2)](_0x3c4d(0) + ' ' + _0x3c4d(1)); `; const restringer = new REstringer(obfuscatedCode); // Optionally skip normalization // const restringer = new REstringer(obfuscatedCode, false); console.log(`REstringer version: ${REstringer.__version__}`); const modified = restringer.deobfuscate(); if (modified) { console.log('Deobfuscation successful:'); console.log(restringer.script); // Output: console.log('hello' + ' ' + 'world'); // or: console.log('hello world'); } else { console.log('No changes made — code may already be clean.'); } ``` ``` -------------------------------- ### Basic Processor Template (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Provides a template for creating custom processors. Includes functions for matching nodes, transforming them, and a main processor function. ```javascript // Static patterns for performance const DETECTION_PATTERNS = { targetPattern: /your-regex-here/, // ... other patterns }; /** * Identifies nodes that match your obfuscation pattern */ export function customProcessorMatch(arb, candidateFilter = () => true) { const matches = []; const candidates = arb.ast[0].typeMap.CallExpression; // or other node type for (let i = 0; i < candidates.length; i++) { const node = candidates[i]; if (DETECTION_PATTERNS.targetPattern.exec(node) && candidateFilter(node)) { matches.push(node); } } return matches; } /** * Transforms a matched node */ export function customProcessorTransform(arb, node) { // Your transformation logic here // Example: replace node with resolved value if (canResolve(node)) { const resolvedValue = resolveNode(node); node.replace(createNewNode(resolvedValue)); } return arb; } /** * Main processor function */ export default function customProcessor(arb, candidateFilter = () => true) { const matches = customProcessorMatch(arb, candidateFilter); for (let i = 0; i < matches.length; i++) { arb = customProcessorTransform(arb, matches[i]); } return arb; } ``` -------------------------------- ### `Sandbox` Class Source: https://context7.com/humansecurity/restringer/llms.txt Provides an isolated V8 execution environment. It blocks dangerous APIs, enforces memory and timeout limits, and cleans non-deterministic globals before each run. ```APIDOC ## `Sandbox` Class ### Description Provides an isolated V8 execution environment using `isolated-vm`. Blocks dangerous APIs (`fetch`, `WebAssembly`, `WebSocket`, `XMLHttpRequest`, etc.), enforces a 128 MB memory limit and 1-second timeout, and deletes non-deterministic globals (`Math.random`, `Date`) before each run. ### Methods #### `run(code: string)` Executes JavaScript code within the sandbox. #### `isReference(value)` Checks if a value is a reference within the sandbox. ### Examples ```javascript import { utils } from 'restringer'; const Sandbox = utils.default.sandbox; const sb = new Sandbox(); // Load shared context (function definitions, variables) sb.run(` var lookup = { A: 65, B: 66, C: 67 }; function getCode(key) { return lookup[key]; } `); // Evaluate in that context const ref = sb.run('getCode("B")'); console.log(sb.isReference(ref)); // true const val = ref.copySync(); console.log(val); // 66 // Dangerous APIs are blocked try { sb.run('fetch("http://evil.com")'); } catch (e) { console.log('fetch is blocked:', e.message); } // Execution is time-limited try { sb.run('while(true){}'); } catch (e) { console.log('Timeout enforced:', e.message.includes('Script execution timed out')); } ``` ``` -------------------------------- ### Using `applyIteratively` for Custom Pipelines Source: https://context7.com/humansecurity/restringer/llms.txt Shows how to use the `applyIteratively` function directly to build lightweight, targeted deobfuscation pipelines without the full `REstringer` class. ```APIDOC ## `applyIteratively(script, modules, maxIterations?)` — Custom Pipelines The `flast` function `applyIteratively` powers REstringer's loop. Use it directly to build lightweight, targeted deobfuscation pipelines without the full `REstringer` class. ```javascript import { applyIteratively } from 'flast'; import { safe, unsafe } from 'restringer'; const normalizeComputed = safe.normalizeComputed.default; const resolveDefiniteBinaryExpressions = unsafe.resolveDefiniteBinaryExpressions.default; const resolveLocalCalls = unsafe.resolveLocalCalls.default; const removeRedundantBlockStatements = safe.removeRedundantBlockStatements.default; let script = ` var a = 2 * 3 + 1; var b = "hel" + "lo"; console["log"](b); `; // Build a focused pipeline const pipeline = [ resolveDefiniteBinaryExpressions, // 2 * 3 + 1 → 7 resolveLocalCalls, // Inline simple local function calls normalizeComputed, // console['log'] → console.log removeRedundantBlockStatements, // Clean up unnecessary {} ]; script = applyIteratively(script, pipeline); console.log(script); // Output: // var a = 7; // var b = 'hello'; // console.log(b); ``` ``` -------------------------------- ### Customizing REstringer Pipelines Source: https://context7.com/humansecurity/restringer/llms.txt Demonstrates how to customize the `safeMethods` and `unsafeMethods` arrays of the `REstringer` class to create a more targeted deobfuscation pipeline. ```APIDOC ## `restringer.safeMethods` and `restringer.unsafeMethods` Arrays of deobfuscation functions applied iteratively. `safeMethods` perform pure AST transformations; `unsafeMethods` use sandboxed `eval`. Both arrays can be modified to customize the deobfuscation pipeline. ```javascript import { REstringer } from 'restringer'; import { safe, unsafe } from 'restringer'; const code = ` function decode(x) { return String.fromCharCode(x - 10); } var msg = decode(114) + decode(111) + decode(121); console.log(msg); `; const restringer = new REstringer(code); // Replace resolveLocalCalls with a filtered version targeting only 'decode' const originalResolveLocalCalls = restringer.unsafeMethods.find( m => m.name === 'resolveLocalCalls' ); const idx = restringer.unsafeMethods.indexOf(originalResolveLocalCalls); restringer.unsafeMethods[idx] = function resolveDecodeCalls(arb) { return originalResolveLocalCalls(arb, n => n.callee?.name === 'decode' ); }; // Restrict safe methods to only what's needed restringer.safeMethods = [ safe.normalizeComputed.default, safe.replaceIdentifierWithFixedAssignedValue.default, ]; restringer.deobfuscate(); console.log(restringer.script); // Output: console.log('h' + 'e' + 'l'); (or resolved further) ``` ``` -------------------------------- ### REstringer Module: Basic Deobfuscation Source: https://github.com/humansecurity/restringer/blob/main/README.md Demonstrates basic deobfuscation using the REstringer module. It takes obfuscated code as a string, attempts to deobfuscate it, and logs the result. ```javascript import {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'); } ``` -------------------------------- ### Run Quick Tests in Watch Mode Source: https://github.com/humansecurity/restringer/blob/main/README.md Enable watch mode for development, which automatically reruns quick tests when file changes are detected. ```bash npm run test:quick:watch ``` -------------------------------- ### Create Isolated V8 Execution Environments with Sandbox Source: https://context7.com/humansecurity/restringer/llms.txt The `Sandbox` class provides an isolated V8 environment that blocks dangerous APIs, enforces memory and time limits, and neutralizes non-deterministic globals. Use `sb.run()` to load shared context or evaluate code. References returned by `sb.run()` can be copied using `.copySync()`. ```javascript import { utils } from 'restringer'; const Sandbox = utils.default.sandbox; const sb = new Sandbox(); // Load shared context (function definitions, variables) sb.run(' var lookup = { A: 65, B: 66, C: 67 }; function getCode(key) { return lookup[key]; } '); // Evaluate in that context const ref = sb.run('getCode("B")'); console.log(sb.isReference(ref)); // true const val = ref.copySync(); console.log(val); // 66 // Dangerous APIs are blocked try { sb.run('fetch("http://evil.com")'); } catch (e) { console.log('fetch is blocked:', e.message); } // Execution is time-limited try { sb.run('while(true){}'); } catch (e) { console.log('Timeout enforced:', e.message.includes('Script execution timed out')); } ``` -------------------------------- ### Create Custom Deobfuscation Pipeline Source: https://github.com/humansecurity/restringer/blob/main/README.md Import specific modules from 'restringer' and 'flast' to define a custom deobfuscation pipeline. Apply these modules iteratively to obfuscated code. ```javascript 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); ``` -------------------------------- ### Processor Mapping in Index.js Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Shows how different obfuscation types are mapped to their corresponding processor modules in the main index.js file. ```javascript export const processors = { 'obfuscator.io': await import('./obfuscator.io.js'), 'augmented_array_replacements': await import('./augmentedArray.js'), 'function_to_array_replacements': await import('./functionToArray.js'), 'caesar_plus': await import('./caesarp.js'), // ... other mappings }; ``` -------------------------------- ### Programmatic Deobfuscation with Configuration Source: https://context7.com/humansecurity/restringer/llms.txt Reads code from a file, configures REstringer options like max iterations and logging level, and then performs deobfuscation, optionally removing dead code. The result is saved to an output file. ```javascript import fs from 'node:fs'; import { REstringer } from 'restringer'; const code = fs.readFileSync('obfuscated.js', 'utf-8'); const restringer = new REstringer(code); // Configure before calling deobfuscate restringer.maxIterations.value = 20; // Limit iterations (default: 500) restringer.detectObfuscationType = true; // Auto-detect obfuscation (default: true) restringer.logger.setLogLevelDebug(); // Enable verbose logging // Pass clean=true to remove dead/unreachable code nodes afterward const wasModified = restringer.deobfuscate(true); if (wasModified) { fs.writeFileSync('output.js', restringer.script, 'utf-8'); console.log('Saved deobfuscated script.'); } else { console.log('Script unchanged.'); } ``` -------------------------------- ### Memory Management Practices (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Demonstrates memory management best practices in JavaScript, favoring traditional for loops for performance and using direct array access patterns like `slice()` and `concat()` for array operations. ```javascript // ✅ Traditional for loops for performance for (let i = 0; i < candidates.length; i++) { const node = candidates[i]; // process node } // ✅ Use direct array access patterns const elements = array.slice(); // Copy array const combined = array1.concat(array2); // Combine arrays ``` -------------------------------- ### Advanced Processor Features (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Demonstrates advanced processor capabilities, including VM evaluation for complex expressions and handling multiple transformation types based on node type. ```javascript import {evalInVm, createNewNode} from '../modules/utils/index.js'; export function advancedProcessorTransform(arb, node) { // Use VM evaluation for complex expressions const expression = extractExpression(node); const result = evalInVm(expression); if (result !== evalInVm.BAD_VALUE) { node.replace(result); } // Handle multiple transformation types switch (node.type) { case 'CallExpression': return handleCallExpression(arb, node); case 'MemberExpression': return handleMemberExpression(arb, node); default: return arb; } } function handleCallExpression(arb, node) { // Specific handling for call expressions const callee = node.callee; if (callee.type === 'Identifier' && isDecodingFunction(callee.name)) { const args = node.arguments.map(arg => arg.value); const decoded = performDecoding(callee.name, args); node.replace(createNewNode(decoded)); } return arb; } ``` -------------------------------- ### REstringer CLI: Verbose Output and Iteration Limit Source: https://github.com/humansecurity/restringer/blob/main/README.md Runs deobfuscation with verbose logging and sets a maximum number of deobfuscation iterations, saving to a file. ```bash restringer obfuscated.js -v -m 10 -o output.js ``` -------------------------------- ### restringer.deobfuscate(clean?) Source: https://context7.com/humansecurity/restringer/llms.txt Initiates the deobfuscation process. Orchestrates detection, pre/post processors, and the iterative module loop. Returns true if modifications were made. ```APIDOC ## `restringer.deobfuscate(clean?)` Entry point for deobfuscation. Orchestrates obfuscation type detection, pre/post processors, and the iterative safe+unsafe module loop. Returns `true` if the script was modified. ```javascript import fs from 'node:fs'; import { REstringer } from 'restringer'; const code = fs.readFileSync('obfuscated.js', 'utf-8'); const restringer = new REstringer(code); // Configure before calling deobfuscate restringer.maxIterations.value = 20; // Limit iterations (default: 500) restringer.detectObfuscationType = true; // Auto-detect obfuscation (default: true) restringer.logger.setLogLevelDebug(); // Enable verbose logging // Pass clean=true to remove dead/unreachable code nodes afterward const wasModified = restringer.deobfuscate(true); if (wasModified) { fs.writeFileSync('output.js', restringer.script, 'utf-8'); console.log('Saved deobfuscated script.'); } else { console.log('Script unchanged.'); } ``` ``` -------------------------------- ### Programmatic Deobfuscation with REstringer Class Source: https://context7.com/humansecurity/restringer/llms.txt Initializes the REstringer class with obfuscated code and performs deobfuscation. The 'normalize' flag controls code formatting after deobfuscation. ```javascript import { REstringer } from 'restringer'; const obfuscatedCode = ` var _0x1a2b = ['hello', 'world', 'log']; var _0x3c4d = function(_0x5e6f, _0x7a8b) { return _0x1a2b[_0x5e6f]; }; console[_0x3c4d(2)](_0x3c4d(0) + ' ' + _0x3c4d(1)); `; const restringer = new REstringer(obfuscatedCode); // Optionally skip normalization // const restringer = new REstringer(obfuscatedCode, false); console.log(`REstringer version: ${REstringer.__version__}`); const modified = restringer.deobfuscate(); if (modified) { console.log('Deobfuscation successful:'); console.log(restringer.script); // Output: console.log('hello' + ' ' + 'world'); // or: console.log('hello world'); } else { console.log('No changes made — code may already be clean.'); } ``` -------------------------------- ### Efficient Node Traversal (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Highlights efficient methods for accessing Abstract Syntax Tree (AST) nodes using `typeMap` for direct access, contrasting it with less efficient full AST traversal. ```javascript // ✅ Use typeMap for direct access const candidates = arb.ast[0].typeMap.CallExpression; // ❌ Don't traverse entire AST function badTraversal(arb) { traverse(arb.ast, { CallExpression(node) { /* inefficient */ } }); } ``` -------------------------------- ### Static Pattern Extraction (JavaScript) Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Illustrates the best practice of extracting regular expression patterns outside of loops or frequently called functions to improve performance. ```javascript // ✅ Extract patterns outside functions const STATIC_PATTERNS = { methodCall: /^(decode|decrypt|transform)$/, arrayPattern: /^\.*$/ }; // ❌ Don't create patterns in loops function badExample(arb) { for (let i = 0; i < nodes.length; i++) { if (/pattern/.test(nodes[i].value)) { // Recreated each iteration // ... } } } ``` -------------------------------- ### Basic Processor Template in JavaScript Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md A template for creating a custom processor, exporting preprocessors and postprocessors arrays. Logic can be contained within a single function or structured further. ```javascript function myProcessorLogic(arb, candidateFilter = () => true) { const candidates = arb.ast[0].typeMap.TargetNodeType .concat(arb.ast[0].typeMap.AnotherTargetNodeType); for (let i = 0 < candidates.length; i++) { const node = candidates[i]; if (matchesCriteria(node) && candidateFilter(node)) { // Apply transformation directly performTransformation(node); } } return arb; } // Processors export arrays of functions, not a default export export const preprocessors = [myProcessorLogic]; export const postprocessors = []; ``` -------------------------------- ### Processor with Custom Filtering Logic Source: https://github.com/humansecurity/restringer/blob/main/src/processors/README.md Illustrates how to create a custom processor that applies transformations only to specific nodes, in this case, arrays with more than 5 elements. ```javascript import {augmentedArrayMatch, augmentedArrayTransform} from './augmentedArray.js'; function customArrayProcessor(arb) { // Only process arrays with more than 5 elements const customFilter = (node) => { const arrayArg = node.arguments[0]; return arrayArg.declNode?.init?.elements?.length > 5; }; const matches = augmentedArrayMatch(arb, customFilter); for (let i = 0; i < matches.length; i++) { arb = augmentedArrayTransform(arb, matches[i]); } return arb; } ``` -------------------------------- ### REstringer CLI: Basic Deobfuscation Source: https://github.com/humansecurity/restringer/blob/main/README.md Performs basic deobfuscation on a JavaScript file and prints the result to standard output. ```bash restringer obfuscated.js ``` -------------------------------- ### Convert JavaScript Values to AST Nodes with createNewNode Source: https://context7.com/humansecurity/restringer/llms.txt Use `createNewNode` to convert JavaScript values into their AST node representations. It supports primitives, arrays, objects, RegExp, BigInt, and Symbols. Unsupported types will result in `BAD_VALUE`. Note the specific handling for `null` and negative zero. ```javascript import { utils } from 'restringer'; const { createNewNode } = utils.default; // Primitives console.log(createNewNode(42)); // { type: 'Literal', value: 42, raw: '42' } console.log(createNewNode('hello')); // { type: 'Literal', value: 'hello', raw: 'hello' } console.log(createNewNode(true)); // { type: 'Literal', value: true, raw: 'true' } // Special values console.log(createNewNode(undefined)); // { type: 'Identifier', name: 'undefined' } console.log(createNewNode(null)); // { type: 'Literal', raw: 'null' } console.log(createNewNode(-0)); // { type: 'UnaryExpression', operator: '-', argument: { type: 'Literal', value: 0 } } // Arrays and objects console.log(createNewNode([1, 'a'])); // { type: 'ArrayExpression', elements: [ {Literal 1}, {Literal 'a'} ] } console.log(createNewNode({ x: 1 })); // { type: 'ObjectExpression', properties: [ { type: 'Property', key: {Literal 'x'}, value: {Literal 1} } ] } // RegExp console.log(createNewNode(/abc/gi)); // { type: 'Literal', regex: { pattern: 'abc', flags: 'gi' } } ``` -------------------------------- ### Processor Development: Basic Processor Function Source: https://github.com/humansecurity/restringer/blob/main/docs/CONTRIBUTING.md A basic processor function demonstrating iteration, matching criteria, and applying transformations. Processors export arrays of functions. ```javascript // Processor function - can be written as a single function function myProcessorLogic(arb, candidateFilter = () => true) { const candidates = arb.ast[0].typeMap.TargetNodeType .concat(arb.ast[0].typeMap.AnotherTargetNodeType); for (let i = 0; i < candidates.length; i++) { const node = candidates[i]; if (matchesCriteria(node) && candidateFilter(node)) { // Apply transformation directly performTransformation(node); } } return arb; } // Processors export arrays of functions, not a default export export const preprocessors = [myProcessorLogic]; export const postprocessors = []; ``` -------------------------------- ### Unsafe Module: `resolveDefiniteBinaryExpressions` Source: https://context7.com/humansecurity/restringer/llms.txt Evaluates binary expressions composed entirely of literal values (e.g., `5 * 3`, `'a' + 'b'`) inside an isolated sandbox and replaces them with their computed results. ```APIDOC ## Unsafe Module: `resolveDefiniteBinaryExpressions` Evaluates binary expressions composed entirely of literal values (e.g., `5 * 3`, `'a' + 'b'`) inside an isolated sandbox and replaces them with their computed results. ```javascript import { applyIteratively } from 'flast'; import { unsafe } from 'restringer'; const resolveDefiniteBinaryExpressions = unsafe.resolveDefiniteBinaryExpressions.default; let script = ` var x = 0x41 + 0x01; // 66 var y = 100 - 37; // 63 var s = 'hel' + 'lo'; // 'hello' var b = true && false; // false var t = true ? 'yes' : 'no'; // 'yes' `; script = applyIteratively(script, [resolveDefiniteBinaryExpressions]); console.log(script); // Output: // var x = 66; // var y = 63; // var s = 'hello'; // var b = false; // var t = 'yes'; ``` ``` -------------------------------- ### `evalInVm(stringToEval, sandbox?)` Source: https://context7.com/humansecurity/restringer/llms.txt Safely evaluates a JavaScript code string within an isolated sandbox. It returns an AST node or `evalInVm.BAD_VALUE` on failure, with built-in caching and anti-debugging trap neutralization. ```APIDOC ## `evalInVm(stringToEval, sandbox?)` ### Description Low-level utility that safely evaluates a JavaScript code string inside the isolated sandbox, returning an AST node or `evalInVm.BAD_VALUE` on failure. Includes built-in caching and anti-debugging trap neutralization. ### Parameters #### Path Parameters - `stringToEval` (string) - Required - The JavaScript code string to evaluate. - `sandbox` (Sandbox) - Optional - An instance of the Sandbox class to use for evaluation. ### Returns - AST node or `evalInVm.BAD_VALUE` on failure. ### Examples ```javascript import { utils } from 'restringer'; const { evalInVm } = utils.default; // Simple arithmetic const result1 = evalInVm('5 + 3'); console.log(result1); // { type: 'Literal', value: 8, raw: '8' } // Array access const result2 = evalInVm('[1, 2, 3].join("-")'); console.log(result2); // { type: 'Literal', value: '1-2-3', raw: '"1-2-3"' } // Non-deterministic — returns BAD_VALUE const result3 = evalInVm('Math.random()'); console.log(result3 === evalInVm.BAD_VALUE); // true // Reuse a sandbox for multiple evaluations (performance optimization) import { Sandbox } from 'restringer'; const sb = new Sandbox(); sb.run('function dec(x) { return x - 10; }'); // Load context const r = evalInVm('dec(107)', sb); console.log(r); // { type: 'Literal', value: 97, raw: '97' } ``` ``` -------------------------------- ### `createNewNode(value)` Source: https://context7.com/humansecurity/restringer/llms.txt Converts a JavaScript value into its corresponding AST node representation. It handles various data types and returns `BAD_VALUE` for unsupported types. ```APIDOC ## `createNewNode(value)` ### Description Converts a JavaScript value into its corresponding AST node representation. Handles primitives, arrays, objects, RegExp, BigInt, Symbols, and functions. Returns `BAD_VALUE` for unsupported types. ### Parameters #### Path Parameters - `value` (any) - Required - The JavaScript value to convert. ### Returns - AST node representation of the value, or `BAD_VALUE` for unsupported types. ### Examples ```javascript import { utils } from 'restringer'; const { createNewNode } = utils.default; // Primitives console.log(createNewNode(42)); // { type: 'Literal', value: 42, raw: '42' } console.log(createNewNode('hello')); // { type: 'Literal', value: 'hello', raw: 'hello' } console.log(createNewNode(true)); // { type: 'Literal', value: true, raw: 'true' } // Special values console.log(createNewNode(undefined)); // { type: 'Identifier', name: 'undefined' } console.log(createNewNode(null)); // { type: 'Literal', raw: 'null' } console.log(createNewNode(-0)); // { type: 'UnaryExpression', operator: '-', argument: { type: 'Literal', value: 0 } } // Arrays and objects console.log(createNewNode([1, 'a'])); // { type: 'ArrayExpression', elements: [ {Literal 1}, {Literal 'a'} ] } console.log(createNewNode({ x: 1 })); // { type: 'ObjectExpression', properties: [ { type: 'Property', key: {Literal 'x'}, value: {Literal 1} } ] } // RegExp console.log(createNewNode(/abc/gi)); // { type: 'Literal', regex: { pattern: 'abc', flags: 'gi' } } ``` ```