Skip to content

Commit

Permalink
Removed padding characters from all base64 encoded strings.
Browse files Browse the repository at this point in the history
Removed RegExp that trims padding characters from `base64` encoded strings from `atob` code helper to prevent mutation of `RegExp.$1` value during calls to the `stringArray`. Fixed #829
  • Loading branch information
sanex3339 committed Jan 1, 2021
1 parent bba709f commit 04be75e
Show file tree
Hide file tree
Showing 19 changed files with 244 additions and 162 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Change Log

v2.10.1
---
* Removed padding characters from all base64 encoded strings. Removed RegExp that trims padding characters from `base64` encoded strings from `atob` code helper to prevent mutation of `RegExp.$1` value during calls to the `stringArray`. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/829

v2.10.0
---
* Improved `rotateStringArray` option
Expand Down
2 changes: 1 addition & 1 deletion dist/index.browser.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.cli.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "2.10.0",
"version": "2.10.1",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
Expand Down Expand Up @@ -86,7 +86,7 @@
"ts-node": "9.1.1",
"typescript": "4.1.3",
"webpack": "5.11.1",
"webpack-cli": "4.3.0",
"webpack-cli": "4.3.1",
"webpack-node-externals": "2.5.2"
},
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion src/container/ServiceIdentifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export enum ServiceIdentifiers {
ICodeTransformerNamesGroupsBuilder = 'ICodeTransformerNamesGroupsBuilder',
ICodeTransformersRunner = 'ICodeTransformersRunner',
ICryptUtils = 'ICryptUtils',
ICryptUtilsSwappedAlphabet = 'ICryptUtilsSwappedAlphabet',
ICryptUtilsStringArray = 'ICryptUtilsStringArray',
ICustomCodeHelper = 'ICustomCodeHelper',
ICustomCodeHelperGroup = 'ICustomCodeHelperGroup',
IControlFlowReplacer = 'IControlFlowReplacer',
Expand Down
10 changes: 5 additions & 5 deletions src/container/modules/utils/UtilsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';

import { IArrayUtils } from '../../../interfaces/utils/IArrayUtils';
import { ICryptUtils } from '../../../interfaces/utils/ICryptUtils';
import { ICryptUtilsSwappedAlphabet } from '../../../interfaces/utils/ICryptUtilsSwappedAlphabet';
import { ICryptUtilsStringArray } from '../../../interfaces/utils/ICryptUtilsStringArray';
import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenceEncoder';
import { ILevelledTopologicalSorter } from '../../../interfaces/utils/ILevelledTopologicalSorter';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';

import { ArrayUtils } from '../../../utils/ArrayUtils';
import { CryptUtils } from '../../../utils/CryptUtils';
import { CryptUtilsSwappedAlphabet } from '../../../utils/CryptUtilsSwappedAlphabet';
import { CryptUtilsStringArray } from '../../../utils/CryptUtilsStringArray';
import { EscapeSequenceEncoder } from '../../../utils/EscapeSequenceEncoder';
import { LevelledTopologicalSorter } from '../../../utils/LevelledTopologicalSorter';
import { RandomGenerator } from '../../../utils/RandomGenerator';
Expand All @@ -31,9 +31,9 @@ export const utilsModule: interfaces.ContainerModule = new ContainerModule((bind
.to(CryptUtils)
.inSingletonScope();

// crypt utils with swapped alphabet
bind<ICryptUtilsSwappedAlphabet>(ServiceIdentifiers.ICryptUtilsSwappedAlphabet)
.to(CryptUtilsSwappedAlphabet)
// crypt utils for string array
bind<ICryptUtilsStringArray>(ServiceIdentifiers.ICryptUtilsStringArray)
.to(CryptUtilsStringArray)
.inSingletonScope();

// escape sequence encoder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { base64alphabetSwapped } from '../../../../constants/Base64AlphabetSwapped';

/**
* This atob logic completely ignores padding characters
*
* @returns {string}
*/
export function AtobTemplate (): string {
return `
var {atobFunctionName} = function (input) {
const chars = '${base64alphabetSwapped}';
const str = String(input).replace(/=+$/, '');
let output = '';
for (
let bc = 0, bs, buffer, idx = 0;
buffer = str.charAt(idx++);
buffer = input.charAt(idx++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
import { ICryptUtils } from './ICryptUtils';

export interface ICryptUtilsSwappedAlphabet extends ICryptUtils {}
export interface ICryptUtilsStringArray extends ICryptUtils {}
16 changes: 8 additions & 8 deletions src/storages/string-array-transformers/StringArrayStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TIdentifierNamesGeneratorFactory } from '../../types/container/generato
import { TStringArrayEncoding } from '../../types/options/TStringArrayEncoding';

import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
import { ICryptUtilsSwappedAlphabet } from '../../interfaces/utils/ICryptUtilsSwappedAlphabet';
import { ICryptUtilsStringArray } from '../../interfaces/utils/ICryptUtilsStringArray';
import { IEncodedValue } from '../../interfaces/IEncodedValue';
import { IIdentifierNamesGenerator } from '../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
import { IOptions } from '../../interfaces/options/IOptions';
Expand Down Expand Up @@ -60,9 +60,9 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc
private readonly arrayUtils: IArrayUtils;

/**
* @type {ICryptUtilsSwappedAlphabet}
* @type {ICryptUtilsStringArray}
*/
private readonly cryptUtilsSwappedAlphabet: ICryptUtilsSwappedAlphabet;
private readonly cryptUtilsStringArray: ICryptUtilsStringArray;

/**
* @type {IIdentifierNamesGenerator}
Expand Down Expand Up @@ -104,21 +104,21 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc
* @param {IArrayUtils} arrayUtils
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
* @param {ICryptUtilsSwappedAlphabet} cryptUtilsSwappedAlphabet
* @param {ICryptUtilsStringArray} cryptUtilsStringArray
*/
public constructor (
@inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
@inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils,
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions,
@inject(ServiceIdentifiers.ICryptUtilsSwappedAlphabet) cryptUtilsSwappedAlphabet: ICryptUtilsSwappedAlphabet
@inject(ServiceIdentifiers.ICryptUtilsStringArray) cryptUtilsStringArray: ICryptUtilsStringArray
) {
super(randomGenerator, options);

this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
this.arrayUtils = arrayUtils;
this.cryptUtilsSwappedAlphabet = cryptUtilsSwappedAlphabet;
this.cryptUtilsStringArray = cryptUtilsStringArray;

this.rc4Keys = this.randomGenerator.getRandomGenerator()
.n(
Expand Down Expand Up @@ -303,7 +303,7 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc
*/
case StringArrayEncoding.Rc4: {
const decodeKey: string = this.randomGenerator.getRandomGenerator().pickone(this.rc4Keys);
const encodedValue: string = this.cryptUtilsSwappedAlphabet.btoa(this.cryptUtilsSwappedAlphabet.rc4(value, decodeKey));
const encodedValue: string = this.cryptUtilsStringArray.btoa(this.cryptUtilsStringArray.rc4(value, decodeKey));

const encodedValueSources: string[] = this.rc4EncodedValuesSourcesCache.get(encodedValue) ?? [];
let encodedValueSourcesLength: number = encodedValueSources.length;
Expand All @@ -326,7 +326,7 @@ export class StringArrayStorage extends MapStorage <`${string}-${TStringArrayEnc

case StringArrayEncoding.Base64: {
const decodeKey: null = null;
const encodedValue: string = this.cryptUtilsSwappedAlphabet.btoa(value);
const encodedValue: string = this.cryptUtilsStringArray.btoa(value);

return { encodedValue, encoding, decodeKey };
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../container/ServiceIdentifiers';

import { ICryptUtilsSwappedAlphabet } from '../interfaces/utils/ICryptUtilsSwappedAlphabet';
import { ICryptUtilsStringArray } from '../interfaces/utils/ICryptUtilsStringArray';
import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';

import { base64alphabetSwapped } from '../constants/Base64AlphabetSwapped';

import { CryptUtils } from './CryptUtils';

@injectable()
export class CryptUtilsSwappedAlphabet extends CryptUtils implements ICryptUtilsSwappedAlphabet {
export class CryptUtilsStringArray extends CryptUtils implements ICryptUtilsStringArray {
/**
* @type {string}
*/
Expand All @@ -23,4 +23,16 @@ export class CryptUtilsSwappedAlphabet extends CryptUtils implements ICryptUtils
) {
super(randomGenerator);
}

/**
* Removes base64 encoded string without padding characters and with swapped alphabet
*
* @param {string} string
* @returns {string}
*/
public btoa (string: string): string {
const output = super.btoa(string);

return output.replace(/=+$/, '');
}
}
22 changes: 12 additions & 10 deletions test/dev/dev.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
'use strict';

import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
import { StringArrayEncoding } from '../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';

(function () {
const JavaScriptObfuscator: any = require('../../index');

let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
console.log('222');
console.log('1');
console.log('22');
console.log('333');
console.log('444');
console.log('555');
console.log('999');
console.log('888');
console.log('777');
console.log('666');
console.log('111');
console.log('4444');
console.log('55555');
console.log('666666');
console.log('7777777');
console.log('88888888');
console.log('999999999');
`,
{
...NO_ADDITIONAL_NODES_PRESET,
compact: false,
stringArray: true,
stringArrayThreshold: 1,
shuffleStringArray: true,
rotateStringArray: true
stringArrayEncoding: [
StringArrayEncoding.Rc4
]
}
).getObfuscatedCode();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { assert } from 'chai';

import { ServiceIdentifiers } from '../../../../../../src/container/ServiceIdentifiers';

import { ICryptUtilsSwappedAlphabet } from '../../../../../../src/interfaces/utils/ICryptUtilsSwappedAlphabet';
import { ICryptUtilsStringArray } from '../../../../../../src/interfaces/utils/ICryptUtilsStringArray';
import { IInversifyContainerFacade } from '../../../../../../src/interfaces/container/IInversifyContainerFacade';
import { IObfuscatedCode } from '../../../../../../src/interfaces/source-code/IObfuscatedCode';
import { IRandomGenerator } from '../../../../../../src/interfaces/utils/IRandomGenerator';
Expand All @@ -22,21 +22,22 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../../src/options/preset
import { InversifyContainerFacade } from '../../../../../../src/container/InversifyContainerFacade';
import { JavaScriptObfuscator } from '../../../../../../src/JavaScriptObfuscatorFacade';
import { readFileAsString } from '../../../../../helpers/readFileAsString';
import { swapLettersCase } from '../../../../../helpers/swapLettersCase';

describe('StringArrayCallsWrapperTemplate', () => {
const stringArrayName: string = 'stringArrayName';
const stringArrayCallsWrapperName: string = 'stringArrayCallsWrapperName';
const atobFunctionName: string = 'atob';

let cryptUtilsSwappedAlphabet: ICryptUtilsSwappedAlphabet,
let cryptUtilsSwappedAlphabet: ICryptUtilsStringArray,
randomGenerator: IRandomGenerator;

before(() => {
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();

inversifyContainerFacade.load('', '', {});
cryptUtilsSwappedAlphabet = inversifyContainerFacade
.get<ICryptUtilsSwappedAlphabet>(ServiceIdentifiers.ICryptUtilsSwappedAlphabet);
.get<ICryptUtilsStringArray>(ServiceIdentifiers.ICryptUtilsStringArray);
randomGenerator = inversifyContainerFacade
.get<IRandomGenerator>(ServiceIdentifiers.IRandomGenerator);
});
Expand Down Expand Up @@ -127,6 +128,50 @@ describe('StringArrayCallsWrapperTemplate', () => {
assert.deepEqual(decodedValue, expectedDecodedValue);
});
});

describe('Variant #3: no regexp inside atob template', () => {
const indexShiftAmount: number = 0;

const expectedRegExpTestValue: string = '12345';

let decodedValue: string;

before(() => {
const atobPolyfill = format(AtobTemplate(), {
atobFunctionName
});
const atobDecodeTemplate: string = format(
StringArrayBase64DecodeTemplate(randomGenerator),
{
atobPolyfill,
atobFunctionName,
selfDefendingCode: '',
stringArrayCallsWrapperName
}
);
const stringArrayCallsWrapperTemplate: string = format(StringArrayCallsWrapperTemplate(), {
decodeCodeHelperTemplate: atobDecodeTemplate,
indexShiftAmount,
stringArrayCallsWrapperName,
stringArrayName
});

decodedValue = Function(`
var ${stringArrayName} = ['${swapLettersCase('c3RyaQ==')}'];
${stringArrayCallsWrapperTemplate}
/(.+)/.test("12345");
${stringArrayCallsWrapperName}(0x0);
return RegExp.$1;
`)();
});

it('should correctly return RegExp.$1 match without mutation by atob template', () => {
assert.deepEqual(decodedValue, expectedRegExpTestValue);
});
});
});

describe('Variant #2: `rc4` encoding', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ describe('StringArrayTransformer', function () {
});

describe('Variant #7: base64 encoding', () => {
const stringArrayRegExp: RegExp = new RegExp(`^var _0x([a-f0-9]){4} *= *\\['${swapLettersCase('dGVzdA==')}'];`);
const stringArrayRegExp: RegExp = new RegExp(`^var _0x([a-f0-9]){4} *= *\\['${swapLettersCase('dGVzdA')}'];`);
const stringArrayCallRegExp: RegExp = /var test *= *_0x([a-f0-9]){4}\(0x0\);/;

let obfuscatedCode: string;
Expand Down Expand Up @@ -507,7 +507,7 @@ describe('StringArrayTransformer', function () {
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=='\];/;
const base64EncodingRegExp: RegExp = /^var _0x([a-f0-9]){4} *= *\['DgvZDa'\];/;

let noneEncodingMatchesCount: number = 0;
let base64EncodingMatchesCount: number = 0;
Expand Down
2 changes: 1 addition & 1 deletion test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import './unit-tests/storages/string-array-transformers/string-array/StringArray
import './unit-tests/storages/string-array-transformers/visited-lexical-scope-nodes-stack/VisitedLexicalScopeNodesStackStorage.spec';
import './unit-tests/utils/ArrayUtils.spec';
import './unit-tests/utils/CryptUtils.spec';
import './unit-tests/utils/CryptUtilsSwappedAlphabet.spec';
import './unit-tests/utils/CryptUtilsStringArray.spec';
import './unit-tests/utils/EscapeSequenceEncoder.spec';
import './unit-tests/utils/LevelledTopologicalSorter.spec';
import './unit-tests/utils/NumberUtils.spec';
Expand Down
32 changes: 23 additions & 9 deletions test/unit-tests/utils/CryptUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { IInversifyContainerFacade } from '../../../src/interfaces/container/IIn

import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';

import { swapLettersCase } from '../../helpers/swapLettersCase';

describe('CryptUtils', () => {
let cryptUtils: ICryptUtils;

Expand All @@ -22,16 +20,32 @@ describe('CryptUtils', () => {
});

describe('btoa', () => {
const expectedString: string = swapLettersCase('C3rYAw5N');
describe('Variant #1: basic', () => {
const expectedString: string = 'c3RyaW5n';

let string: string;
let string: string;

before(() => {
string = cryptUtils.btoa('string');
});
before(() => {
string = cryptUtils.btoa('string');
});

it('should create a base-64 encoded string from a given string', () => {
assert.equal(string, expectedString);
});
});

describe('Variant #2: padding characters', () => {
const expectedString: string = 'c3RyaQ==';

it('should create a base-64 encoded string from a given string', () => {
assert.equal(string, expectedString);
let string: string;

before(() => {
string = cryptUtils.btoa('stri');
});

it('should create a base-64 encoded string from a given string with padding characters', () => {
assert.equal(string, expectedString);
});
});
});

Expand Down
Loading

0 comments on commit 04be75e

Please sign in to comment.