restringer
Version:
Deobfuscate Javascript with emphasis on reconstructing strings
51 lines (49 loc) • 2.02 kB
JavaScript
import {areReferencesModified} from '../utils/areReferencesModified.js';
import {getMainDeclaredObjectOfMemberExpression} from '../utils/getMainDeclaredObjectOfMemberExpression.js';
/**
* When an identifier holds a static value which is assigned after declaration but doesn't change afterwards,
* replace all references to it with the value.
* @param {Arborist} arb
* @param {Function} candidateFilter (optional) a filter to apply on the candidates list
* @return {Arborist}
*/
function replaceIdentifierWithFixedValueNotAssignedAtDeclaration(arb, candidateFilter = () => true) {
const relevantNodes = [
...(arb.ast[0].typeMap.Identifier || []),
];
for (let i = 0; i < relevantNodes.length; i++) {
const n = relevantNodes[i];
if (n.parentNode?.type === 'VariableDeclarator' &&
!n.parentNode.init &&
n.references?.filter(r =>
r.parentNode.type === 'AssignmentExpression' &&
getMainDeclaredObjectOfMemberExpression(r.parentNode.left) === r).length === 1 &&
!n.references.some(r =>
(/For.*Statement/.test(r.parentNode.type) &&
r.parentKey === 'left') ||
// This covers cases like:
// let a; b === c ? (b++, a = 1) : a = 2
[
r.parentNode.parentNode.type,
r.parentNode.parentNode?.parentNode?.type,
r.parentNode.parentNode?.parentNode?.parentNode?.type,
].includes('ConditionalExpression')) &&
candidateFilter(n)) {
const assignmentNode = n.references.find(r =>
r.parentNode.type === 'AssignmentExpression' &&
getMainDeclaredObjectOfMemberExpression(r.parentNode.left) === r);
const valueNode = assignmentNode.parentNode.right;
if (valueNode.type === 'Literal') {
const refs = n.references.filter(r => r !== assignmentNode);
if (!areReferencesModified(arb.ast, refs)) {
for (const ref of refs) {
if (ref.parentNode.type === 'CallExpression' && ref.parentKey === 'callee') continue;
arb.markNode(ref, valueNode);
}
}
}
}
}
return arb;
}
export default replaceIdentifierWithFixedValueNotAssignedAtDeclaration;