Improved integration between renameProperties and controlFlowFlattening options (#1060)

This commit is contained in:
Timofey Kachalov
2022-02-12 20:35:31 +03:00
committed by GitHub
parent 2095d47058
commit b99b6a46ee
18 changed files with 268 additions and 177 deletions
+25 -42
View File
@@ -1,55 +1,38 @@
'use strict';
import { StringArrayWrappersType } from '../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
(function () {
const JavaScriptObfuscator: any = require('../../index');
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
function foo () {
function bar() {
var string1 = 'string1';
var string2 = 'string2';
var string3 = 'string3';
var string4 = 'string4';
var string5 = 'string5';
var string6 = 'string6';
function bark () {
var string1 = 'string1';
var string2 = 'string2';
var string3 = 'string3';
var string4 = 'string4';
var string5 = 'string5';
var string6 = 'string6';
}
}
bar()
var obj = {
foo: 1
}
console.log(foo());
`,
{
identifierNamesGenerator: 'mangled',
compact: false,
controlFlowFlattening: false,
controlFlowFlatteningThreshold: 1,
simplify: false,
stringArrayRotate: false,
stringArray: true,
stringArrayIndexesType: [
'hexadecimal-number',
'hexadecimal-numeric-string'
],
stringArrayThreshold: 1,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 1,
rotateStringArray: true,
stringArrayWrappersType: StringArrayWrappersType.Function,
transformObjectKeys: false,
seed: 1
"compact": false,
"controlFlowFlattening": true,
"controlFlowFlatteningThreshold": 1,
"disableConsoleOutput": false,
"identifierNamesGenerator": "mangled",
"log": true,
"numbersToExpressions": true,
"renameProperties": true,
"renamePropertiesMode": "safe",
"simplify": false,
"stringArray": true,
"stringArrayCallsTransform": true,
"stringArrayIndexShift": true,
"stringArrayRotate": false,
"stringArrayShuffle": false,
"stringArrayWrappersCount": 5,
"stringArrayWrappersChainedCalls": true,
"stringArrayWrappersParametersMaxCount": 5,
"stringArrayWrappersType": "function",
"stringArrayThreshold": 0,
"transformObjectKeys": true,
"unicodeEscapeSequence": false,
"ignoreRequireImports": false
}
).getObfuscatedCode();
@@ -3,6 +3,7 @@ import { assert } from 'chai';
import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/presets/NoCustomNodes';
import { readFileAsString } from '../../../../../helpers/readFileAsString';
import { getRegExpMatch } from '../../../../../helpers/getRegExpMatch';
import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade';
@@ -10,34 +11,67 @@ describe('StringLiteralControlFlowReplacer', () => {
describe('replace', () => {
const variableMatch: string = '_0x([a-f0-9]){4,6}';
const controlFlowStorageStringLiteralRegExp: RegExp = new RegExp(
`var ${variableMatch} *= *\\{'\\w{5}' *: *'test'\\};`
);
const controlFlowStorageCallRegExp: RegExp = new RegExp(
`var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\];`
);
describe('Variant #1 - base behavior', () => {
let obfuscatedCode: string;
const controlFlowStorageStringLiteralRegExp: RegExp = new RegExp(
`var ${variableMatch} *= *\\{'\\w{5}' *: *'test'\\};`
);
const controlFlowStorageCallRegExp: RegExp = new RegExp(
`var ${variableMatch} *= *${variableMatch}\\['\\w{5}'\\];`
);
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/input-1.js');
let obfuscatedCode: string;
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/input-1.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
});
it('should add string literal node as property of control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageStringLiteralRegExp);
});
it('should replace string literal node with call to control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
});
});
it('should add string literal node as property of control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageStringLiteralRegExp);
});
describe('Variant #2 - same storage key for same string values', () => {
const storageKeyRegExp: RegExp = /'(\w{5})': 'value'/;
const expectedStorageCallsMatchesCount: number = 5;
it('should replace string literal node with call to control flow storage node', () => {
assert.match(obfuscatedCode, controlFlowStorageCallRegExp);
let storageCallsMatchesCount: number;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/same-storage-key-for-same-string-values.js');
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
compact: false,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
const storageKeyMatch = getRegExpMatch(obfuscatedCode, storageKeyRegExp);
const storageCallsRegExp = new RegExp(`${variableMatch}\\[\'${storageKeyMatch}\']`, 'g')
storageCallsMatchesCount = obfuscatedCode.match(storageCallsRegExp)?.length ?? 0;
});
it('should add string literal nodes with same values under same storage item', () => {
assert.equal(storageCallsMatchesCount, expectedStorageCallsMatchesCount);
});
});
});
});
@@ -0,0 +1,7 @@
(function () {
var string1 = 'value';
var string2 = 'value';
var string3 = 'value';
var string4 = 'value';
var string5 = 'value';
})();
@@ -411,6 +411,70 @@ describe('RenamePropertiesTransformer', () => {
assert.match(obfuscatedCode, propertyRegExp);
});
});
describe('Variant #10: integration with `controlFlowFlattening` option', () => {
const propertyRegExp: RegExp = new RegExp(
'const b *= *{ *' +
'\'\\w{5}\' *: *\'a\' *' +
'}; *' +
'const c *= *{' +
'\'a\': *0x1' +
'};' +
'c\\[b\\[\'\\w{5}\']];'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/control-flow-flattening-integration.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
renameProperties: true,
renamePropertiesMode: RenamePropertiesMode.Unsafe,
identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
}
).getObfuscatedCode();
});
it('Should correctly rename property when `controlFlowFlattening` option is enabled', () => {
assert.match(obfuscatedCode, propertyRegExp);
});
});
describe('Variant #11: integration with `transformObjectKeys` option', () => {
const propertyRegExp: RegExp = new RegExp(
'const b *= *{}; *' +
'b\\[\'a\'] *= *0x1;' +
'const foo *= *b;' +
'foo\\[\'a\'];'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/transform-object-keys-integration.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
renameProperties: true,
renamePropertiesMode: RenamePropertiesMode.Unsafe,
identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
transformObjectKeys: true
}
).getObfuscatedCode();
});
it('Should correctly rename property when `transformObjectKeys` option is enabled', () => {
assert.match(obfuscatedCode, propertyRegExp);
});
});
});
describe('Variant #3: Ignored literal node type', () => {
@@ -132,13 +132,14 @@ describe('StringArrayRotateFunctionTransformer', function () {
});
describe('Code evaluation', function () {
this.timeout(100000);
const samplesCount: number = 50;
const evaluationTimeout: number = 5000;
const samplesCount: number = 100;
this.timeout(samplesCount * evaluationTimeout);
let hasRuntimeErrors: boolean = false;
before(() => {
before(async() => {
const code: string = readFileAsString(__dirname + '/fixtures/code-evaluation.js');
const obfuscateFunc = () => {
@@ -186,7 +187,10 @@ describe('StringArrayRotateFunctionTransformer', function () {
for (let i = 0; i < samplesCount; i++) {
try {
const evaluationResult = eval(obfuscateFunc());
const evaluationResult = await evaluateInWorker(
obfuscateFunc(),
evaluationTimeout
);
if (evaluationResult !== 'fooooooo') {
hasRuntimeErrors = true;
@@ -205,7 +209,7 @@ describe('StringArrayRotateFunctionTransformer', function () {
});
describe('Prevent early successful comparison', () => {
const evaluationTimeout: number = 1000;
const evaluationTimeout: number = 1000;
const samplesCount: number = 100;
let numberNumericalExpressionAnalyzerAnalyzeStub: sinon.SinonStub;
@@ -86,6 +86,24 @@ describe('NodeMetadata', () => {
});
});
describe('propertyKeyToRenameNode', () => {
const expectedValue: boolean = true;
let node: ESTree.Identifier,
value: boolean | undefined;
before(() => {
node = NodeFactory.identifierNode('foo');
node.metadata = {};
node.metadata.propertyKeyToRenameNode = true;
value = NodeMetadata.isPropertyKeyToRenameNode(node);
});
it('should return metadata value', () => {
assert.equal(value, expectedValue);
});
});
describe('isStringArrayCallLiteralNode', () => {
const expectedValue: boolean = true;