Fixed invalid code generation for rest arguments when controlFlowFlattening option is enabled

Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/887
This commit is contained in:
sanex
2021-02-28 18:14:11 +03:00
parent c0ac1239e2
commit dd328b3c22
11 changed files with 101 additions and 17 deletions
+4
View File
@@ -1,5 +1,9 @@
Change Log
v2.10.5
---
* Fixed invalid code generation for rest arguments when `controlFlowFlattening` option is enabled. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/887
v2.10.4
---
* Fixed invalid behaviour of `numbersToExpressions` option for float numbers. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/882
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "2.10.4",
"version": "2.10.5",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
@@ -15,6 +15,7 @@ import { initializable } from '../../decorators/Initializable';
import { AbstractCustomNode } from '../AbstractCustomNode';
import { NodeFactory } from '../../node/NodeFactory';
import { NodeUtils } from '../../node/NodeUtils';
import { NodeGuards } from '../../node/NodeGuards';
@injectable()
export class CallExpressionFunctionNode extends AbstractCustomNode {
@@ -57,11 +58,26 @@ export class CallExpressionFunctionNode extends AbstractCustomNode {
*/
protected getNodeStructure (): TStatement[] {
const calleeIdentifier: ESTree.Identifier = NodeFactory.identifierNode('callee');
const params: ESTree.Identifier[] = [];
const params: (ESTree.Identifier | ESTree.RestElement)[] = [];
const callArguments: (ESTree.Identifier | ESTree.SpreadElement)[] = [];
const argumentsLength: number = this.expressionArguments.length;
for (let i: number = 0; i < argumentsLength; i++) {
params.push(NodeFactory.identifierNode(`param${i + 1}`));
const argument: ESTree.Expression | ESTree.SpreadElement = this.expressionArguments[i];
const isSpreadCallArgument: boolean = NodeGuards.isSpreadElementNode(argument);
const baseIdentifierNode: ESTree.Identifier = NodeFactory.identifierNode(`param${i + 1}`);
params.push(
isSpreadCallArgument
? NodeFactory.restElementNode(baseIdentifierNode)
: baseIdentifierNode
);
callArguments.push(
isSpreadCallArgument
? NodeFactory.spreadElementNode(baseIdentifierNode)
: baseIdentifierNode
);
}
const structure: TStatement = NodeFactory.expressionStatementNode(
@@ -74,7 +90,7 @@ export class CallExpressionFunctionNode extends AbstractCustomNode {
NodeFactory.returnStatementNode(
NodeFactory.callExpressionNode(
calleeIdentifier,
params
callArguments
)
)
])
+25 -1
View File
@@ -317,7 +317,7 @@ export class NodeFactory {
* @returns {FunctionExpression}
*/
public static functionExpressionNode (
params: ESTree.Identifier[],
params: ESTree.Pattern[],
body: ESTree.BlockStatement
): ESTree.FunctionExpression {
return {
@@ -493,6 +493,18 @@ export class NodeFactory {
};
}
/**
* @param {Pattern} argument
* @returns {SpreadElement}
*/
public static restElementNode (argument: ESTree.Pattern): ESTree.RestElement {
return {
type: NodeType.RestElement,
argument,
metadata: { ignoredNode: false }
};
}
/**
* @param {Expression} argument
* @returns {ReturnStatement}
@@ -517,6 +529,18 @@ export class NodeFactory {
};
}
/**
* @param {Expression} argument
* @returns {SpreadElement}
*/
public static spreadElementNode (argument: ESTree.Expression): ESTree.SpreadElement {
return {
type: NodeType.SpreadElement,
argument,
metadata: { ignoredNode: false }
};
}
/**
* @param {Expression} discriminant
* @param {SwitchCase[]} cases
+9 -7
View File
@@ -7,17 +7,19 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
const epsilon = 500;
const number = 500000.0000001;
if(epsilon > 0 && number > 500000.0000000 && number < 500000.0000002) {
console.log("math works!");
};
(function () {
const log = console.log;
const first = 'foo';
const rest = ['bar', 'baz', 'bark'];
log(first, ...rest);
})();
`,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
compact: false,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
@@ -117,5 +117,37 @@ describe('CallExpressionControlFlowReplacer', function () {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #4 - rest call argument', () => {
const controlFlowStorageCallRegExp: RegExp = /_0x([a-f0-9]){4,6}\['\w{5}']\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\);/;
const controlFlowStorageNodeRegExp: RegExp = new RegExp(`` +
`'\\w{5}' *: *function *\\(_0x([a-f0-9]){4,6}, *_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\) *\\{` +
`return *_0x([a-f0-9]){4,6}\\(_0x([a-f0-9]){4,6}, *\.\.\._0x([a-f0-9]){4,6}\\);` +
`\\}` +
``);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/rest-call-argument.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
});
it('should replace call expression node with call to control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
});
it('should keep spread parameter and rest call argument inside control flow storage node function', () => {
assert.match(obfuscatedCode, controlFlowStorageNodeRegExp);
});
});
});
});
@@ -0,0 +1,6 @@
(function () {
const log = console.log;
const first = 'foo';
const rest = ['bar', 'baz', 'bark'];
log(first, ...rest);
})();
@@ -221,7 +221,7 @@ describe('FunctionControlFlowTransformer', function () {
});
});
describe('arrow function expression', () => {
describe('Variant #7 - arrow function expression', () => {
describe('Variant #1 - arrow function expression with body', () => {
const regexp: RegExp = new RegExp(rootControlFlowStorageNodeMatch);
@@ -269,7 +269,7 @@ describe('FunctionControlFlowTransformer', function () {
});
});
describe('prevailing kind of variables', () => {
describe('Variant #8 - prevailing kind of variables', () => {
describe('Variant #1 - `var` kind', () => {
const regexp: RegExp = new RegExp(`var ${variableMatch} *= *\\{`);