js-confuser 1.7.1 → 1.7.3
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/.github/workflows/node.js.yml +1 -1
- package/CHANGELOG.md +73 -0
- package/README.md +32 -31
- package/dist/compiler.js +2 -8
- package/dist/constants.js +22 -10
- package/dist/index.js +15 -30
- package/dist/obfuscator.js +15 -62
- package/dist/options.js +33 -40
- 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 +121 -5
- package/dist/templates/core.js +35 -0
- package/dist/templates/crash.js +22 -11
- 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 +189 -43
- package/dist/transforms/antiTooling.js +26 -22
- package/dist/transforms/calculator.js +19 -55
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +242 -333
- package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +46 -25
- package/dist/transforms/deadCode.js +542 -31
- package/dist/transforms/dispatcher.js +112 -112
- 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 +41 -80
- package/dist/transforms/extraction/objectExtraction.js +24 -56
- package/dist/transforms/finalizer.js +6 -20
- package/dist/transforms/flatten.js +51 -99
- package/dist/transforms/identifier/globalAnalysis.js +21 -26
- package/dist/transforms/identifier/globalConcealing.js +72 -56
- package/dist/transforms/identifier/movedDeclarations.js +66 -38
- package/dist/transforms/identifier/renameVariables.js +36 -68
- package/dist/transforms/identifier/variableAnalysis.js +21 -48
- package/dist/transforms/lock/antiDebug.js +20 -25
- package/dist/transforms/lock/integrity.js +53 -52
- package/dist/transforms/lock/lock.js +161 -126
- package/dist/transforms/minify.js +77 -108
- package/dist/transforms/opaquePredicates.js +12 -49
- package/dist/transforms/preparation.js +28 -49
- package/dist/transforms/renameLabels.js +5 -22
- package/dist/transforms/rgf.js +125 -72
- package/dist/transforms/shuffle.js +42 -47
- package/dist/transforms/stack.js +41 -98
- package/dist/transforms/string/encoding.js +76 -27
- package/dist/transforms/string/stringCompression.js +75 -68
- package/dist/transforms/string/stringConcealing.js +127 -135
- package/dist/transforms/string/stringEncoding.js +6 -26
- package/dist/transforms/string/stringSplitting.js +5 -30
- package/dist/transforms/transform.js +76 -104
- 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 +5 -1
- package/dist/util/identifiers.js +9 -72
- 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 +31 -36
- package/dist/util/scope.js +6 -3
- package/docs/Countermeasures.md +13 -6
- package/docs/Integrity.md +35 -28
- package/docs/RGF.md +6 -1
- package/docs/RenameVariables.md +116 -0
- package/docs/TamperProtection.md +100 -0
- package/docs/Template.md +117 -0
- package/package.json +3 -3
- package/src/constants.ts +17 -0
- package/src/index.ts +7 -5
- package/src/options.ts +60 -7
- package/src/order.ts +2 -2
- package/src/templates/bufferToString.ts +79 -11
- package/src/templates/core.ts +29 -0
- package/src/templates/crash.ts +6 -38
- package/src/templates/es5.ts +1 -1
- package/src/templates/functionLength.ts +21 -3
- package/src/templates/globals.ts +3 -0
- package/src/templates/template.ts +205 -46
- package/src/transforms/antiTooling.ts +33 -11
- package/src/transforms/calculator.ts +4 -2
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +12 -5
- package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +46 -10
- package/src/transforms/deadCode.ts +74 -42
- package/src/transforms/dispatcher.ts +99 -73
- package/src/transforms/es5/antiClass.ts +25 -12
- package/src/transforms/es5/antiDestructuring.ts +1 -1
- package/src/transforms/es5/antiES6Object.ts +2 -2
- package/src/transforms/es5/antiTemplate.ts +1 -1
- package/src/transforms/extraction/classExtraction.ts +168 -0
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +11 -16
- package/src/transforms/extraction/objectExtraction.ts +4 -15
- package/src/transforms/flatten.ts +20 -5
- package/src/transforms/identifier/globalAnalysis.ts +18 -1
- package/src/transforms/identifier/globalConcealing.ts +119 -72
- package/src/transforms/identifier/movedDeclarations.ts +90 -24
- package/src/transforms/identifier/renameVariables.ts +16 -1
- package/src/transforms/lock/antiDebug.ts +2 -2
- package/src/transforms/lock/integrity.ts +13 -11
- package/src/transforms/lock/lock.ts +122 -30
- package/src/transforms/minify.ts +28 -13
- package/src/transforms/opaquePredicates.ts +2 -2
- package/src/transforms/preparation.ts +16 -0
- package/src/transforms/rgf.ts +139 -12
- package/src/transforms/shuffle.ts +3 -3
- package/src/transforms/stack.ts +19 -4
- package/src/transforms/string/encoding.ts +88 -51
- package/src/transforms/string/stringCompression.ts +86 -17
- package/src/transforms/string/stringConcealing.ts +148 -118
- package/src/transforms/string/stringEncoding.ts +1 -2
- package/src/transforms/string/stringSplitting.ts +1 -2
- package/src/transforms/transform.ts +63 -46
- package/src/types.ts +2 -0
- package/src/util/compare.ts +39 -5
- package/src/util/gen.ts +10 -3
- package/src/util/guard.ts +10 -0
- package/src/util/insert.ts +17 -0
- package/src/util/random.ts +81 -1
- package/src/util/scope.ts +14 -2
- package/test/code/Cash.test.ts +94 -5
- 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 +129 -55
- package/test/templates/template.test.ts +211 -1
- package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +37 -18
- package/test/transforms/dispatcher.test.ts +55 -0
- package/test/transforms/extraction/classExtraction.test.ts +86 -0
- package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +8 -0
- package/test/transforms/extraction/objectExtraction.test.ts +2 -0
- package/test/transforms/identifier/globalConcealing.test.ts +89 -0
- package/test/transforms/identifier/movedDeclarations.test.ts +61 -0
- package/test/transforms/identifier/renameVariables.test.ts +75 -1
- package/test/transforms/lock/tamperProtection.test.ts +336 -0
- package/test/transforms/minify.test.ts +37 -0
- package/test/transforms/rgf.test.ts +50 -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/src/options.ts
CHANGED
|
@@ -146,7 +146,7 @@ export interface ObfuscateOptions {
|
|
|
146
146
|
*
|
|
147
147
|
* ⚠️ Significantly impacts performance, use sparingly!
|
|
148
148
|
*
|
|
149
|
-
*
|
|
149
|
+
* Control-flow Flattening hinders program comprehension by creating convoluted switch statements. (`true/false/0-1`)
|
|
150
150
|
*
|
|
151
151
|
* Use a number to control the percentage from 0 to 1.
|
|
152
152
|
*
|
|
@@ -187,7 +187,7 @@ export interface ObfuscateOptions {
|
|
|
187
187
|
/**
|
|
188
188
|
* ### `stringConcealing`
|
|
189
189
|
*
|
|
190
|
-
*
|
|
190
|
+
* String Concealing involves encoding strings to conceal plain-text values. (`true/false/0-1`)
|
|
191
191
|
*
|
|
192
192
|
* `"console"` -> `decrypt('<~@rH7+Dert~>')`
|
|
193
193
|
*
|
|
@@ -200,7 +200,7 @@ export interface ObfuscateOptions {
|
|
|
200
200
|
/**
|
|
201
201
|
* ### `stringEncoding`
|
|
202
202
|
*
|
|
203
|
-
*
|
|
203
|
+
* String Encoding transforms a string into an encoded representation. (`true/false/0-1`)
|
|
204
204
|
*
|
|
205
205
|
* `"console"` -> `'\x63\x6f\x6e\x73\x6f\x6c\x65'`
|
|
206
206
|
*
|
|
@@ -215,7 +215,7 @@ export interface ObfuscateOptions {
|
|
|
215
215
|
/**
|
|
216
216
|
* ### `stringSplitting`
|
|
217
217
|
*
|
|
218
|
-
*
|
|
218
|
+
* String Splitting splits your strings into multiple expressions. (`true/false/0-1`)
|
|
219
219
|
*
|
|
220
220
|
* `"console"` -> `String.fromCharCode(99) + 'ons' + 'ole'`
|
|
221
221
|
*
|
|
@@ -230,7 +230,7 @@ export interface ObfuscateOptions {
|
|
|
230
230
|
/**
|
|
231
231
|
* ### `duplicateLiteralsRemoval`
|
|
232
232
|
*
|
|
233
|
-
*
|
|
233
|
+
* Duplicate Literals Removal replaces duplicate literals with a single variable name. (`true/false`)
|
|
234
234
|
*
|
|
235
235
|
* - Potency Medium
|
|
236
236
|
* - Resilience Low
|
|
@@ -406,6 +406,22 @@ export interface ObfuscateOptions {
|
|
|
406
406
|
*/
|
|
407
407
|
context?: string[];
|
|
408
408
|
|
|
409
|
+
/**
|
|
410
|
+
* ### `lock.tamperProtection`
|
|
411
|
+
*
|
|
412
|
+
* Tamper Protection safeguards the runtime behavior from being altered by JavaScript pitfalls. (`true/false`)
|
|
413
|
+
*
|
|
414
|
+
* **⚠️ Tamper Protection requires eval and ran in a non-strict mode environment!**
|
|
415
|
+
*
|
|
416
|
+
* - **This can break your code.**
|
|
417
|
+
* - **Due to the security concerns of arbitrary code execution, you must enable this yourself.**
|
|
418
|
+
*
|
|
419
|
+
* [Learn more here](https://github.com/MichaelXF/js-confuser/blob/master/TamperProtection.md).
|
|
420
|
+
*
|
|
421
|
+
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
422
|
+
*/
|
|
423
|
+
tamperProtection?: boolean | ((varName: string) => boolean);
|
|
424
|
+
|
|
409
425
|
/**
|
|
410
426
|
* ### `lock.startDate`
|
|
411
427
|
*
|
|
@@ -585,6 +601,15 @@ export interface ObfuscateOptions {
|
|
|
585
601
|
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
586
602
|
*/
|
|
587
603
|
debugComments?: boolean;
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* ### `preserveFunctionLength`
|
|
607
|
+
*
|
|
608
|
+
* Modified functions will retain the correct `function.length` property. Enabled by default. (`true/false`)
|
|
609
|
+
*
|
|
610
|
+
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
611
|
+
*/
|
|
612
|
+
preserveFunctionLength?: boolean;
|
|
588
613
|
}
|
|
589
614
|
|
|
590
615
|
const validProperties = new Set([
|
|
@@ -619,6 +644,21 @@ const validProperties = new Set([
|
|
|
619
644
|
"verbose",
|
|
620
645
|
"globalVariables",
|
|
621
646
|
"debugComments",
|
|
647
|
+
"preserveFunctionLength",
|
|
648
|
+
]);
|
|
649
|
+
|
|
650
|
+
const validLockProperties = new Set([
|
|
651
|
+
"selfDefending",
|
|
652
|
+
"antiDebug",
|
|
653
|
+
"context",
|
|
654
|
+
"tamperProtection",
|
|
655
|
+
"startDate",
|
|
656
|
+
"endDate",
|
|
657
|
+
"domainLock",
|
|
658
|
+
"osLock",
|
|
659
|
+
"browserLock",
|
|
660
|
+
"integrity",
|
|
661
|
+
"countermeasures",
|
|
622
662
|
]);
|
|
623
663
|
|
|
624
664
|
const validOses = new Set(["windows", "linux", "osx", "ios", "android"]);
|
|
@@ -654,6 +694,7 @@ export function validateOptions(options: ObfuscateOptions) {
|
|
|
654
694
|
options.target,
|
|
655
695
|
"Missing options.target option (required, must one the following: 'browser' or 'node')"
|
|
656
696
|
);
|
|
697
|
+
|
|
657
698
|
ok(
|
|
658
699
|
["browser", "node"].includes(options.target),
|
|
659
700
|
`'${options.target}' is not a valid target mode`
|
|
@@ -675,6 +716,13 @@ export function validateOptions(options: ObfuscateOptions) {
|
|
|
675
716
|
}
|
|
676
717
|
|
|
677
718
|
if (options.lock) {
|
|
719
|
+
ok(typeof options.lock === "object", "options.lock must be an object");
|
|
720
|
+
Object.keys(options.lock).forEach((key) => {
|
|
721
|
+
if (!validLockProperties.has(key)) {
|
|
722
|
+
throw new TypeError("Invalid lock option: '" + key + "'");
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
678
726
|
// Validate browser-lock option
|
|
679
727
|
if (
|
|
680
728
|
options.lock.browserLock &&
|
|
@@ -764,13 +812,18 @@ export async function correctOptions(
|
|
|
764
812
|
if (!options.hasOwnProperty("renameGlobals")) {
|
|
765
813
|
options.renameGlobals = true; // RenameGlobals is on by default
|
|
766
814
|
}
|
|
815
|
+
if (!options.hasOwnProperty("preserveFunctionLength")) {
|
|
816
|
+
options.preserveFunctionLength = true; // preserveFunctionLength is on by default
|
|
817
|
+
}
|
|
767
818
|
|
|
768
819
|
if (options.globalVariables && !(options.globalVariables instanceof Set)) {
|
|
769
820
|
options.globalVariables = new Set(Object.keys(options.globalVariables));
|
|
770
821
|
}
|
|
771
822
|
|
|
772
|
-
if (options.lock
|
|
773
|
-
options.
|
|
823
|
+
if (options.lock) {
|
|
824
|
+
if (options.lock.selfDefending) {
|
|
825
|
+
options.compact = true; // self defending forcibly enables this
|
|
826
|
+
}
|
|
774
827
|
}
|
|
775
828
|
|
|
776
829
|
// options.globalVariables outlines generic globals that should be present in the execution context
|
package/src/order.ts
CHANGED
|
@@ -1,19 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
placeholderVariablePrefix,
|
|
3
|
+
predictableFunctionTag,
|
|
4
|
+
} from "../constants";
|
|
5
|
+
import Transform from "../transforms/transform";
|
|
6
|
+
import { Node } from "../util/gen";
|
|
1
7
|
import Template from "./template";
|
|
2
8
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
9
|
+
export const createGetGlobalTemplate = (
|
|
10
|
+
transform: Transform,
|
|
11
|
+
object: Node,
|
|
12
|
+
parents: Node[]
|
|
13
|
+
) => {
|
|
14
|
+
var options = transform.options;
|
|
15
|
+
if (options.lock?.tamperProtection) {
|
|
16
|
+
return new Template(`
|
|
17
|
+
function {getGlobalFnName}(){
|
|
18
|
+
var localVar = false;
|
|
19
|
+
eval(${transform.jsConfuserVar("localVar")} + " = true")
|
|
20
|
+
if (!localVar) {
|
|
21
|
+
{countermeasures}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const root = eval("this");
|
|
25
|
+
return root;
|
|
26
|
+
}
|
|
27
|
+
`).setDefaultVariables({
|
|
28
|
+
countermeasures: transform.lockTransform.getCounterMeasuresCode(
|
|
29
|
+
object,
|
|
30
|
+
parents
|
|
31
|
+
),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return GetGlobalTemplate;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const GetGlobalTemplate = new Template(`
|
|
39
|
+
function ${placeholderVariablePrefix}CFG__getGlobalThis${predictableFunctionTag}(){
|
|
40
|
+
return globalThis
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ${placeholderVariablePrefix}CFG__getGlobal${predictableFunctionTag}(){
|
|
44
|
+
return global
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ${placeholderVariablePrefix}CFG__getWindow${predictableFunctionTag}(){
|
|
48
|
+
return window
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function ${placeholderVariablePrefix}CFG__getThisFunction${predictableFunctionTag}(){
|
|
52
|
+
return new Function("return this")()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function {getGlobalFnName}(array = [
|
|
56
|
+
${placeholderVariablePrefix}CFG__getGlobalThis${predictableFunctionTag},
|
|
57
|
+
${placeholderVariablePrefix}CFG__getGlobal${predictableFunctionTag},
|
|
58
|
+
${placeholderVariablePrefix}CFG__getWindow${predictableFunctionTag},
|
|
59
|
+
${placeholderVariablePrefix}CFG__getThisFunction${predictableFunctionTag}
|
|
60
|
+
]){
|
|
61
|
+
var bestMatch
|
|
62
|
+
var itemsToSearch = []
|
|
5
63
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
64
|
+
bestMatch = Object
|
|
65
|
+
itemsToSearch["push"](("")["__proto__"]["constructor"]["name"])
|
|
66
|
+
} catch(e) {
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
A: for(var i = 0; i < array["length"]; i++) {
|
|
8
70
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
71
|
+
bestMatch = array[i]()
|
|
72
|
+
for(var j = 0; j < itemsToSearch["length"]; j++) {
|
|
73
|
+
if(typeof bestMatch[itemsToSearch[j]] === "undefined") continue A;
|
|
74
|
+
}
|
|
75
|
+
return bestMatch
|
|
76
|
+
} catch(e) {}
|
|
13
77
|
}
|
|
78
|
+
|
|
79
|
+
return bestMatch || this;
|
|
14
80
|
}
|
|
81
|
+
`);
|
|
15
82
|
|
|
16
|
-
|
|
83
|
+
export const BufferToStringTemplate = new Template(`
|
|
84
|
+
{GetGlobalTemplate}
|
|
85
|
+
|
|
86
|
+
var __globalObject = {getGlobalFnName}() || {};
|
|
17
87
|
var __TextDecoder = __globalObject["TextDecoder"];
|
|
18
88
|
var __Uint8Array = __globalObject["Uint8Array"];
|
|
19
89
|
var __Buffer = __globalObject["Buffer"];
|
|
@@ -63,6 +133,4 @@ export const BufferToStringTemplate = Template(`
|
|
|
63
133
|
return utf8ArrayToStr(buffer);
|
|
64
134
|
}
|
|
65
135
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
136
|
`);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Template from "./template";
|
|
2
|
+
|
|
3
|
+
export const IndexOfTemplate = new Template(`
|
|
4
|
+
function indexOf(str, substr) {
|
|
5
|
+
const len = str.length;
|
|
6
|
+
const sublen = substr.length;
|
|
7
|
+
let count = 0;
|
|
8
|
+
|
|
9
|
+
if (sublen > len) {
|
|
10
|
+
return -1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i <= len - sublen; i++) {
|
|
14
|
+
for (let j = 0; j < sublen; j++) {
|
|
15
|
+
if (str[i + j] === substr[j]) {
|
|
16
|
+
count++;
|
|
17
|
+
if (count === sublen) {
|
|
18
|
+
return i;
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
count = 0;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return -1;
|
|
28
|
+
}
|
|
29
|
+
`);
|
package/src/templates/crash.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import Template from "./template";
|
|
2
2
|
|
|
3
|
-
export const CrashTemplate1 = Template(`
|
|
4
|
-
var {var} = "
|
|
5
|
-
while(
|
|
6
|
-
{var} = {var}
|
|
3
|
+
export const CrashTemplate1 = new Template(`
|
|
4
|
+
var {var} = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_\`{|}~"';
|
|
5
|
+
while(true){
|
|
6
|
+
{var} = {var};
|
|
7
|
+
if(!{var}) break;
|
|
7
8
|
}
|
|
8
9
|
`);
|
|
9
10
|
|
|
10
|
-
export const CrashTemplate2 = Template(`
|
|
11
|
+
export const CrashTemplate2 = new Template(`
|
|
11
12
|
while(true) {
|
|
12
13
|
var {var} = 99;
|
|
13
14
|
for({var} = 99; {var} == {var}; {var} *= {var}) {
|
|
@@ -20,36 +21,3 @@ while(true) {
|
|
|
20
21
|
{var}--
|
|
21
22
|
}
|
|
22
23
|
};`);
|
|
23
|
-
|
|
24
|
-
export const CrashTemplate3 = Template(`
|
|
25
|
-
try {
|
|
26
|
-
function {$2}(y, x){
|
|
27
|
-
return x;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
var {$1} = {$2}(this, function () {
|
|
31
|
-
var {$3} = function () {
|
|
32
|
-
var regExp = {$3}
|
|
33
|
-
.constructor('return /" + this + "/')()
|
|
34
|
-
.constructor('^([^ ]+( +[^ ]+)+)+[^ ]}');
|
|
35
|
-
|
|
36
|
-
return !regExp.call({$1});
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return {$3}();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
{$1}();
|
|
43
|
-
} catch ( {$1}e ) {
|
|
44
|
-
while({$1}e ? {$1}e : !{$1}e){
|
|
45
|
-
var {$1}b;
|
|
46
|
-
var {$1}c = 0;
|
|
47
|
-
({$1}e ? !{$1}e : {$1}e) ? (function({$1}e){
|
|
48
|
-
{$1}c = {$1}e ? 0 : !{$1}e ? 1 : 0;
|
|
49
|
-
})({$1}e) : {$1}b = 1;
|
|
50
|
-
|
|
51
|
-
if({$1}b&&{$1}c){break;}
|
|
52
|
-
if({$1}b){continue;}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
`);
|
package/src/templates/es5.ts
CHANGED
|
@@ -5,7 +5,7 @@ import Template from "./template";
|
|
|
5
5
|
*
|
|
6
6
|
* Source: https://vanillajstoolkit.com/polyfills/
|
|
7
7
|
*/
|
|
8
|
-
export const ES5Template = Template(`
|
|
8
|
+
export const ES5Template = new Template(`
|
|
9
9
|
if (!Array.prototype.forEach) {
|
|
10
10
|
Array.prototype.forEach = function forEach (callback, thisArg) {
|
|
11
11
|
if (typeof callback !== 'function') {
|
|
@@ -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 = new 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,71 +1,230 @@
|
|
|
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";
|
|
6
|
+
import traverse from "../traverse";
|
|
7
|
+
|
|
8
|
+
export interface TemplateVariables {
|
|
9
|
+
[varName: string]:
|
|
10
|
+
| string
|
|
11
|
+
| (() => Node | Node[] | Template)
|
|
12
|
+
| Node
|
|
13
|
+
| Node[]
|
|
14
|
+
| Template;
|
|
15
|
+
}
|
|
3
16
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Templates provides an easy way to parse code snippets into AST subtrees.
|
|
19
|
+
*
|
|
20
|
+
* These AST subtrees can added to the obfuscated code, tailored with variable names.
|
|
21
|
+
*
|
|
22
|
+
* 1. Basic string interpolation
|
|
23
|
+
*
|
|
24
|
+
* ```js
|
|
25
|
+
* var Base64Template = new Template(`
|
|
26
|
+
* function {name}(str){
|
|
27
|
+
* return btoa(str)
|
|
28
|
+
* }
|
|
29
|
+
* `);
|
|
30
|
+
*
|
|
31
|
+
* var functionDeclaration = Base64Template.single({ name: "atob" });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* 2. AST subtree insertion
|
|
35
|
+
*
|
|
36
|
+
* ```js
|
|
37
|
+
* var Base64Template = new Template(`
|
|
38
|
+
* function {name}(str){
|
|
39
|
+
* {getWindow}
|
|
40
|
+
*
|
|
41
|
+
* return {getWindowName}btoa(str)
|
|
42
|
+
* }`)
|
|
43
|
+
*
|
|
44
|
+
* var functionDeclaration = Base64Template.single({
|
|
45
|
+
* name: "atob",
|
|
46
|
+
* getWindowName: "newWindow",
|
|
47
|
+
* getWindow: () => {
|
|
48
|
+
* return acorn.parse("var newWindow = {}").body[0];
|
|
49
|
+
* }
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Here, the `getWindow` variable is a function that returns an AST subtree. This must be a `Node[]` array or Template.
|
|
54
|
+
* Optionally, the function can be replaced with just the `Node[]` array or Template if it's already computed.
|
|
55
|
+
*
|
|
56
|
+
* 3. Template subtree insertion
|
|
57
|
+
*
|
|
58
|
+
* ```js
|
|
59
|
+
* var NewWindowTemplate = new Template(`
|
|
60
|
+
* var newWindow = {};
|
|
61
|
+
* `);
|
|
62
|
+
*
|
|
63
|
+
* var Base64Template = new Template(`
|
|
64
|
+
* function {name}(str){
|
|
65
|
+
* {NewWindowTemplate}
|
|
66
|
+
*
|
|
67
|
+
* return newWindow.btoa(str)
|
|
68
|
+
* }`)
|
|
69
|
+
*
|
|
70
|
+
* var functionDeclaration = Base64Template.single({
|
|
71
|
+
* name: "atob",
|
|
72
|
+
* NewWindowTemplate: NewWindowTemplate
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export default class Template {
|
|
77
|
+
templates: string[];
|
|
78
|
+
defaultVariables: TemplateVariables;
|
|
79
|
+
requiredVariables: Set<string>;
|
|
80
|
+
|
|
81
|
+
constructor(...templates: string[]) {
|
|
82
|
+
this.templates = templates;
|
|
83
|
+
this.defaultVariables = Object.create(null);
|
|
84
|
+
this.requiredVariables = new Set<string>();
|
|
85
|
+
|
|
86
|
+
this.findRequiredVariables();
|
|
87
|
+
}
|
|
10
88
|
|
|
11
|
-
|
|
12
|
-
|
|
89
|
+
setDefaultVariables(defaultVariables: TemplateVariables): this {
|
|
90
|
+
this.defaultVariables = defaultVariables;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
13
93
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
94
|
+
private findRequiredVariables() {
|
|
95
|
+
var matches = this.templates[0].match(/{[$A-z0-9_]+}/g);
|
|
96
|
+
if (matches !== null) {
|
|
97
|
+
matches.forEach((variable) => {
|
|
98
|
+
var name = variable.slice(1, -1);
|
|
99
|
+
|
|
100
|
+
// $ variables are for default variables
|
|
101
|
+
if (name.startsWith("$")) {
|
|
102
|
+
throw new Error("Default variables are no longer supported.");
|
|
103
|
+
} else {
|
|
104
|
+
this.requiredVariables.add(name);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
18
108
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Interpolates the template with the given variables.
|
|
112
|
+
*
|
|
113
|
+
* Prepares the template string for AST parsing.
|
|
114
|
+
*
|
|
115
|
+
* @param variables
|
|
116
|
+
*/
|
|
117
|
+
private interpolateTemplate(variables: TemplateVariables = {}) {
|
|
118
|
+
var allVariables = { ...this.defaultVariables, ...variables };
|
|
119
|
+
|
|
120
|
+
// Validate all variables were passed in
|
|
121
|
+
for (var requiredVariable of this.requiredVariables) {
|
|
122
|
+
if (typeof allVariables[requiredVariable] === "undefined") {
|
|
123
|
+
throw new Error(
|
|
124
|
+
this.templates[0] +
|
|
125
|
+
" missing variable: " +
|
|
126
|
+
requiredVariable +
|
|
127
|
+
" from " +
|
|
128
|
+
JSON.stringify(allVariables)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
27
131
|
}
|
|
28
132
|
|
|
29
|
-
var
|
|
133
|
+
var template = choice(this.templates);
|
|
134
|
+
var output = template;
|
|
30
135
|
|
|
31
|
-
|
|
136
|
+
Object.keys(allVariables).forEach((name) => {
|
|
137
|
+
var bracketName = "{" + name.replace("$", "\\$") + "}";
|
|
32
138
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
139
|
+
var value = allVariables[name] + "";
|
|
140
|
+
if (typeof allVariables[name] !== "string") {
|
|
141
|
+
value = name;
|
|
142
|
+
}
|
|
36
143
|
|
|
37
144
|
var reg = new RegExp(bracketName, "g");
|
|
38
145
|
|
|
39
|
-
|
|
146
|
+
output = output.replace(reg, value);
|
|
40
147
|
});
|
|
41
148
|
|
|
42
|
-
return
|
|
149
|
+
return { output, template };
|
|
43
150
|
}
|
|
44
151
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Finds the variables in the AST and replaces them with the given values.
|
|
154
|
+
*
|
|
155
|
+
* Note: Mutates the AST.
|
|
156
|
+
* @param ast
|
|
157
|
+
* @param variables
|
|
158
|
+
*/
|
|
159
|
+
private interpolateAST(ast: Node, variables: TemplateVariables) {
|
|
160
|
+
var allVariables = { ...this.defaultVariables, ...variables };
|
|
161
|
+
|
|
162
|
+
var astNames = new Set(
|
|
163
|
+
Object.keys(allVariables).filter((name) => {
|
|
164
|
+
return typeof allVariables[name] !== "string";
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (astNames.size === 0) return;
|
|
169
|
+
|
|
170
|
+
traverse(ast, (o, p) => {
|
|
171
|
+
if (o.type === "Identifier" && allVariables[o.name]) {
|
|
172
|
+
return () => {
|
|
173
|
+
var value = allVariables[o.name];
|
|
174
|
+
ok(typeof value !== "string");
|
|
175
|
+
|
|
176
|
+
var insertNodes = typeof value === "function" ? value() : value;
|
|
177
|
+
if (insertNodes instanceof Template) {
|
|
178
|
+
insertNodes = insertNodes.compile(allVariables);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!Array.isArray(insertNodes)) {
|
|
182
|
+
// Replace with expression
|
|
183
|
+
|
|
184
|
+
Object.assign(o, insertNodes);
|
|
185
|
+
} else {
|
|
186
|
+
// Insert multiple statements/declarations
|
|
187
|
+
var expressionStatement: Node = p[0];
|
|
188
|
+
var body: Node[] = p[1] as any;
|
|
189
|
+
|
|
190
|
+
ok(expressionStatement.type === "ExpressionStatement");
|
|
191
|
+
ok(Array.isArray(body));
|
|
192
|
+
|
|
193
|
+
var index = body.indexOf(expressionStatement);
|
|
194
|
+
|
|
195
|
+
body.splice(index, 1, ...insertNodes);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
compile(variables: TemplateVariables = {}): Node[] {
|
|
203
|
+
var { output, template } = this.interpolateTemplate(variables);
|
|
49
204
|
|
|
50
|
-
|
|
205
|
+
var program: Node;
|
|
206
|
+
try {
|
|
207
|
+
program = parseSnippet(output);
|
|
51
208
|
} catch (e) {
|
|
52
|
-
|
|
53
|
-
console.error(template);
|
|
54
|
-
throw new Error("Template failed to parse");
|
|
209
|
+
throw new Error(output + "\n" + "Template failed to parse: " + e.message);
|
|
55
210
|
}
|
|
56
|
-
}
|
|
57
211
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return
|
|
212
|
+
this.interpolateAST(program, variables);
|
|
213
|
+
|
|
214
|
+
return program.body;
|
|
61
215
|
}
|
|
62
216
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
217
|
+
single(variables: TemplateVariables = {}): Node {
|
|
218
|
+
var nodes = this.compile(variables);
|
|
219
|
+
|
|
220
|
+
if (nodes.length !== 1) {
|
|
221
|
+
nodes = nodes.filter((node) => node.type !== "EmptyStatement");
|
|
222
|
+
ok(
|
|
223
|
+
nodes.length === 1,
|
|
224
|
+
`Expected single node, got ${nodes.map((node) => node.type).join(", ")}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
69
227
|
|
|
70
|
-
|
|
228
|
+
return nodes[0];
|
|
229
|
+
}
|
|
71
230
|
}
|