mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Implemented more correct way to store AST from eval expressions that allows recursive eval expressions obfuscation
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -9,6 +9,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
|
||||
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
|
||||
|
||||
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
import { NodeGuards } from '../../node/NodeGuards';
|
||||
import { Nodes } from '../../node/Nodes';
|
||||
import { NodeUtils } from '../../node/NodeUtils';
|
||||
|
||||
@@ -30,8 +31,8 @@ export class AstToEvalCallExpressionTransformer extends AbstractNodeTransformer
|
||||
*/
|
||||
public getVisitor (): IVisitor {
|
||||
return {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
|
||||
if (parentNode && node.isEvalRoot) {
|
||||
leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
|
||||
if (parentNode && node.isEvalRoot && NodeGuards.isFunctionDeclarationNode(node)) {
|
||||
return this.transformNode(node, parentNode);
|
||||
}
|
||||
}
|
||||
@@ -39,12 +40,13 @@ export class AstToEvalCallExpressionTransformer extends AbstractNodeTransformer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ExpressionStatement} node
|
||||
* @param {FunctionDeclaration} functionDeclaration
|
||||
* @param {Node} parentNode
|
||||
* @returns {Node}
|
||||
*/
|
||||
public transformNode (node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
|
||||
const code: string = NodeUtils.convertStructureToCode([node]);
|
||||
public transformNode (functionDeclaration: ESTree.FunctionDeclaration, parentNode: ESTree.Node): ESTree.Node {
|
||||
const targetAst: ESTree.Statement[] = functionDeclaration.body.body;
|
||||
const code: string = NodeUtils.convertStructureToCode(targetAst);
|
||||
|
||||
return Nodes.getCallExpressionNode(
|
||||
Nodes.getIdentifierNode('eval'),
|
||||
|
||||
@@ -63,10 +63,14 @@ export class EvalCallExpressionToAstTransformer extends AbstractNodeTransformer
|
||||
|
||||
const code: string = callExpressionFirstArgument.value;
|
||||
const ast: TStatement[] = NodeUtils.convertCodeToStructure(code);
|
||||
const programNode: ESTree.Program = Nodes.getProgramNode(ast);
|
||||
const functionDeclaration: ESTree.FunctionDeclaration = Nodes.getFunctionDeclarationNode(
|
||||
'evalRoot',
|
||||
[],
|
||||
Nodes.getBlockStatementNode(<any>ast)
|
||||
);
|
||||
|
||||
programNode.isEvalRoot = true;
|
||||
functionDeclaration.isEvalRoot = true;
|
||||
|
||||
return programNode;
|
||||
return functionDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -7,19 +7,17 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
|
||||
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
|
||||
`
|
||||
(function(){
|
||||
function foo (bar) {
|
||||
eval('console.log(bar, \\'baz\\');');
|
||||
console.log(eval('bar; console.log(\\'bark\\')'));
|
||||
function foo (a, b) {
|
||||
return eval('var c = a + b; eval(\\'a + c\\');');
|
||||
|
||||
}
|
||||
|
||||
foo(1);
|
||||
console.log(foo(1, 2));
|
||||
})();
|
||||
`,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
compact: false,
|
||||
stringArray: true,
|
||||
stringArrayThreshold: 1
|
||||
compact: false
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
|
||||
|
||||
+87
-11
@@ -12,7 +12,7 @@ import { getRegExpMatch } from '../../../../helpers/getRegExpMatch';
|
||||
describe('AstToEvalCallExpressionTransformer', () => {
|
||||
describe('variant #1: identifier reference', () => {
|
||||
const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
|
||||
const variableReferenceIdentifierRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
|
||||
const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
|
||||
|
||||
let functionIdentifierName: string | null,
|
||||
obfuscatedCode: string,
|
||||
@@ -30,11 +30,11 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
obfuscatedCode = obfuscationResult.getObfuscatedCode();
|
||||
|
||||
functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
|
||||
variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, variableReferenceIdentifierRegExp);
|
||||
variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should obfuscate eval string', () => {
|
||||
assert.match(obfuscatedCode, variableReferenceIdentifierRegExp);
|
||||
assert.match(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should correctly transform function parameter inside eval expression', () => {
|
||||
@@ -44,7 +44,7 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
|
||||
describe('variant #2: call expression with identifier reference', () => {
|
||||
const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
|
||||
const variableReferenceIdentifierRegExp: RegExp = /eval *\('console\[\\'log\\']\((_0x(?:[a-f0-9]){4,6})\);'\);/;
|
||||
const evalExpressionRegExp: RegExp = /eval *\('console\[\\'log\\']\((_0x(?:[a-f0-9]){4,6})\);'\);/;
|
||||
|
||||
let functionIdentifierName: string | null,
|
||||
obfuscatedCode: string,
|
||||
@@ -62,11 +62,11 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
obfuscatedCode = obfuscationResult.getObfuscatedCode();
|
||||
|
||||
functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
|
||||
variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, variableReferenceIdentifierRegExp);
|
||||
variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should obfuscate eval string', () => {
|
||||
assert.match(obfuscatedCode, variableReferenceIdentifierRegExp);
|
||||
assert.match(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should correctly transform function parameter inside eval expression', () => {
|
||||
@@ -75,7 +75,7 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
});
|
||||
|
||||
describe('variant #3: multiple statements in eval', () => {
|
||||
const regExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}; *\\n_0x([a-f0-9]){4,6};'\);/;
|
||||
const regExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}; *_0x([a-f0-9]){4,6};'\);/;
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
@@ -127,7 +127,7 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
|
||||
describe('variant #5: eval expression as argument', () => {
|
||||
const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
|
||||
const variableReferenceIdentifierRegExp: RegExp = /console\['log']\(eval *\('(_0x(?:[a-f0-9]){4,6});'\)\);/;
|
||||
const evalExpressionRegExp: RegExp = /console\['log']\(eval *\('(_0x(?:[a-f0-9]){4,6});'\)\);/;
|
||||
|
||||
let functionIdentifierName: string | null,
|
||||
obfuscatedCode: string,
|
||||
@@ -145,11 +145,11 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
obfuscatedCode = obfuscationResult.getObfuscatedCode();
|
||||
|
||||
functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
|
||||
variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, variableReferenceIdentifierRegExp);
|
||||
variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should obfuscate eval string', () => {
|
||||
assert.match(obfuscatedCode, variableReferenceIdentifierRegExp);
|
||||
assert.match(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should correctly transform function parameter inside eval expression', () => {
|
||||
@@ -157,7 +157,83 @@ describe('AstToEvalCallExpressionTransformer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('variant #6: integration with control flow flattening', () => {
|
||||
describe('variant #6: nested eval expressions', () => {
|
||||
const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6}), *(_0x(?:[a-f0-9]){4,6})\)/;
|
||||
const evalExpressionMatch: string = `` +
|
||||
`eval *\\('` +
|
||||
`var *(_0x(?:[a-f0-9]){4,6}) *= *(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` +
|
||||
`eval\\(\\\\'` +
|
||||
`(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` +
|
||||
`\\\\'\\);` +
|
||||
`'\\);` +
|
||||
``;
|
||||
const evalExpressionRegExp: RegExp = new RegExp(evalExpressionMatch);
|
||||
const expectedEvaluationResult: number = 4;
|
||||
|
||||
let evaluationResult: number,
|
||||
functionIdentifierAName: string | null,
|
||||
functionIdentifierBName: string | null,
|
||||
obfuscatedCode: string,
|
||||
variableReferenceIdentifierAName1: string | null,
|
||||
variableReferenceIdentifierAName2: string | null,
|
||||
variableReferenceIdentifierBName: string | null,
|
||||
variableReferenceIdentifierCName1: string | null,
|
||||
variableReferenceIdentifierCName2: string | null;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/nested-eval-expressions.js');
|
||||
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET
|
||||
}
|
||||
);
|
||||
|
||||
obfuscatedCode = obfuscationResult.getObfuscatedCode();
|
||||
|
||||
functionIdentifierAName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 0);
|
||||
functionIdentifierBName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 1);
|
||||
|
||||
// parameter `a` reference inside parent eval expression
|
||||
variableReferenceIdentifierAName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 1);
|
||||
// parameter `a` reference inside nested eval expression
|
||||
variableReferenceIdentifierAName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 3);
|
||||
// parameter `b` reference inside parent eval expression
|
||||
variableReferenceIdentifierBName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 2);
|
||||
// variable declaration `c` inside parent eval expression
|
||||
variableReferenceIdentifierCName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 0);
|
||||
// variable `c` reference inside nested eval expression
|
||||
variableReferenceIdentifierCName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 4);
|
||||
|
||||
evaluationResult = eval(obfuscatedCode);
|
||||
});
|
||||
|
||||
it('should obfuscate eval string', () => {
|
||||
assert.match(obfuscatedCode, evalExpressionRegExp);
|
||||
});
|
||||
|
||||
it('should generate correct code', () => {
|
||||
assert.equal(evaluationResult, expectedEvaluationResult);
|
||||
});
|
||||
|
||||
it('match #1: should correctly transform function parameter `a` inside eval expression', () => {
|
||||
assert.equal(functionIdentifierAName, variableReferenceIdentifierAName1);
|
||||
});
|
||||
|
||||
it('match #2: should correctly transform function parameter `a` inside nested eval expression', () => {
|
||||
assert.equal(functionIdentifierAName, variableReferenceIdentifierAName2);
|
||||
});
|
||||
|
||||
it('match #3: should correctly transform function parameter `b` inside eval expression', () => {
|
||||
assert.equal(functionIdentifierBName, variableReferenceIdentifierBName);
|
||||
});
|
||||
|
||||
it('match #4: should correctly transform variable declaration and variable reference inside eval and nested eval expressions', () => {
|
||||
assert.equal(variableReferenceIdentifierCName1, variableReferenceIdentifierCName2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('variant #7: integration with control flow flattening', () => {
|
||||
const variableMatch: string = '_0x([a-f0-9]){4,6}';
|
||||
const controlFlowStorageNodeMatch: string = `` +
|
||||
`var *${variableMatch} *= *\\{` +
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
(function(){
|
||||
function foo (a, b) {
|
||||
return eval('var c = a + b; eval(\'a + c\');');
|
||||
}
|
||||
|
||||
return foo(1, 2);
|
||||
})();
|
||||
Reference in New Issue
Block a user