UNPKG

restringer

Version:

Deobfuscate Javascript with emphasis on reconstructing strings

60 lines (58 loc) 2.57 kB
import {parseCode} from 'flast'; import {badValue} from '../config.js'; import {Sandbox} from '../utils/sandbox.js'; import {evalInVm} from '../utils/evalInVm.js'; import {createOrderedSrc} from '../utils/createOrderedSrc.js'; import {getDeclarationWithContext} from '../utils/getDeclarationWithContext.js'; /** * Resolve eval call expressions where the argument isn't a literal. * E.g. * eval(function() {return 'atob'}()); // <-- will be resolved into 'atob' * @param {Arborist} arb * @param {Function} candidateFilter (optional) a filter to apply on the candidates list * @return {Arborist} */ function resolveEvalCallsOnNonLiterals(arb, candidateFilter = () => true) { let sharedSb; const relevantNodes = [ ...(arb.ast[0].typeMap.CallExpression || []), ]; for (let i = 0; i < relevantNodes.length; i++) { const n = relevantNodes[i]; if (n.callee.name === 'eval' && n.arguments.length === 1 && n.arguments[0].type !== 'Literal' && candidateFilter(n)) { // The code inside the eval might contain references to outside code that should be included. const contextNodes = getDeclarationWithContext(n, true); // In case any of the target candidate is included in the context it should be removed. const possiblyRedundantNodes = [n, n?.parentNode, n?.parentNode?.parentNode]; for (let i = 0; i < possiblyRedundantNodes.length; i++) { if (contextNodes.includes(possiblyRedundantNodes[i])) contextNodes.splice(contextNodes.indexOf(possiblyRedundantNodes[i]), 1); } const context = contextNodes.length ? createOrderedSrc(contextNodes) : ''; const src = `${context}\n;var __a_ = ${createOrderedSrc([n.arguments[0]])}\n;__a_`; sharedSb = sharedSb || new Sandbox(); const newNode = evalInVm(src, sharedSb); const targetNode = n.parentNode.type === 'ExpressionStatement' ? n.parentNode : n; let replacementNode = newNode; try { if (newNode.type === 'Literal') { try { replacementNode = parseCode(newNode.value); } catch { // Edge case for broken scripts that can be solved // by adding a newline after closing brackets except if part of a regexp replacementNode = parseCode(newNode.value.replace(/([)}])(?!\/)/g, '$1\n')); } finally { // If when parsed the newNode results in an empty program - use the unparsed newNode. if (!replacementNode.body.length) replacementNode = newNode; } } } catch {} if (replacementNode !== badValue) arb.markNode(targetNode, replacementNode); } } return arb; } export default resolveEvalCallsOnNonLiterals;