UNPKG

restringer

Version:

Deobfuscate Javascript with emphasis on reconstructing strings

69 lines (67 loc) 3.19 kB
/** * Augmented Array Replacements * The obfuscated script uses a shuffled array, * requiring an IIFE to re-order it before the values can be extracted correctly. * E.g. * const a = ['hello', 'log']; * (function(arr, times) { * for (let i = 0; i < times; i++) { * a.push(a.shift()); * } * })(a, 1); * console[a[0]](a[1]); // If the array isn't un-shuffled, this will become `console['hello']('log');` which will throw an error. * // Once un-shuffled, it will work correctly - `console['log']('hello');` * This processor will un-shuffle the array by running the IIFE augmenting it, and replace the array with the un-shuffled version, * while removing the augmenting IIFE. */ import {config, unsafe, utils} from '../modules/index.js'; const {resolveFunctionToArray} = unsafe; const {badValue} = config; const {createOrderedSrc, evalInVm, getDeclarationWithContext} = utils.default; /** * Extract the array and the immediately-invoking function expression. * Run the IIFE and extract the new augmented array state. * Remove the IIFE and replace the array with its new state. * @param {Arborist} arb * @return {Arborist} */ function replaceArrayWithStaticAugmentedVersion(arb) { const relevantNodes = [ ...(arb.ast[0].typeMap.CallExpression || []), ]; for (let i = 0; i < relevantNodes.length; i++) { const n = relevantNodes[i]; if (n.callee.type === 'FunctionExpression' && n.arguments.length > 1 && n.arguments[0].type === 'Identifier' && n.arguments[1].type === 'Literal' && !Number.isNaN(parseInt(n.arguments[1].value))) { let targetNode = n; while (targetNode && (targetNode.type !== 'ExpressionStatement' && targetNode.parentNode.type !== 'SequenceExpression')) { targetNode = targetNode?.parentNode; } const relevantArrayIdentifier = n.arguments.find(n => n.type === 'Identifier'); const declKind = /function/i.test(relevantArrayIdentifier.declNode.parentNode.type) ? '' : 'var '; const ref = !declKind ? `${relevantArrayIdentifier.name}()` : relevantArrayIdentifier.name; // The context for this eval is the relevant array and the IIFE augmenting it (the candidate). const contextNodes = getDeclarationWithContext(n, true); const context = `${contextNodes.length ? createOrderedSrc(contextNodes) : ''}`; // By adding the name of the array after the context, the un-shuffled array is procured. const src = `${context};\n${createOrderedSrc([targetNode])}\n${ref};`; const replacementNode = evalInVm(src); // The new node will hold the un-shuffled array's assignment if (replacementNode !== badValue) { arb.markNode(targetNode || n); if (relevantArrayIdentifier.declNode.parentNode.type === 'FunctionDeclaration') { arb.markNode(relevantArrayIdentifier.declNode.parentNode.body, { type: 'BlockStatement', body: [{ type: 'ReturnStatement', argument: replacementNode, }], }); } else arb.markNode(relevantArrayIdentifier.declNode.parentNode.init, replacementNode); } } } return arb; } export const preprocessors = [replaceArrayWithStaticAugmentedVersion, resolveFunctionToArray.default]; export const postprocessors = [];