Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ jobs:
node-version: 14.x
- os: ubuntu-latest,
node-version: 16.x
- os: windows-latest,
node-version: 14.x
- os: ubuntu-latest,
node-version: 17.x
- os: windows-latest,
node-version: 16.x

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Change Log

v3.2.0
---
* **New options**: `stringArrayCallsTransform` and `stringArrayCallsTransformThreshold`
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/pull/1046

v3.1.0
---
* Added support of `es2022` features: class static block
Expand Down
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,12 @@ Following options are available for the JS Obfuscator:
splitStrings: false,
splitStringsChunkLength: 10,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.5,
stringArrayEncoding: [],
stringArrayIndexesType: [
'hexadecimal-number'
],
stringArrayEncoding: [],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
Expand Down Expand Up @@ -441,8 +443,10 @@ Following options are available for the JS Obfuscator:
--split-strings <boolean>
--split-strings-chunk-length <number>
--string-array <boolean>
--string-array-indexes-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
--string-array-calls-transform <boolean>
--string-array-calls-transform-threshold <number>
--string-array-encoding '<list>' (comma separated) [none, base64, rc4]
--string-array-indexes-type '<list>' (comma separated) [hexadecimal-number, hexadecimal-numeric-string]
--string-array-index-shift <boolean>
--string-array-rotate <boolean>
--string-array-shuffle <boolean>
Expand Down Expand Up @@ -1097,6 +1101,20 @@ Type: `boolean` Default: `true`

Removes string literals and place them in a special array. For instance, the string `"Hello World"` in `var m = "Hello World";` will be replaced with something like `var m = _0x12c456[0x1];`

### `stringArrayCallsTransform`
Type: `boolean` Default: `true`

##### :warning: [`stringArray`](#stringarray) option must be enabled

Enables the transformation of calls to the [`stringArray`](#stringarray). All arguments of these calls may be extracted to different object depending on [`stringArrayCallsTransformThreshold`](#stringarraycallstransformthreshold) value.

### `stringArrayCallsTransformThreshold`
Type: `number` Default: `0.5`

##### :warning: [`stringArray`](#stringarray) and [`stringArrayCallsTransformThreshold`](#stringarraycallstransformthreshold) options must be enabled

You can use this setting to adjust the probability (from 0 to 1) that calls to the string array will be transformed.

### `stringArrayEncoding`
Type: `string[]` Default: `[]`

Expand Down Expand Up @@ -1419,7 +1437,7 @@ Unicode escape sequence increases code size greatly and strings easily can be re
## Preset Options
### High obfuscation, low performance

Performance will 50-100% slower than without obfuscation
The performance will be much slower than without obfuscation

```javascript
{
Expand All @@ -1440,6 +1458,8 @@ Performance will 50-100% slower than without obfuscation
splitStrings: true,
splitStringsChunkLength: 5,
stringArray: true,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayEncoding: ['rc4'],
stringArrayIndexShift: true,
stringArrayRotate: true,
Expand All @@ -1456,7 +1476,7 @@ Performance will 50-100% slower than without obfuscation

### Medium obfuscation, optimal performance

Performance will 30-35% slower than without obfuscation
The performance will be slower than without obfuscation

```javascript
{
Expand All @@ -1477,6 +1497,8 @@ Performance will 30-35% slower than without obfuscation
splitStrings: true,
splitStringsChunkLength: 10,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.75,
stringArrayEncoding: ['base64'],
stringArrayIndexShift: true,
stringArrayRotate: true,
Expand All @@ -1493,7 +1515,7 @@ Performance will 30-35% slower than without obfuscation

### Low obfuscation, High performance

Performance will slightly slower than without obfuscation
The performance will be at a relatively normal level

```javascript
{
Expand All @@ -1511,6 +1533,7 @@ Performance will slightly slower than without obfuscation
simplify: true,
splitStrings: false,
stringArray: true,
stringArrayCallsTransform: false,
stringArrayEncoding: [],
stringArrayIndexShift: true,
stringArrayRotate: true,
Expand Down Expand Up @@ -1542,6 +1565,8 @@ Performance will slightly slower than without obfuscation
simplify: true,
splitStrings: false,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.5,
stringArrayEncoding: [],
stringArrayIndexShift: true,
stringArrayRotate: true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"test:devRuntimePerformance": "ts-node test/dev/dev-runtime-performance.ts",
"test:full": "yarn run test:dev && yarn run test:mocha-coverage && yarn run test:mocha-memory-performance",
"test:mocha": "mocha --require ts-node/register --require source-map-support/register test/index.spec.ts --exit",
"test:mocha-coverage": "nyc --reporter text-summary --no-clean yarn run test:mocha",
"test:mocha-coverage": "NODE_OPTIONS=--max-old-space-size=4096 nyc --reporter text-summary --no-clean yarn run test:mocha",
"test:mocha-coverage:report": "nyc report --reporter=lcov",
"test:mocha-memory-performance": "cross-env NODE_OPTIONS=--max-old-space-size=230 mocha --require ts-node/register test/performance-tests/JavaScriptObfuscatorMemory.spec.ts",
"test": "yarn run test:full",
Expand Down
7 changes: 3 additions & 4 deletions src/JavaScriptObfuscator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { CodeTransformer } from './enums/code-transformers/CodeTransformer';
import { CodeTransformationStage } from './enums/code-transformers/CodeTransformationStage';
import { LoggingMessage } from './enums/logger/LoggingMessage';
import { NodeTransformer } from './enums/node-transformers/NodeTransformer';
import { NodeTransformationStage } from './enums/node-transformers/NodeTransformationStage';
import { NodeTransformationStage } from './enums/node-transformers/NodeTransformationStage';
import { SourceMapSourcesMode } from './enums/source-map/SourceMapSourcesMode';

import { ecmaVersion } from './constants/EcmaVersion';
Expand Down Expand Up @@ -90,6 +90,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
NodeTransformer.ScopeIdentifiersTransformer,
NodeTransformer.ScopeThroughIdentifiersTransformer,
NodeTransformer.SplitStringTransformer,
NodeTransformer.StringArrayControlFlowTransformer,
NodeTransformer.StringArrayRotateFunctionTransformer,
NodeTransformer.StringArrayScopeCallsWrapperTransformer,
NodeTransformer.StringArrayTransformer,
Expand Down Expand Up @@ -220,9 +221,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.DeadCodeInjection);
}

if (this.options.controlFlowFlattening) {
astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.ControlFlowFlattening);
}
astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.ControlFlowFlattening);

if (this.options.renameProperties) {
astTree = this.runNodeTransformationStage(astTree, NodeTransformationStage.RenameProperties);
Expand Down
12 changes: 11 additions & 1 deletion src/cli/JavaScriptObfuscatorCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,19 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
)
.option(
'--string-array <boolean>',
'Disables gathering of all literal strings into an array and replacing every literal string with an array call',
'Enables gathering of all literal strings into an array and replacing every literal string with an array call',
BooleanSanitizer
)
.option(
'--string-array-calls-transform <boolean>',
'Enables the transformation of calls to the string array',
BooleanSanitizer
)
.option(
'--string-array-calls-transform-threshold <number>',
'The probability that that calls to the string array will be transformed',
parseFloat
)
.option(
'--string-array-encoding <list> (comma separated, without whitespaces)',
'Encodes each string in strings array using base64 or rc4 (this option can slow down your code speed). ' +
Expand Down
3 changes: 2 additions & 1 deletion src/container/ServiceIdentifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum ServiceIdentifiers {
ICodeTransformer = 'ICodeTransformer',
ICodeTransformerNamesGroupsBuilder = 'ICodeTransformerNamesGroupsBuilder',
ICodeTransformersRunner = 'ICodeTransformersRunner',
IControlFlowStorage = 'IControlFlowStorage',
ICryptUtils = 'ICryptUtils',
ICryptUtilsStringArray = 'ICryptUtilsStringArray',
ICustomCodeHelper = 'ICustomCodeHelper',
Expand Down Expand Up @@ -50,6 +51,7 @@ export enum ServiceIdentifiers {
IRandomGenerator = 'IRandomGenerator',
IRenamePropertiesReplacer = 'IRenamePropertiesReplacer',
IScopeIdentifiersTraverser = 'IScopeIdentifiersTraverser',
ISetUtils = 'ISetUtils',
ISourceCode = 'ISourceCode',
IScopeAnalyzer = 'IScopeAnalyzer',
IStringArrayIndexNode = 'IStringArrayIndexNode',
Expand All @@ -59,7 +61,6 @@ export enum ServiceIdentifiers {
IThroughIdentifierReplacer = 'IThroughIdentifierReplacer',
IVisitedLexicalScopeNodesStackStorage = 'IVisitedLexicalScopeNodesStackStorage',
Newable__ICustomNode = 'Newable<ICustomNode>',
Newable__TControlFlowStorage = 'Newable<TControlFlowStorage>',
TCustomNodeGroupStorage = 'TCustomNodeGroupStorage',
TInputOptions = 'TInputOptions'
}
10 changes: 5 additions & 5 deletions src/container/modules/custom-nodes/CustomNodesModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { StringArrayIndexNode } from '../../../enums/custom-nodes/string-array-i
import { StringArrayScopeCallsWrapperFunctionNode } from '../../../custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperFunctionNode';
import { StringArrayScopeCallsWrapperVariableNode } from '../../../custom-nodes/string-array-nodes/StringArrayScopeCallsWrapperVariableNode';
import { StringLiteralControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/StringLiteralControlFlowStorageCallNode';
import { StringLiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/StringLiteralNode';
import { LiteralNode } from '../../../custom-nodes/control-flow-flattening-nodes/LiteralNode';

export const customNodesModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
// control flow custom nodes
Expand Down Expand Up @@ -55,12 +55,12 @@ export const customNodesModule: interfaces.ContainerModule = new ContainerModule
.whenTargetNamed(ControlFlowCustomNode.ExpressionWithOperatorControlFlowStorageCallNode);

bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
.toConstructor(LogicalExpressionFunctionNode)
.whenTargetNamed(ControlFlowCustomNode.LogicalExpressionFunctionNode);
.toConstructor(LiteralNode)
.whenTargetNamed(ControlFlowCustomNode.LiteralNode);

bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
.toConstructor(StringLiteralNode)
.whenTargetNamed(ControlFlowCustomNode.StringLiteralNode);
.toConstructor(LogicalExpressionFunctionNode)
.whenTargetNamed(ControlFlowCustomNode.LogicalExpressionFunctionNode);

bind<interfaces.Newable<ICustomNode>>(ServiceIdentifiers.Newable__ICustomNode)
.toConstructor(StringLiteralControlFlowStorageCallNode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { BlockStatementControlFlowTransformer } from '../../../node-transformers
import { CallExpressionControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/CallExpressionControlFlowReplacer';
import { FunctionControlFlowTransformer } from '../../../node-transformers/control-flow-transformers/FunctionControlFlowTransformer';
import { LogicalExpressionControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/LogicalExpressionControlFlowReplacer';
import { StringArrayCallControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/StringArrayCallControlFlowReplacer';
import { StringArrayControlFlowTransformer } from '../../../node-transformers/control-flow-transformers/StringArrayControlFlowTransformer';
import { StringLiteralControlFlowReplacer } from '../../../node-transformers/control-flow-transformers/control-flow-replacers/StringLiteralControlFlowReplacer';

export const controlFlowTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
Expand All @@ -25,6 +27,10 @@ export const controlFlowTransformersModule: interfaces.ContainerModule = new Con
.to(FunctionControlFlowTransformer)
.whenTargetNamed(NodeTransformer.FunctionControlFlowTransformer);

bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
.to(StringArrayControlFlowTransformer)
.whenTargetNamed(NodeTransformer.StringArrayControlFlowTransformer);

// control flow replacers
bind<IControlFlowReplacer>(ServiceIdentifiers.IControlFlowReplacer)
.to(BinaryExpressionControlFlowReplacer)
Expand All @@ -38,6 +44,10 @@ export const controlFlowTransformersModule: interfaces.ContainerModule = new Con
.to(LogicalExpressionControlFlowReplacer)
.whenTargetNamed(ControlFlowReplacer.LogicalExpressionControlFlowReplacer);

bind<IControlFlowReplacer>(ServiceIdentifiers.IControlFlowReplacer)
.to(StringArrayCallControlFlowReplacer)
.whenTargetNamed(ControlFlowReplacer.StringArrayCallControlFlowReplacer);

bind<IControlFlowReplacer>(ServiceIdentifiers.IControlFlowReplacer)
.to(StringLiteralControlFlowReplacer)
.whenTargetNamed(ControlFlowReplacer.StringLiteralControlFlowReplacer);
Expand Down
57 changes: 27 additions & 30 deletions src/container/modules/storages/StoragesModule.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import { ContainerModule, interfaces } from 'inversify';
import { ServiceIdentifiers } from '../../ServiceIdentifiers';

import { TControlFlowStorage } from '../../../types/storages/TControlFlowStorage';
import { TConstructor } from '../../../types/TConstructor';
import { TControlFlowStorageFactory } from '../../../types/container/node-transformers/TControlFlowStorageFactory';
import {
TControlFlowStorageFactoryCreator
} from '../../../types/container/node-transformers/TControlFlowStorageFactoryCreator';
import { TCustomCodeHelperGroupStorage } from '../../../types/storages/TCustomCodeHelperGroupStorage';

import { IControlFlowStorage } from '../../../interfaces/storages/control-flow-transformers/IControlFlowStorage';
import { IGlobalIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IGlobalIdentifierNamesCacheStorage';
import { ILiteralNodesCacheStorage } from '../../../interfaces/storages/string-array-transformers/ILiteralNodesCacheStorage';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IPropertyIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IPropertyIdentifierNamesCacheStorage';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { IStringArrayScopeCallsWrappersDataStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayScopeCallsWrappersDataStorage';
import { IStringArrayStorage } from '../../../interfaces/storages/string-array-transformers/IStringArrayStorage';
import { IVisitedLexicalScopeNodesStackStorage } from '../../../interfaces/storages/string-array-transformers/IVisitedLexicalScopeNodesStackStorage';

import { ControlFlowStorage } from '../../../storages/custom-nodes/ControlFlowStorage';
import { ControlFlowStorage } from '../../../enums/storages/ControlFlowStorage';

import { CustomCodeHelperGroupStorage } from '../../../storages/custom-code-helpers/CustomCodeHelperGroupStorage';
import { FunctionControlFlowStorage } from '../../../storages/control-flow-transformers/FunctionControlFlowStorage';
import { GlobalIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/GlobalIdentifierNamesCacheStorage';
import { LiteralNodesCacheStorage } from '../../../storages/string-array-transformers/LiteralNodesCacheStorage';
import { PropertyIdentifierNamesCacheStorage } from '../../../storages/identifier-names-cache/PropertyIdentifierNamesCacheStorage';
import { StringArrayScopeCallsWrappersDataStorage } from '../../../storages/string-array-transformers/StringArrayScopeCallsWrappersDataStorage';
import {
StringControlFlowStorage
} from '../../../storages/control-flow-transformers/StringControlFlowStorage';
import { StringArrayStorage } from '../../../storages/string-array-transformers/StringArrayStorage';
import { VisitedLexicalScopeNodesStackStorage } from '../../../storages/string-array-transformers/VisitedLexicalScopeNodesStackStorage';

Expand All @@ -29,6 +35,10 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
.to(CustomCodeHelperGroupStorage)
.inSingletonScope();

bind<IControlFlowStorage>(ServiceIdentifiers.IControlFlowStorage)
.to(FunctionControlFlowStorage)
.whenTargetNamed(ControlFlowStorage.FunctionControlFlowStorage);

bind<IGlobalIdentifierNamesCacheStorage>(ServiceIdentifiers.IGlobalIdentifierNamesCacheStorage)
.to(GlobalIdentifierNamesCacheStorage)
.inSingletonScope();
Expand All @@ -49,34 +59,21 @@ export const storagesModule: interfaces.ContainerModule = new ContainerModule((b
.to(StringArrayScopeCallsWrappersDataStorage)
.inSingletonScope();

bind<IControlFlowStorage>(ServiceIdentifiers.IControlFlowStorage)
.to(StringControlFlowStorage)
.whenTargetNamed(ControlFlowStorage.StringControlFlowStorage);

bind<IVisitedLexicalScopeNodesStackStorage>(ServiceIdentifiers.IVisitedLexicalScopeNodesStackStorage)
.to(VisitedLexicalScopeNodesStackStorage)
.inSingletonScope();

bind<interfaces.Newable<TControlFlowStorage>>(ServiceIdentifiers.Newable__TControlFlowStorage)
.toConstructor(ControlFlowStorage);

// controlFlowStorage factory
bind<TControlFlowStorage>(ServiceIdentifiers.Factory__TControlFlowStorage)
.toFactory<TControlFlowStorage>((context: interfaces.Context) => {
return (): TControlFlowStorage => {
const constructor = context.container
.get<TConstructor<[IRandomGenerator, IOptions], TControlFlowStorage>>(
ServiceIdentifiers.Newable__TControlFlowStorage
);
const randomGenerator: IRandomGenerator = context.container
.get<IRandomGenerator>(ServiceIdentifiers.IRandomGenerator);
const options: IOptions = context.container
.get<IOptions>(ServiceIdentifiers.IOptions);

const storage: TControlFlowStorage = new constructor(
randomGenerator,
options
);

storage.initialize();

return storage;
};
});
bind<IControlFlowStorage>(ServiceIdentifiers.Factory__TControlFlowStorage)
.toFactory((context: interfaces.Context): TControlFlowStorageFactoryCreator =>
(controlFlowStorageName: ControlFlowStorage): TControlFlowStorageFactory => (): IControlFlowStorage =>
context.container.getNamed<IControlFlowStorage>(
ServiceIdentifiers.IControlFlowStorage,
controlFlowStorageName
)
);
});
Loading