js-confuser 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +12 -29
- package/dist/compiler.js +2 -8
- package/dist/constants.js +17 -10
- package/dist/index.js +7 -30
- package/dist/obfuscator.js +15 -62
- package/dist/options.js +21 -38
- package/dist/order.js +4 -7
- package/dist/parser.js +5 -13
- package/dist/precedence.js +6 -8
- package/dist/presets.js +4 -6
- package/dist/probability.js +13 -24
- package/dist/templates/bufferToString.js +100 -5
- package/dist/templates/crash.js +51 -9
- package/dist/templates/es5.js +125 -6
- package/dist/templates/functionLength.js +24 -6
- package/dist/templates/globals.js +9 -0
- package/dist/templates/template.js +71 -30
- package/dist/transforms/antiTooling.js +26 -22
- package/dist/transforms/calculator.js +18 -54
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +236 -333
- package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +46 -25
- package/dist/transforms/deadCode.js +528 -27
- package/dist/transforms/dispatcher.js +110 -108
- package/dist/transforms/es5/antiClass.js +70 -44
- package/dist/transforms/es5/antiDestructuring.js +14 -38
- package/dist/transforms/es5/antiES6Object.js +39 -48
- package/dist/transforms/es5/antiSpreadOperator.js +5 -14
- package/dist/transforms/es5/antiTemplate.js +10 -19
- package/dist/transforms/es5/es5.js +7 -40
- package/dist/transforms/extraction/classExtraction.js +83 -0
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +45 -79
- package/dist/transforms/extraction/objectExtraction.js +27 -54
- package/dist/transforms/finalizer.js +6 -20
- package/dist/transforms/flatten.js +51 -99
- package/dist/transforms/identifier/globalAnalysis.js +8 -26
- package/dist/transforms/identifier/globalConcealing.js +35 -54
- package/dist/transforms/identifier/movedDeclarations.js +66 -38
- package/dist/transforms/identifier/renameVariables.js +29 -68
- package/dist/transforms/identifier/variableAnalysis.js +21 -48
- package/dist/transforms/lock/antiDebug.js +20 -25
- package/dist/transforms/lock/integrity.js +48 -47
- package/dist/transforms/lock/lock.js +62 -113
- package/dist/transforms/minify.js +77 -108
- package/dist/transforms/opaquePredicates.js +11 -48
- package/dist/transforms/preparation.js +17 -50
- package/dist/transforms/renameLabels.js +5 -22
- package/dist/transforms/rgf.js +98 -66
- package/dist/transforms/shuffle.js +41 -46
- package/dist/transforms/stack.js +35 -98
- package/dist/transforms/string/encoding.js +73 -27
- package/dist/transforms/string/stringCompression.js +44 -68
- package/dist/transforms/string/stringConcealing.js +125 -134
- package/dist/transforms/string/stringEncoding.js +6 -26
- package/dist/transforms/string/stringSplitting.js +5 -30
- package/dist/transforms/transform.js +50 -100
- package/dist/traverse.js +11 -18
- package/dist/util/compare.js +27 -29
- package/dist/util/gen.js +32 -86
- package/dist/util/guard.js +0 -1
- package/dist/util/identifiers.js +11 -74
- package/dist/util/insert.js +27 -77
- package/dist/util/math.js +0 -3
- package/dist/util/object.js +3 -7
- package/dist/util/random.js +5 -36
- package/dist/util/scope.js +6 -3
- package/docs/ControlFlowFlattening.md +1 -1
- package/docs/ES5.md +197 -0
- package/package.json +3 -3
- package/src/constants.ts +12 -0
- package/src/options.ts +13 -0
- package/src/order.ts +2 -2
- package/src/templates/bufferToString.ts +49 -11
- package/src/templates/functionLength.ts +21 -3
- package/src/templates/globals.ts +3 -0
- package/src/templates/template.ts +85 -25
- package/src/transforms/antiTooling.ts +33 -11
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +2 -2
- package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +46 -10
- package/src/transforms/deadCode.ts +0 -16
- package/src/transforms/dispatcher.ts +101 -69
- package/src/transforms/es5/antiClass.ts +10 -1
- package/src/transforms/extraction/classExtraction.ts +168 -0
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +13 -10
- package/src/transforms/extraction/objectExtraction.ts +7 -14
- package/src/transforms/flatten.ts +20 -5
- package/src/transforms/identifier/globalConcealing.ts +29 -65
- package/src/transforms/identifier/movedDeclarations.ts +90 -24
- package/src/transforms/minify.ts +27 -12
- package/src/transforms/rgf.ts +103 -5
- package/src/transforms/stack.ts +12 -3
- package/src/transforms/string/encoding.ts +85 -51
- package/src/transforms/string/stringCompression.ts +5 -8
- package/src/transforms/string/stringConcealing.ts +139 -113
- package/src/transforms/string/stringEncoding.ts +1 -2
- package/src/transforms/string/stringSplitting.ts +1 -2
- package/src/transforms/transform.ts +30 -1
- package/src/util/compare.ts +39 -5
- package/src/util/gen.ts +10 -3
- package/src/util/identifiers.ts +6 -3
- package/src/util/insert.ts +17 -0
- package/src/util/scope.ts +14 -2
- package/test/code/Cash.test.ts +10 -4
- package/test/code/StrictMode.src.js +65 -0
- package/test/code/StrictMode.test.js +37 -0
- package/test/compare.test.ts +62 -2
- package/test/options.test.ts +111 -55
- package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +37 -18
- package/test/transforms/dispatcher.test.ts +82 -0
- package/test/transforms/extraction/classExtraction.test.ts +86 -0
- package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +29 -8
- package/test/transforms/extraction/objectExtraction.test.ts +37 -15
- package/test/transforms/identifier/globalConcealing.test.ts +42 -2
- package/test/transforms/identifier/movedDeclarations.test.ts +61 -0
- package/test/transforms/lock/integrity.test.ts +24 -0
- package/test/transforms/minify.test.ts +37 -0
- package/test/transforms/rgf.test.ts +50 -0
- package/test/util/identifiers.test.ts +21 -0
- package/dist/transforms/controlFlowFlattening/choiceFlowObfuscation.js +0 -62
- package/dist/transforms/controlFlowFlattening/controlFlowObfuscation.js +0 -159
- package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +0 -106
- package/dist/transforms/eval.js +0 -84
- package/dist/transforms/hexadecimalNumbers.js +0 -63
- package/dist/transforms/hideInitializingCode.js +0 -270
- package/dist/transforms/identifier/nameRecycling.js +0 -218
- package/dist/transforms/label.js +0 -67
- package/dist/transforms/preparation/nameConflicts.js +0 -116
- package/dist/transforms/preparation/preparation.js +0 -188
package/docs/ES5.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
## `ES5`
|
|
2
|
+
|
|
3
|
+
The ES5 option converts most ES6+ features into ES5 compatible code.
|
|
4
|
+
|
|
5
|
+
Option name: `es5`
|
|
6
|
+
|
|
7
|
+
Option values: `true/false`
|
|
8
|
+
|
|
9
|
+
Note: Does not cover all cases such as Promises or Generator functions. Use [Babel](https://babel.dev/).
|
|
10
|
+
|
|
11
|
+
The ES5 option is intended to undo any ES6 feature the obfuscator adds to your code. If you input ES5 code, and enable the `es5` option, you can be guaranteed to have ES5 compatible output.
|
|
12
|
+
|
|
13
|
+
## Example
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// Input
|
|
17
|
+
function print(...messages){
|
|
18
|
+
console.log(...messages); // The spread operator (...)
|
|
19
|
+
// was introduced in ES6!
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
print("Hello", "World"); // "Hello World"
|
|
23
|
+
|
|
24
|
+
// Output
|
|
25
|
+
var __p_2580918143;
|
|
26
|
+
function print() {
|
|
27
|
+
var __p_7607361496;
|
|
28
|
+
var messages, __p_2591841272 = (__p_7607361496 = Array.prototype.slice.call(arguments), messages = __p_7607361496.slice(0));
|
|
29
|
+
(__p_2580918143 = console).log.apply(__p_2580918143, [].concat(Array.prototype.slice.call(messages)));
|
|
30
|
+
}
|
|
31
|
+
print('Hello', 'World'); // "Hello World"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Polyfill Array Methods
|
|
35
|
+
|
|
36
|
+
When the ES5 option is enabled, array method polyfills will be injected to the top of your script.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
if (!Array.prototype.forEach) {
|
|
40
|
+
Array.prototype.forEach = function forEach(callback, thisArg) {
|
|
41
|
+
if (typeof callback !== 'function') {
|
|
42
|
+
throw new TypeError(callback + ' is not a function');
|
|
43
|
+
}
|
|
44
|
+
var array = this;
|
|
45
|
+
thisArg = thisArg || this;
|
|
46
|
+
for (var i = 0, l = array.length; i !== l; ++i) {
|
|
47
|
+
callback.call(thisArg, array[i], i, array);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Destructuring
|
|
54
|
+
|
|
55
|
+
The ES5 option supports transpiling the destructuring patterns.
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
// Input
|
|
59
|
+
var {userName, email} = { userName: "John", email: "email@exampe.com" };
|
|
60
|
+
|
|
61
|
+
// Output
|
|
62
|
+
var __p_7467473759;
|
|
63
|
+
var userName, email, __p_4755992742 = (__p_7467473759 = {
|
|
64
|
+
userName: 'John',
|
|
65
|
+
email: 'email@exampe.com'
|
|
66
|
+
}, userName = __p_7467473759.userName, email = __p_7467473759.email);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Spread Operator
|
|
70
|
+
|
|
71
|
+
The ES5 option supports transpiling the spread operator.
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// Input
|
|
75
|
+
array.push(...objects);
|
|
76
|
+
|
|
77
|
+
// Output
|
|
78
|
+
var __p_6344935930;
|
|
79
|
+
(__p_6344935930 = array).push.apply(__p_6344935930, [].concat(Array.prototype.slice.call(objects)));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Template String
|
|
83
|
+
|
|
84
|
+
The ES5 option supports transpiling template strings.
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
// Input
|
|
88
|
+
var myString = `Hello ${userName}`;
|
|
89
|
+
|
|
90
|
+
// Output
|
|
91
|
+
var myString = 'Hello ' + (userName + '');
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Object getters/setters
|
|
95
|
+
|
|
96
|
+
The ES5 option supports transpiling getter and setter methods.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
// Input
|
|
100
|
+
var _name;
|
|
101
|
+
var myObject = {
|
|
102
|
+
get name(){
|
|
103
|
+
return _name;
|
|
104
|
+
},
|
|
105
|
+
set name(newName){
|
|
106
|
+
_name = newName;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Output
|
|
111
|
+
function __p_6886881506(base, computedProps, getters, setters) {
|
|
112
|
+
for (var i = 0; i < computedProps.length; i++) {
|
|
113
|
+
base[computedProps[i][0]] = computedProps[i][1];
|
|
114
|
+
}
|
|
115
|
+
var keys = Object.create(null);
|
|
116
|
+
Object.keys(getters).forEach(function (key) {
|
|
117
|
+
return keys[key] = 1;
|
|
118
|
+
});
|
|
119
|
+
Object.keys(setters).forEach(function (key) {
|
|
120
|
+
return keys[key] = 1;
|
|
121
|
+
});
|
|
122
|
+
Object.keys(keys).forEach(function (key) {
|
|
123
|
+
Object.defineProperty(base, key, {
|
|
124
|
+
set: setters[key],
|
|
125
|
+
get: getters[key],
|
|
126
|
+
configurable: true
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
return base;
|
|
130
|
+
}
|
|
131
|
+
var _name;
|
|
132
|
+
var myObject = __p_6886881506({}, [], {
|
|
133
|
+
'name': function () {
|
|
134
|
+
return _name;
|
|
135
|
+
}
|
|
136
|
+
}, {
|
|
137
|
+
'name': function (newName) {
|
|
138
|
+
_name = newName;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Arrow Functions
|
|
144
|
+
|
|
145
|
+
The ES5 option converts arrow functions into regular functions.
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
// Input
|
|
149
|
+
var print = message => console.log(message);
|
|
150
|
+
|
|
151
|
+
// Output
|
|
152
|
+
var print = function (message) {
|
|
153
|
+
return console.log(message);
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Const/Let
|
|
158
|
+
|
|
159
|
+
The ES5 option converts `const` and `let` to a regular `var` keyword.
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// Input
|
|
163
|
+
let myVar1 = true;
|
|
164
|
+
const myVar2 = "String";
|
|
165
|
+
|
|
166
|
+
// Output
|
|
167
|
+
var myVar1 = true;
|
|
168
|
+
var myVar2 = 'String';
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Classes
|
|
172
|
+
|
|
173
|
+
The ES5 option partially supports transpiling classes.
|
|
174
|
+
|
|
175
|
+
## Reserved Identifiers
|
|
176
|
+
|
|
177
|
+
The ES5 option will change any illegal uses of reserved identifiers.
|
|
178
|
+
|
|
179
|
+
```js
|
|
180
|
+
// Input
|
|
181
|
+
var myObject = {true: 1};
|
|
182
|
+
myObject.for = true;
|
|
183
|
+
|
|
184
|
+
// Output
|
|
185
|
+
var myObject = {"true": 1};
|
|
186
|
+
myObject["for"] = true;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Features not supported
|
|
190
|
+
|
|
191
|
+
- Promises
|
|
192
|
+
- Async / Await
|
|
193
|
+
- Generator functions
|
|
194
|
+
- Nullish coalescing
|
|
195
|
+
- Optional chaining
|
|
196
|
+
|
|
197
|
+
Use [Babel](https://babel.dev/) to transpile these features. JS-Confuser will only support features the obfuscator may potentially add to your code.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-confuser",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "JavaScript Obfuscation Tool.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"author": "MichaelXF",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"acorn": "^8.
|
|
26
|
-
"escodegen": "^2.
|
|
25
|
+
"acorn": "^8.12.1",
|
|
26
|
+
"escodegen": "^2.1.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@babel/cli": "^7.17.6",
|
package/src/constants.ts
CHANGED
|
@@ -82,3 +82,15 @@ export const reservedIdentifiers = new Set([
|
|
|
82
82
|
|
|
83
83
|
export const noRenameVariablePrefix = "__NO_JS_CONFUSER_RENAME__";
|
|
84
84
|
export const placeholderVariablePrefix = "__p_";
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Tells the obfuscator this function is predictable:
|
|
88
|
+
* - Never called with extraneous parameters
|
|
89
|
+
*/
|
|
90
|
+
export const predictableFunctionTag = "__JS_PREDICT__";
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Tells the obfuscator this function is critical for the Obfuscated code.
|
|
94
|
+
* - Example: string decryption function
|
|
95
|
+
*/
|
|
96
|
+
export const criticalFunctionTag = "__JS_CRITICAL__";
|
package/src/options.ts
CHANGED
|
@@ -585,6 +585,15 @@ export interface ObfuscateOptions {
|
|
|
585
585
|
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
586
586
|
*/
|
|
587
587
|
debugComments?: boolean;
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* ### `preserveFunctionLength`
|
|
591
|
+
*
|
|
592
|
+
* Modified functions will retain the correct `function.length` property. Enabled by default. (`true/false`)
|
|
593
|
+
*
|
|
594
|
+
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
595
|
+
*/
|
|
596
|
+
preserveFunctionLength?: boolean;
|
|
588
597
|
}
|
|
589
598
|
|
|
590
599
|
const validProperties = new Set([
|
|
@@ -619,6 +628,7 @@ const validProperties = new Set([
|
|
|
619
628
|
"verbose",
|
|
620
629
|
"globalVariables",
|
|
621
630
|
"debugComments",
|
|
631
|
+
"preserveFunctionLength",
|
|
622
632
|
]);
|
|
623
633
|
|
|
624
634
|
const validOses = new Set(["windows", "linux", "osx", "ios", "android"]);
|
|
@@ -764,6 +774,9 @@ export async function correctOptions(
|
|
|
764
774
|
if (!options.hasOwnProperty("renameGlobals")) {
|
|
765
775
|
options.renameGlobals = true; // RenameGlobals is on by default
|
|
766
776
|
}
|
|
777
|
+
if (!options.hasOwnProperty("preserveFunctionLength")) {
|
|
778
|
+
options.preserveFunctionLength = true; // preserveFunctionLength is on by default
|
|
779
|
+
}
|
|
767
780
|
|
|
768
781
|
if (options.globalVariables && !(options.globalVariables instanceof Set)) {
|
|
769
782
|
options.globalVariables = new Set(Object.keys(options.globalVariables));
|
package/src/order.ts
CHANGED
|
@@ -1,19 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
placeholderVariablePrefix,
|
|
3
|
+
predictableFunctionTag,
|
|
4
|
+
} from "../constants";
|
|
1
5
|
import Template from "./template";
|
|
2
6
|
|
|
3
|
-
export const
|
|
4
|
-
function
|
|
7
|
+
export const GetGlobalTemplate = Template(`
|
|
8
|
+
function ${placeholderVariablePrefix}CFG__getGlobalThis${predictableFunctionTag}(){
|
|
9
|
+
return globalThis
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function ${placeholderVariablePrefix}CFG__getGlobal${predictableFunctionTag}(){
|
|
13
|
+
return global
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function ${placeholderVariablePrefix}CFG__getWindow${predictableFunctionTag}(){
|
|
17
|
+
return window
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ${placeholderVariablePrefix}CFG__getThisFunction${predictableFunctionTag}(){
|
|
21
|
+
return new Function("return this")()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function {getGlobalFnName}(array = [
|
|
25
|
+
${placeholderVariablePrefix}CFG__getGlobalThis${predictableFunctionTag},
|
|
26
|
+
${placeholderVariablePrefix}CFG__getGlobal${predictableFunctionTag},
|
|
27
|
+
${placeholderVariablePrefix}CFG__getWindow${predictableFunctionTag},
|
|
28
|
+
${placeholderVariablePrefix}CFG__getThisFunction${predictableFunctionTag}
|
|
29
|
+
]){
|
|
30
|
+
var bestMatch
|
|
31
|
+
var itemsToSearch = []
|
|
5
32
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
33
|
+
bestMatch = Object
|
|
34
|
+
itemsToSearch["push"](("")["__proto__"]["constructor"]["name"])
|
|
35
|
+
} catch(e) {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
A: for(var i = 0; i < array["length"]; i++) {
|
|
8
39
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
40
|
+
bestMatch = array[i]()
|
|
41
|
+
for(var j = 0; j < itemsToSearch["length"]; j++) {
|
|
42
|
+
if(typeof bestMatch[itemsToSearch[j]] === "undefined") continue A;
|
|
43
|
+
}
|
|
44
|
+
return bestMatch
|
|
45
|
+
} catch(e) {}
|
|
13
46
|
}
|
|
47
|
+
|
|
48
|
+
return bestMatch || this;
|
|
14
49
|
}
|
|
50
|
+
`);
|
|
15
51
|
|
|
16
|
-
|
|
52
|
+
export const BufferToStringTemplate = Template(`
|
|
53
|
+
|
|
54
|
+
${GetGlobalTemplate.source}
|
|
55
|
+
|
|
56
|
+
var __globalObject = {getGlobalFnName}() || {};
|
|
17
57
|
var __TextDecoder = __globalObject["TextDecoder"];
|
|
18
58
|
var __Uint8Array = __globalObject["Uint8Array"];
|
|
19
59
|
var __Buffer = __globalObject["Buffer"];
|
|
@@ -63,6 +103,4 @@ export const BufferToStringTemplate = Template(`
|
|
|
63
103
|
return utf8ArrayToStr(buffer);
|
|
64
104
|
}
|
|
65
105
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
106
|
`);
|
|
@@ -3,12 +3,30 @@ import Template from "./template";
|
|
|
3
3
|
/**
|
|
4
4
|
* Helper function to set `function.length` property.
|
|
5
5
|
*/
|
|
6
|
-
export const FunctionLengthTemplate = Template(
|
|
6
|
+
export const FunctionLengthTemplate = Template(
|
|
7
|
+
`
|
|
7
8
|
function {name}(functionObject, functionLength){
|
|
8
|
-
|
|
9
|
+
{ObjectDefineProperty}(functionObject, "length", {
|
|
9
10
|
"value": functionLength,
|
|
10
11
|
"configurable": true
|
|
11
12
|
});
|
|
12
13
|
return functionObject;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
`,
|
|
16
|
+
`
|
|
17
|
+
function {name}(functionObject, functionLength){
|
|
18
|
+
return {ObjectDefineProperty}(functionObject, "length", {
|
|
19
|
+
"value": functionLength,
|
|
20
|
+
"configurable": true
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
`,
|
|
24
|
+
`
|
|
25
|
+
function {name}(functionObject, functionLength){
|
|
26
|
+
return {ObjectDefineProperty}["call"](null, functionObject, "length", {
|
|
27
|
+
"value": functionLength,
|
|
28
|
+
"configurable": true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
`
|
|
32
|
+
);
|
|
@@ -1,57 +1,103 @@
|
|
|
1
1
|
import { Node } from "../util/gen";
|
|
2
2
|
import { parseSnippet, parseSync } from "../parser";
|
|
3
|
+
import { ok } from "assert";
|
|
4
|
+
import { choice } from "../util/random";
|
|
5
|
+
import { placeholderVariablePrefix } from "../constants";
|
|
3
6
|
|
|
4
7
|
export interface ITemplate {
|
|
5
|
-
fill(variables?: { [name: string]: string | number }):
|
|
8
|
+
fill(variables?: { [name: string]: string | number }): {
|
|
9
|
+
output: string;
|
|
10
|
+
template: string;
|
|
11
|
+
};
|
|
6
12
|
|
|
7
13
|
compile(variables?: { [name: string]: string | number }): Node[];
|
|
8
14
|
|
|
9
15
|
single(variables?: { [name: string]: string | number }): Node;
|
|
10
16
|
|
|
17
|
+
variables(variables): ITemplate;
|
|
18
|
+
|
|
19
|
+
ignoreMissingVariables(): ITemplate;
|
|
20
|
+
|
|
21
|
+
templates: string[];
|
|
11
22
|
source: string;
|
|
12
23
|
}
|
|
13
24
|
|
|
14
|
-
export default function Template(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
export default function Template(...templates: string[]): ITemplate {
|
|
26
|
+
ok(templates.length);
|
|
27
|
+
|
|
28
|
+
var requiredVariables = new Set<string>();
|
|
29
|
+
var providedVariables = {};
|
|
30
|
+
var defaultVariables: { [key: string]: string } = Object.create(null);
|
|
31
|
+
|
|
32
|
+
// This may picked up "$mb[pP`x]" from String Encoding function
|
|
33
|
+
// ignoreMissingVariables() prevents this
|
|
34
|
+
var matches = templates[0].match(/{[$A-z0-9_]+}/g);
|
|
35
|
+
if (matches !== null) {
|
|
36
|
+
matches.forEach((variable) => {
|
|
37
|
+
var name = variable.slice(1, -1);
|
|
38
|
+
|
|
39
|
+
// $ variables are for default variables
|
|
40
|
+
if (name.startsWith("$")) {
|
|
41
|
+
defaultVariables[name] =
|
|
42
|
+
placeholderVariablePrefix +
|
|
43
|
+
"td_" +
|
|
44
|
+
Object.keys(defaultVariables).length;
|
|
45
|
+
} else {
|
|
46
|
+
requiredVariables.add(name);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
18
49
|
}
|
|
19
|
-
var vars = Object.create(null);
|
|
20
|
-
new Array(neededVariables + 1).fill(0).forEach((x, i) => {
|
|
21
|
-
vars["\\$" + i] = "temp_" + i;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
function fill(variables?: { [name: string]: string | number }): string {
|
|
25
|
-
if (!variables) {
|
|
26
|
-
variables = Object.create(null);
|
|
27
|
-
}
|
|
28
50
|
|
|
29
|
-
|
|
51
|
+
function fill(
|
|
52
|
+
variables: { [name: string]: string | number } = Object.create(null)
|
|
53
|
+
) {
|
|
54
|
+
var userVariables = { ...providedVariables, ...variables };
|
|
55
|
+
|
|
56
|
+
// Validate all variables were passed in
|
|
57
|
+
for (var requiredVariable of requiredVariables) {
|
|
58
|
+
if (typeof userVariables[requiredVariable] === "undefined") {
|
|
59
|
+
throw new Error(
|
|
60
|
+
templates[0] +
|
|
61
|
+
" missing variable: " +
|
|
62
|
+
requiredVariable +
|
|
63
|
+
" from " +
|
|
64
|
+
JSON.stringify(userVariables)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
30
68
|
|
|
31
|
-
var
|
|
69
|
+
var template = choice(templates);
|
|
70
|
+
var output = template;
|
|
71
|
+
var allVariables = {
|
|
72
|
+
...defaultVariables,
|
|
73
|
+
...userVariables,
|
|
74
|
+
};
|
|
32
75
|
|
|
33
|
-
Object.keys(
|
|
34
|
-
var bracketName = "{" + name + "}";
|
|
35
|
-
var value =
|
|
76
|
+
Object.keys(allVariables).forEach((name) => {
|
|
77
|
+
var bracketName = "{" + name.replace("$", "\\$") + "}";
|
|
78
|
+
var value = allVariables[name] + "";
|
|
36
79
|
|
|
37
80
|
var reg = new RegExp(bracketName, "g");
|
|
38
81
|
|
|
39
|
-
|
|
82
|
+
output = output.replace(reg, value);
|
|
40
83
|
});
|
|
41
84
|
|
|
42
|
-
return
|
|
85
|
+
return { output, template };
|
|
43
86
|
}
|
|
44
87
|
|
|
45
88
|
function compile(variables: { [name: string]: string | number }): Node[] {
|
|
46
|
-
var
|
|
89
|
+
var { output, template } = fill(variables);
|
|
47
90
|
try {
|
|
48
|
-
var program = parseSnippet(
|
|
91
|
+
var program = parseSnippet(output);
|
|
49
92
|
|
|
50
93
|
return program.body;
|
|
51
94
|
} catch (e) {
|
|
52
95
|
console.error(e);
|
|
53
96
|
console.error(template);
|
|
54
|
-
|
|
97
|
+
console.error({ ...providedVariables, ...variables });
|
|
98
|
+
throw new Error(
|
|
99
|
+
"Template failed to parse: OUTPUT= " + output + " SOURCE= " + template
|
|
100
|
+
);
|
|
55
101
|
}
|
|
56
102
|
}
|
|
57
103
|
|
|
@@ -60,11 +106,25 @@ export default function Template(template: string): ITemplate {
|
|
|
60
106
|
return nodes[0];
|
|
61
107
|
}
|
|
62
108
|
|
|
109
|
+
function variables(newVariables) {
|
|
110
|
+
Object.assign(providedVariables, newVariables);
|
|
111
|
+
return obj;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function ignoreMissingVariables() {
|
|
115
|
+
defaultVariables = Object.create(null);
|
|
116
|
+
requiredVariables.clear();
|
|
117
|
+
return obj;
|
|
118
|
+
}
|
|
119
|
+
|
|
63
120
|
var obj: ITemplate = {
|
|
64
121
|
fill,
|
|
65
122
|
compile,
|
|
66
123
|
single,
|
|
67
|
-
|
|
124
|
+
templates,
|
|
125
|
+
variables,
|
|
126
|
+
ignoreMissingVariables,
|
|
127
|
+
source: templates[0],
|
|
68
128
|
};
|
|
69
129
|
|
|
70
130
|
return obj;
|
|
@@ -1,32 +1,51 @@
|
|
|
1
1
|
import { ObfuscateOrder } from "../order";
|
|
2
|
+
import Template from "../templates/template";
|
|
2
3
|
import { isBlock } from "../traverse";
|
|
3
4
|
import {
|
|
5
|
+
Node,
|
|
4
6
|
ExpressionStatement,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
CallExpression,
|
|
8
|
+
Identifier,
|
|
7
9
|
} from "../util/gen";
|
|
8
|
-
import {
|
|
10
|
+
import { prepend } from "../util/insert";
|
|
9
11
|
import Transform from "./transform";
|
|
10
12
|
|
|
11
13
|
// JsNice.org tries to separate sequence expressions into multiple lines, this stops that.
|
|
12
14
|
export default class AntiTooling extends Transform {
|
|
15
|
+
fnName: string;
|
|
16
|
+
|
|
13
17
|
constructor(o) {
|
|
14
18
|
super(o, ObfuscateOrder.AntiTooling);
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
apply(tree: Node) {
|
|
22
|
+
super.apply(tree);
|
|
23
|
+
|
|
24
|
+
if (typeof this.fnName === "string") {
|
|
25
|
+
prepend(
|
|
26
|
+
tree,
|
|
27
|
+
Template(`
|
|
28
|
+
function {fnName}(){
|
|
29
|
+
}
|
|
30
|
+
`).single({ fnName: this.fnName })
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
match(object, parents) {
|
|
18
36
|
return isBlock(object) || object.type == "SwitchCase";
|
|
19
37
|
}
|
|
20
38
|
|
|
21
39
|
transform(object, parents) {
|
|
22
40
|
return () => {
|
|
23
|
-
var exprs = [];
|
|
24
|
-
var deleteExprs = [];
|
|
41
|
+
var exprs: Node[] = [];
|
|
42
|
+
var deleteExprs: Node[] = [];
|
|
25
43
|
|
|
26
|
-
var body =
|
|
44
|
+
var body: Node[] =
|
|
45
|
+
object.type == "SwitchCase" ? object.consequent : object.body;
|
|
27
46
|
|
|
28
47
|
const end = () => {
|
|
29
|
-
function flatten(expr) {
|
|
48
|
+
function flatten(expr: Node) {
|
|
30
49
|
if (expr.type == "ExpressionStatement") {
|
|
31
50
|
flatten(expr.expression);
|
|
32
51
|
} else if (expr.type == "SequenceExpression") {
|
|
@@ -41,13 +60,16 @@ export default class AntiTooling extends Transform {
|
|
|
41
60
|
|
|
42
61
|
if (flattened.length > 1) {
|
|
43
62
|
flattened[0] = { ...flattened[0] };
|
|
63
|
+
|
|
64
|
+
if (!this.fnName) {
|
|
65
|
+
this.fnName = this.getPlaceholder();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// (expr1,expr2,expr3) -> F(expr1, expr2, expr3)
|
|
44
69
|
this.replace(
|
|
45
70
|
exprs[0],
|
|
46
71
|
ExpressionStatement(
|
|
47
|
-
|
|
48
|
-
choice(["typeof", "void", "!"]),
|
|
49
|
-
SequenceExpression(flattened)
|
|
50
|
-
)
|
|
72
|
+
CallExpression(Identifier(this.fnName), [...flattened])
|
|
51
73
|
)
|
|
52
74
|
);
|
|
53
75
|
|
|
@@ -47,9 +47,8 @@ import {
|
|
|
47
47
|
import { chance, choice, getRandomInteger, shuffle } from "../../util/random";
|
|
48
48
|
import Transform from "../transform";
|
|
49
49
|
import ExpressionObfuscation from "./expressionObfuscation";
|
|
50
|
-
import { isModuleSource } from "../string/stringConcealing";
|
|
51
50
|
import { reservedIdentifiers } from "../../constants";
|
|
52
|
-
import { isDirective } from "../../util/compare";
|
|
51
|
+
import { isDirective, isModuleSource } from "../../util/compare";
|
|
53
52
|
|
|
54
53
|
const flattenStructures = new Set([
|
|
55
54
|
"IfStatement",
|
|
@@ -1571,6 +1570,7 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
1571
1570
|
!this.isDebug &&
|
|
1572
1571
|
o.type === "Identifier" &&
|
|
1573
1572
|
this.mangleIdentifiers &&
|
|
1573
|
+
!reservedIdentifiers.has(o.name) &&
|
|
1574
1574
|
chance(50 - this.mangledExpressionsMade / 100) &&
|
|
1575
1575
|
!p.find((x) => isVarContext(x))
|
|
1576
1576
|
) {
|