Mixed string array encoding prototype (#727)

`stringArrayEncoding` option now accepts an array of encodings. Each string will be randomly encoded with passed encoding.
This commit is contained in:
Timofey Kachalov
2020-08-30 21:10:46 +03:00
committed by GitHub
parent 8c91a254be
commit 42ff66849e
39 changed files with 545 additions and 232 deletions
+8 -9
View File
@@ -7,20 +7,19 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
function func () {
var obj = {foo: 'bar'};
console.log(obj.foo);
}
var string1 = 'foo';
var string2 = 'bar';
var string3 = 'baz';
var string4 = 'bark';
func();
console.log(string1, string2, string3, string4);
`,
{
...NO_ADDITIONAL_NODES_PRESET,
compact: false,
transformObjectKeys: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1
stringArray: true,
stringArrayThreshold: 1,
stringArrayEncoding: []
}
).getObfuscatedCode();
@@ -72,7 +72,7 @@ describe('StringArrayCallsWrapperCodeHelper', () => {
identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
stringArray: true,
stringArrayThreshold: 1,
stringArrayEncoding: StringArrayEncoding.Base64
stringArrayEncoding: [StringArrayEncoding.Base64]
}
).getObfuscatedCode();
});
@@ -841,7 +841,10 @@ describe('JavaScriptObfuscator', () => {
renameProperties: true,
rotateStringArray: true,
stringArray: true,
stringArrayEncoding: StringArrayEncoding.Rc4,
stringArrayEncoding: [
StringArrayEncoding.Base64,
StringArrayEncoding.Rc4
],
stringArrayThreshold: 1,
transformObjectKeys: true,
unicodeEscapeSequence: false
@@ -932,7 +935,7 @@ describe('JavaScriptObfuscator', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
...baseParams,
stringArrayEncoding: StringArrayEncoding.Rc4
stringArrayEncoding: [StringArrayEncoding.Rc4]
}
).getObfuscatedCode();
@@ -955,7 +958,7 @@ describe('JavaScriptObfuscator', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
...baseParams,
stringArrayEncoding: StringArrayEncoding.Rc4
stringArrayEncoding: [StringArrayEncoding.Rc4]
}
).getObfuscatedCode();
@@ -977,7 +980,7 @@ describe('JavaScriptObfuscator', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
...baseParams,
stringArrayEncoding: StringArrayEncoding.Rc4
stringArrayEncoding: [StringArrayEncoding.Rc4]
}
).getObfuscatedCode();
});
@@ -1000,7 +1003,7 @@ describe('JavaScriptObfuscator', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
...baseParams,
stringArrayEncoding: StringArrayEncoding.Rc4
stringArrayEncoding: [StringArrayEncoding.Rc4]
}
).getObfuscatedCode();
@@ -1022,7 +1025,7 @@ describe('JavaScriptObfuscator', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
...baseParams,
stringArrayEncoding: StringArrayEncoding.Base64
stringArrayEncoding: [StringArrayEncoding.Base64]
}
).getObfuscatedCode();
@@ -11,7 +11,9 @@ import { swapLettersCase } from '../../../../helpers/swapLettersCase';
import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
describe('StringArrayTransformer', () => {
describe('StringArrayTransformer', function () {
this.timeout(60000);
describe('Variant #1: default behaviour', () => {
const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['test'\];/;
const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
@@ -199,7 +201,7 @@ describe('StringArrayTransformer', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayEncoding: StringArrayEncoding.Base64,
stringArrayEncoding: [StringArrayEncoding.Base64],
stringArrayThreshold: 1
}
).getObfuscatedCode();
@@ -228,7 +230,7 @@ describe('StringArrayTransformer', () => {
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayEncoding: StringArrayEncoding.Rc4,
stringArrayEncoding: [StringArrayEncoding.Rc4],
stringArrayThreshold: 1
}
).getObfuscatedCode();
@@ -257,7 +259,7 @@ describe('StringArrayTransformer', () => {
...NO_ADDITIONAL_NODES_PRESET,
seed: 1, // set seed to prevent rare case when all encoded values are the same
stringArray: true,
stringArrayEncoding: StringArrayEncoding.Rc4,
stringArrayEncoding: [StringArrayEncoding.Rc4],
stringArrayThreshold: 1
}
).getObfuscatedCode();
@@ -280,7 +282,175 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #10: `stringArrayThreshold` option value', () => {
describe('Variant #10: none and base64 encoding', () => {
describe('Variant #1: string array values', () => {
const samplesCount: number = 100;
const expectedMatchesChance: number = 0.5;
const expectedMatchesDelta: number = 0.15;
const noneEncodingRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['test'\];/;
const base64EncodingRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['DgvZDa=='\];/;
let noneEncodingMatchesCount: number = 0;
let base64EncodingMatchesCount: number = 0;
let obfuscatedCode: string;
let noneEncodingMatchesChance: number;
let base64EncodingMatchesChance: number;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
for (let i = 0; i < samplesCount; i++) {
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayEncoding: [
StringArrayEncoding.None,
StringArrayEncoding.Base64
],
stringArrayThreshold: 1
}
).getObfuscatedCode();
if (obfuscatedCode.match(noneEncodingRegExp)) {
noneEncodingMatchesCount = noneEncodingMatchesCount + 1;
}
if (obfuscatedCode.match(base64EncodingRegExp)) {
base64EncodingMatchesCount = base64EncodingMatchesCount + 1;
}
noneEncodingMatchesChance = noneEncodingMatchesCount / samplesCount;
base64EncodingMatchesChance = base64EncodingMatchesCount / samplesCount;
}
});
it('should add literal node value to the string array without encoding', () => {
assert.closeTo(noneEncodingMatchesChance, expectedMatchesChance, expectedMatchesDelta);
});
it('should add literal node value to the string array encoded using base64', () => {
assert.closeTo(base64EncodingMatchesChance, expectedMatchesChance, expectedMatchesDelta);
});
});
});
describe('Variant #11: none and rc4 encoding', () => {
describe('Variant #1: string array calls wrapper call', () => {
const samplesCount: number = 100;
const expectedMatchesChance: number = 0.5;
const expectedMatchesDelta: number = 0.15;
const noneEncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
const rc4EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0', *'.{4}'\);/;
let noneEncodingMatchesCount: number = 0;
let rc4EncodingMatchesCount: number = 0;
let obfuscatedCode: string;
let noneEncodingMatchesChance: number;
let rc4EncodingMatchesChance: number;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
for (let i = 0; i < samplesCount; i++) {
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayEncoding: [
StringArrayEncoding.None,
StringArrayEncoding.Rc4
],
stringArrayThreshold: 1
}
).getObfuscatedCode();
if (obfuscatedCode.match(noneEncodingRegExp)) {
noneEncodingMatchesCount = noneEncodingMatchesCount + 1;
}
if (obfuscatedCode.match(rc4EncodingRegExp)) {
rc4EncodingMatchesCount = rc4EncodingMatchesCount + 1;
}
noneEncodingMatchesChance = noneEncodingMatchesCount / samplesCount;
rc4EncodingMatchesChance = rc4EncodingMatchesCount / samplesCount;
}
});
it('should replace literal node value with value from string array without encoding', () => {
assert.closeTo(noneEncodingMatchesChance, expectedMatchesChance, expectedMatchesDelta);
});
it('should replace literal node value with value from string array encoded using rc4', () => {
assert.closeTo(rc4EncodingMatchesChance, expectedMatchesChance, expectedMatchesDelta);
});
});
});
describe('Variant #12: base64 and rc4 encoding', () => {
describe('Variant #1: single string literal', () => {
const samplesCount: number = 100;
const expectedMatchesChance: number = 0.5;
const expectedMatchesDelta: number = 0.15;
const base64EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0'\);/;
const rc4EncodingRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\('0x0', *'.{4}'\);/;
let base64EncodingMatchesCount: number = 0;
let rc4EncodingMatchesCount: number = 0;
let obfuscatedCode: string;
let base64EncodingMatchesChance: number;
let rc4EncodingMatchesChance: number;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
for (let i = 0; i < samplesCount; i++) {
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayEncoding: [
StringArrayEncoding.Base64,
StringArrayEncoding.Rc4
],
stringArrayThreshold: 1
}
).getObfuscatedCode();
if (obfuscatedCode.match(base64EncodingRegExp)) {
base64EncodingMatchesCount = base64EncodingMatchesCount + 1;
}
if (obfuscatedCode.match(rc4EncodingRegExp)) {
rc4EncodingMatchesCount = rc4EncodingMatchesCount + 1;
}
base64EncodingMatchesChance = base64EncodingMatchesCount / samplesCount;
rc4EncodingMatchesChance = rc4EncodingMatchesCount / samplesCount;
}
});
it('should replace literal node value with value from string array encoded using base64', () => {
assert.closeTo(base64EncodingMatchesChance, expectedMatchesChance, expectedMatchesDelta);
});
it('should replace literal node value with value from string array encoded using rc4', () => {
assert.closeTo(rc4EncodingMatchesChance, expectedMatchesChance, expectedMatchesDelta);
});
});
});
describe('Variant #13: `stringArrayThreshold` option value', () => {
const samples: number = 1000;
const stringArrayThreshold: number = 0.5;
const delta: number = 0.1;
@@ -323,7 +493,7 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #11: string array calls wrapper name', () => {
describe('Variant #14: string array calls wrapper name', () => {
const regExp: RegExp = /console\[b\('0x0'\)]\('a'\);/;
let obfuscatedCode: string;
@@ -347,7 +517,7 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #12: `reservedStrings` option is enabled', () => {
describe('Variant #15: `reservedStrings` option is enabled', () => {
describe('Variant #1: base `reservedStrings` values', () => {
describe('Variant #1: single reserved string value', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
@@ -519,7 +689,7 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #13: object expression key literal', () => {
describe('Variant #16: object expression key literal', () => {
describe('Variant #1: base key literal', () => {
const stringArrayRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['bar'];/;
const objectExpressionRegExp: RegExp = /var test *= *{'foo' *: *_0x([a-f0-9]){4}\('0x0'\)};/;
@@ -577,7 +747,7 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #14: import declaration source literal', () => {
describe('Variant #17: import declaration source literal', () => {
const importDeclarationRegExp: RegExp = /import *{ *bar *} *from *'foo';/;
let obfuscatedCode: string;
@@ -600,7 +770,7 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #15: export all declaration source literal', () => {
describe('Variant #18: export all declaration source literal', () => {
const exportAllDeclarationRegExp: RegExp = /export *\* *from *'foo';/;
let obfuscatedCode: string;
@@ -623,7 +793,7 @@ describe('StringArrayTransformer', () => {
});
});
describe('Variant #16: export named declaration source literal', () => {
describe('Variant #19: export named declaration source literal', () => {
const exportNamedDeclarationRegExp: RegExp = /export *{ *bar *} *from *'foo';/;
let obfuscatedCode: string;
@@ -543,7 +543,7 @@ describe('OptionsNormalizer', () => {
...getDefaultOptions(),
shuffleStringArray: true,
stringArray: false,
stringArrayEncoding: StringArrayEncoding.Rc4,
stringArrayEncoding: [StringArrayEncoding.Rc4],
stringArrayThreshold: 0.5,
rotateStringArray: true
});
@@ -552,7 +552,7 @@ describe('OptionsNormalizer', () => {
...getDefaultOptions(),
shuffleStringArray: false,
stringArray: false,
stringArrayEncoding: false,
stringArrayEncoding: [StringArrayEncoding.None],
stringArrayThreshold: 0,
rotateStringArray: false
};
@@ -567,12 +567,14 @@ describe('OptionsNormalizer', () => {
before(() => {
optionsPreset = getNormalizedOptions({
...getDefaultOptions(),
stringArrayEncoding: true
stringArrayEncoding: []
});
expectedOptionsPreset = {
...getDefaultOptions(),
stringArrayEncoding: StringArrayEncoding.Base64
stringArrayEncoding: [
StringArrayEncoding.None
]
};
});
@@ -2,6 +2,8 @@ import { assert } from 'chai';
import { readFileAsString } from '../helpers/readFileAsString';
import { StringArrayEncoding } from '../../src/enums/StringArrayEncoding';
import { JavaScriptObfuscator } from '../../src/JavaScriptObfuscatorFacade';
const heapValueToMB = (value: number) => Math.round(value / 1024 / 1024 * 100) / 100;
@@ -41,7 +43,7 @@ describe('JavaScriptObfuscator memory', function () {
splitStrings: true,
splitStringsChunkLength: 2,
stringArray: true,
stringArrayEncoding: 'base64',
stringArrayEncoding: [StringArrayEncoding.Base64],
stringArrayThreshold: 0.75,
transformObjectKeys: true,
unicodeEscapeSequence: false
@@ -34,7 +34,11 @@ describe('JavaScriptObfuscator runtime eval', function () {
splitStrings: true,
splitStringsChunkLength: 5,
stringArray: true,
stringArrayEncoding: StringArrayEncoding.Rc4,
stringArrayEncoding: [
StringArrayEncoding.None,
StringArrayEncoding.Base64,
StringArrayEncoding.Rc4
],
stringArrayThreshold: 1,
transformObjectKeys: true,
unicodeEscapeSequence: true
@@ -11,6 +11,8 @@ import { IInversifyContainerFacade } from '../../../../src/interfaces/container/
import { IStringArrayStorageAnalyzer } from '../../../../src/interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
import { IStringArrayStorageItemData } from '../../../../src/interfaces/storages/string-array-storage/IStringArrayStorageItem';
import { StringArrayEncoding } from '../../../../src/enums/StringArrayEncoding';
import { InversifyContainerFacade } from '../../../../src/container/InversifyContainerFacade';
import { NodeFactory } from '../../../../src/node/NodeFactory';
import { NodeMetadata } from '../../../../src/node/NodeMetadata';
@@ -34,12 +36,14 @@ describe('StringArrayStorageAnalyzer', () => {
const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
encodedValue: 'foo',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'foo'
};
const expectedStringArrayStorageItemData2: IStringArrayStorageItemData = {
encodedValue: 'bar',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 1,
value: 'bar'
@@ -85,6 +89,7 @@ describe('StringArrayStorageAnalyzer', () => {
const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
encodedValue: 'foo',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'foo'
@@ -124,6 +129,7 @@ describe('StringArrayStorageAnalyzer', () => {
const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
encodedValue: 'foo',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'foo'
@@ -163,6 +169,7 @@ describe('StringArrayStorageAnalyzer', () => {
const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
encodedValue: 'foo',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'foo'
@@ -213,6 +220,7 @@ describe('StringArrayStorageAnalyzer', () => {
const expectedStringArrayStorageItemData1: IStringArrayStorageItemData = {
encodedValue: 'foo',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'foo'
@@ -330,6 +338,7 @@ describe('StringArrayStorageAnalyzer', () => {
const expectedStringArrayStorageItemData1: undefined = undefined;
const expectedStringArrayStorageItemData2: IStringArrayStorageItemData = {
encodedValue: 'bar',
encoding: StringArrayEncoding.None,
decodeKey: null,
index: 0,
value: 'bar'
@@ -9,76 +9,62 @@ import { StringArrayEncodingSanitizer } from '../../../../src/cli/sanitizers/Str
describe('StringArrayEncodingSanitizer', () => {
describe('Variant #1: string array encoding `base64`', () => {
const inputValue: string = 'base64';
const expectedValue: TStringArrayEncoding = true;
const expectedValue: TStringArrayEncoding[] = [StringArrayEncoding.Base64];
let value: TStringArrayEncoding;
let value: TStringArrayEncoding[];
before(() => {
value = StringArrayEncodingSanitizer(inputValue);
});
it('should sanitize value', () => {
assert.equal(value, expectedValue);
assert.deepEqual(value, expectedValue);
});
});
describe('Variant #2: string array encoding `true`', () => {
const inputValue: string = 'true';
const expectedValue: TStringArrayEncoding = true;
describe('Variant #2: string array encoding `base64, rc4`', () => {
const inputValue: string = 'base64,rc4';
const expectedValue: TStringArrayEncoding[] = [
StringArrayEncoding.Base64,
StringArrayEncoding.Rc4
];
let value: TStringArrayEncoding;
let value: TStringArrayEncoding[];
before(() => {
value = StringArrayEncodingSanitizer(inputValue);
});
it('should sanitize value', () => {
assert.equal(value, expectedValue);
assert.deepEqual(value, expectedValue);
});
});
describe('Variant #3: string array encoding `1`', () => {
const inputValue: string = '1';
const expectedValue: TStringArrayEncoding = true;
let value: TStringArrayEncoding;
before(() => {
value = StringArrayEncodingSanitizer(inputValue);
});
it('should sanitize value', () => {
assert.equal(value, expectedValue);
});
});
describe('Variant #4: string array encoding `rc4`', () => {
const inputValue: string = 'rc4';
const expectedValue: TStringArrayEncoding = StringArrayEncoding.Rc4;
let value: TStringArrayEncoding;
before(() => {
value = StringArrayEncodingSanitizer(inputValue);
});
it('should sanitize value', () => {
assert.equal(value, expectedValue);
});
});
describe('Variant #5: string array encoding `foo`', () => {
describe('Variant #3: string array encoding `foo`', () => {
const inputValue: string = 'foo';
const expectedValue: TStringArrayEncoding = false;
let value: TStringArrayEncoding;
let testFunc: () => TStringArrayEncoding[];
before(() => {
value = StringArrayEncodingSanitizer(inputValue);
testFunc = () => StringArrayEncodingSanitizer(inputValue);
});
it('should sanitize value', () => {
assert.equal(value, expectedValue);
it('should throw an error for invalid encoding', () => {
assert.throws(testFunc, 'Invalid value');
});
});
describe('Variant #4: string array encoding `base64,foo`', () => {
const inputValue: string = 'base64,foo';
let testFunc: () => TStringArrayEncoding[];
before(() => {
testFunc = () => StringArrayEncodingSanitizer(inputValue);
});
it('should throw an error for invalid encoding', () => {
assert.throws(testFunc, 'Invalid value');
});
});
});
@@ -54,7 +54,7 @@ describe('StringArrayStorage', () => {
before(() => {
const stringArrayStorage: IStringArrayStorage = getStorageInstance({
stringArrayEncoding: StringArrayEncoding.Rc4
stringArrayEncoding: [StringArrayEncoding.Rc4]
});
for (let i = 0; i < samplesCount; i++) {