js-confuser 2.0.0-alpha.2 → 2.0.0-alpha.4
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/.prettierrc +4 -0
- package/CHANGELOG.md +42 -8
- package/Migration.md +23 -8
- package/README.md +2 -2
- package/dist/constants.js +11 -2
- package/dist/index.js +49 -6
- package/dist/obfuscator.js +121 -10
- package/dist/order.js +0 -1
- package/dist/probability.js +1 -96
- package/dist/templates/getGlobalTemplate.js +4 -1
- package/dist/templates/integrityTemplate.js +1 -1
- package/dist/templates/stringCompressionTemplate.js +3 -3
- package/dist/templates/tamperProtectionTemplates.js +1 -1
- package/dist/templates/template.js +17 -12
- package/dist/transforms/controlFlowFlattening.js +112 -83
- package/dist/transforms/deadCode.js +21 -22
- package/dist/transforms/dispatcher.js +62 -37
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +5 -0
- package/dist/transforms/extraction/objectExtraction.js +1 -2
- package/dist/transforms/finalizer.js +1 -1
- package/dist/transforms/flatten.js +2 -19
- package/dist/transforms/identifier/globalConcealing.js +3 -4
- package/dist/transforms/identifier/movedDeclarations.js +12 -5
- package/dist/transforms/identifier/renameVariables.js +40 -6
- package/dist/transforms/lock/integrity.js +9 -1
- package/dist/transforms/lock/lock.js +16 -9
- package/dist/transforms/minify.js +64 -27
- package/dist/transforms/opaquePredicates.js +6 -7
- package/dist/transforms/pack.js +32 -5
- package/dist/transforms/plugin.js +20 -39
- package/dist/transforms/preparation.js +25 -36
- package/dist/transforms/renameLabels.js +1 -2
- package/dist/transforms/rgf.js +36 -16
- package/dist/transforms/shuffle.js +10 -11
- package/dist/transforms/string/stringCompression.js +14 -10
- package/dist/transforms/string/stringConcealing.js +7 -5
- package/dist/transforms/string/stringEncoding.js +4 -2
- package/dist/transforms/string/stringSplitting.js +4 -2
- package/dist/transforms/variableMasking.js +3 -2
- package/dist/utils/NameGen.js +5 -2
- package/dist/utils/PredicateGen.js +62 -0
- package/dist/utils/ast-utils.js +24 -9
- package/dist/utils/random-utils.js +10 -0
- package/dist/validateOptions.js +2 -2
- package/index.d.ts +16 -2
- package/package.json +2 -2
- package/src/constants.ts +15 -5
- package/src/index.ts +15 -5
- package/src/obfuscationResult.ts +7 -1
- package/src/obfuscator.ts +152 -12
- package/src/options.ts +26 -8
- package/src/order.ts +0 -2
- package/src/templates/getGlobalTemplate.ts +5 -1
- package/src/templates/integrityTemplate.ts +14 -19
- package/src/templates/stringCompressionTemplate.ts +4 -28
- package/src/templates/tamperProtectionTemplates.ts +7 -3
- package/src/templates/template.ts +5 -3
- package/src/transforms/controlFlowFlattening.ts +139 -83
- package/src/transforms/deadCode.ts +27 -30
- package/src/transforms/dispatcher.ts +24 -5
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +10 -1
- package/src/transforms/extraction/objectExtraction.ts +1 -2
- package/src/transforms/finalizer.ts +1 -1
- package/src/transforms/flatten.ts +3 -22
- package/src/transforms/identifier/globalConcealing.ts +26 -17
- package/src/transforms/identifier/movedDeclarations.ts +18 -6
- package/src/transforms/identifier/renameVariables.ts +48 -6
- package/src/transforms/lock/integrity.ts +11 -1
- package/src/transforms/lock/lock.ts +26 -10
- package/src/transforms/minify.ts +85 -38
- package/src/transforms/opaquePredicates.ts +6 -9
- package/src/transforms/pack.ts +41 -5
- package/src/transforms/plugin.ts +47 -69
- package/src/transforms/preparation.ts +33 -46
- package/src/transforms/renameLabels.ts +1 -2
- package/src/transforms/rgf.ts +52 -23
- package/src/transforms/shuffle.ts +28 -26
- package/src/transforms/string/encoding.ts +1 -1
- package/src/transforms/string/stringCompression.ts +22 -13
- package/src/transforms/string/stringConcealing.ts +13 -7
- package/src/transforms/string/stringEncoding.ts +6 -2
- package/src/transforms/string/stringSplitting.ts +9 -4
- package/src/transforms/variableMasking.ts +2 -2
- package/src/utils/NameGen.ts +13 -3
- package/src/utils/PredicateGen.ts +61 -0
- package/src/utils/ast-utils.ts +16 -9
- package/src/utils/random-utils.ts +14 -0
- package/src/validateOptions.ts +7 -4
- package/src/probability.ts +0 -110
- package/src/transforms/functionOutlining.ts +0 -225
- package/src/utils/ControlObject.ts +0 -141
|
@@ -4,10 +4,10 @@ import { NameGen } from "../../utils/NameGen";
|
|
|
4
4
|
import Template from "../../templates/template";
|
|
5
5
|
import { PluginArg, PluginObject } from "../plugin";
|
|
6
6
|
import { Order } from "../../order";
|
|
7
|
-
import { computeProbabilityMap } from "../../probability";
|
|
8
7
|
import {
|
|
9
8
|
MULTI_TRANSFORM,
|
|
10
9
|
reservedIdentifiers,
|
|
10
|
+
reservedNodeModuleIdentifiers,
|
|
11
11
|
variableFunctionName,
|
|
12
12
|
} from "../../constants";
|
|
13
13
|
import {
|
|
@@ -16,11 +16,15 @@ import {
|
|
|
16
16
|
prepend,
|
|
17
17
|
} from "../../utils/ast-utils";
|
|
18
18
|
import { createGetGlobalTemplate } from "../../templates/getGlobalTemplate";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
getRandomInteger,
|
|
21
|
+
getRandomString,
|
|
22
|
+
shuffle,
|
|
23
|
+
} from "../../utils/random-utils";
|
|
20
24
|
import { ok } from "assert";
|
|
21
25
|
|
|
22
26
|
const ignoreGlobals = new Set([
|
|
23
|
-
|
|
27
|
+
...reservedNodeModuleIdentifiers,
|
|
24
28
|
"__dirname",
|
|
25
29
|
"eval",
|
|
26
30
|
"arguments",
|
|
@@ -52,19 +56,21 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
const createSwitchStatement = () => {
|
|
55
|
-
const cases = Array.from(globalMapping.keys()).map(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
t.
|
|
60
|
-
t.
|
|
61
|
-
t.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
const cases = shuffle(Array.from(globalMapping.keys())).map(
|
|
60
|
+
(originalName) => {
|
|
61
|
+
var mappedKey = globalMapping.get(originalName);
|
|
62
|
+
|
|
63
|
+
return t.switchCase(t.stringLiteral(mappedKey), [
|
|
64
|
+
t.returnStatement(
|
|
65
|
+
t.memberExpression(
|
|
66
|
+
t.identifier(globalVarName),
|
|
67
|
+
t.stringLiteral(originalName),
|
|
68
|
+
true
|
|
69
|
+
)
|
|
70
|
+
),
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
68
74
|
|
|
69
75
|
return t.switchStatement(t.identifier("mapping"), cases);
|
|
70
76
|
};
|
|
@@ -132,7 +138,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
132
138
|
if (!mapping) {
|
|
133
139
|
// Allow user to disable custom global variables
|
|
134
140
|
if (
|
|
135
|
-
!computeProbabilityMap(
|
|
141
|
+
!me.computeProbabilityMap(
|
|
142
|
+
me.options.globalConcealing,
|
|
143
|
+
globalName
|
|
144
|
+
)
|
|
136
145
|
)
|
|
137
146
|
continue;
|
|
138
147
|
|
|
@@ -65,8 +65,12 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
65
65
|
if (!functionPath || !(functionPath.node as NodeSymbol)[PREDICTABLE])
|
|
66
66
|
return;
|
|
67
67
|
|
|
68
|
+
var fnBody = functionPath.get("body");
|
|
69
|
+
|
|
70
|
+
if (!fnBody.isBlockStatement()) return;
|
|
71
|
+
|
|
68
72
|
// Must be direct child of the function
|
|
69
|
-
if (path.parentPath !==
|
|
73
|
+
if (path.parentPath !== fnBody) return;
|
|
70
74
|
|
|
71
75
|
const functionName = path.node.id.name;
|
|
72
76
|
|
|
@@ -99,7 +103,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
prepend(
|
|
102
|
-
|
|
106
|
+
fnBody,
|
|
103
107
|
new Template(`
|
|
104
108
|
if(!${functionName}) {
|
|
105
109
|
${functionName} = {functionExpression};
|
|
@@ -113,6 +117,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
113
117
|
},
|
|
114
118
|
VariableDeclaration: {
|
|
115
119
|
exit(path) {
|
|
120
|
+
if (me.isSkipped(path)) return;
|
|
116
121
|
if (path.node.kind !== "var") return;
|
|
117
122
|
if (path.node.declarations.length !== 1) return;
|
|
118
123
|
|
|
@@ -150,7 +155,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
150
155
|
let isDefinedAtTop = false;
|
|
151
156
|
const parentPath = path.parentPath;
|
|
152
157
|
if (parentPath.isBlock()) {
|
|
153
|
-
isDefinedAtTop =
|
|
158
|
+
isDefinedAtTop =
|
|
159
|
+
parentPath
|
|
160
|
+
.get("body")
|
|
161
|
+
.filter((x) => x.type !== "ImportDeclaration")
|
|
162
|
+
.indexOf(path) === 0;
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
// Already at the top - nothing will change
|
|
@@ -214,7 +223,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
214
223
|
path.isBlock()
|
|
215
224
|
) as NodePath<t.Block>;
|
|
216
225
|
|
|
217
|
-
var topNode = block.node.body
|
|
226
|
+
var topNode = block.node.body.filter(
|
|
227
|
+
(x) => x.type !== "ImportDeclaration"
|
|
228
|
+
)[0];
|
|
218
229
|
const variableDeclarator = t.variableDeclarator(
|
|
219
230
|
t.identifier(name)
|
|
220
231
|
);
|
|
@@ -223,8 +234,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
223
234
|
topNode.declarations.push(variableDeclarator);
|
|
224
235
|
break;
|
|
225
236
|
} else {
|
|
226
|
-
|
|
227
|
-
|
|
237
|
+
prepend(
|
|
238
|
+
block,
|
|
239
|
+
me.skip(t.variableDeclaration("var", [variableDeclarator]))
|
|
228
240
|
);
|
|
229
241
|
}
|
|
230
242
|
|
|
@@ -6,9 +6,7 @@ import { Order } from "../../order";
|
|
|
6
6
|
import {
|
|
7
7
|
noRenameVariablePrefix,
|
|
8
8
|
placeholderVariablePrefix,
|
|
9
|
-
variableFunctionName,
|
|
10
9
|
} from "../../constants";
|
|
11
|
-
import { computeProbabilityMap } from "../../probability";
|
|
12
10
|
import {
|
|
13
11
|
getParentFunctionOrProgram,
|
|
14
12
|
isDefiningIdentifier,
|
|
@@ -30,6 +28,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
30
28
|
|
|
31
29
|
const definedMap = new Map<t.Node, Set<string>>();
|
|
32
30
|
const referencedMap = new Map<t.Node, Set<string>>();
|
|
31
|
+
const paramMap = new Map<t.Node, Set<string>>(); // Used for default function parameter special case
|
|
33
32
|
const bindingMap = new Map<t.Node, Map<string, NodePath<t.Identifier>>>();
|
|
34
33
|
|
|
35
34
|
const renamedVariables = new Map<t.Node, Map<string, string>>();
|
|
@@ -50,9 +49,12 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
50
49
|
];
|
|
51
50
|
|
|
52
51
|
let isDefined = false;
|
|
52
|
+
let isParameter = false;
|
|
53
53
|
|
|
54
54
|
if (path.isBindingIdentifier() && isDefiningIdentifier(path)) {
|
|
55
55
|
isDefined = true;
|
|
56
|
+
const binding = path.scope.getBinding(path.node.name);
|
|
57
|
+
if (binding?.kind === "param") isParameter = true;
|
|
56
58
|
|
|
57
59
|
// Function ID is defined in the parent's function declaration
|
|
58
60
|
if (
|
|
@@ -117,7 +119,30 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
117
119
|
|
|
118
120
|
var newName = null;
|
|
119
121
|
|
|
122
|
+
const skippedPaths = new Set();
|
|
123
|
+
|
|
120
124
|
for (let contextPath of contextPaths) {
|
|
125
|
+
if (skippedPaths.has(contextPath)) continue;
|
|
126
|
+
|
|
127
|
+
if (contextPath.isFunction()) {
|
|
128
|
+
var assignmentPattern = contextPath.find(
|
|
129
|
+
(p) => p.listKey === "params" && p.parentPath.isFunction()
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
if (assignmentPattern?.isAssignmentPattern()) {
|
|
133
|
+
var functionPath = assignmentPattern.getFunctionParent();
|
|
134
|
+
|
|
135
|
+
if (functionPath) {
|
|
136
|
+
// The parameters can be still accessed...
|
|
137
|
+
const params = paramMap.get(functionPath.node);
|
|
138
|
+
if (params?.has(identifierName)) {
|
|
139
|
+
} else {
|
|
140
|
+
skippedPaths.add(functionPath);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
121
146
|
const { node } = contextPath;
|
|
122
147
|
|
|
123
148
|
const defined = definedMap.get(node);
|
|
@@ -140,6 +165,17 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
140
165
|
// 5. Update Identifier node's 'name' property
|
|
141
166
|
node.name = newName;
|
|
142
167
|
node[RENAMED] = true;
|
|
168
|
+
|
|
169
|
+
// 6. Additional parameter mapping
|
|
170
|
+
const binding = identifierPath.scope.getBinding(identifierName);
|
|
171
|
+
if (binding?.kind === "param") {
|
|
172
|
+
var mapNode = binding.scope.path.node;
|
|
173
|
+
if (!paramMap.has(mapNode)) {
|
|
174
|
+
paramMap.set(mapNode, new Set([identifierName]));
|
|
175
|
+
} else {
|
|
176
|
+
paramMap.get(mapNode).add(identifierName);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
143
179
|
}
|
|
144
180
|
},
|
|
145
181
|
|
|
@@ -215,19 +251,25 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
215
251
|
// Placeholder variables should always be renamed
|
|
216
252
|
if (name.startsWith(placeholderVariablePrefix)) return true;
|
|
217
253
|
|
|
218
|
-
|
|
219
|
-
|
|
254
|
+
const binding = bindings?.get(name);
|
|
255
|
+
|
|
256
|
+
if (binding) {
|
|
257
|
+
// Do not rename exports
|
|
258
|
+
if (isExportedIdentifier(binding)) return false;
|
|
259
|
+
}
|
|
220
260
|
|
|
221
261
|
if (name === me.obfuscator.getStringCompressionLibraryName())
|
|
222
262
|
return false;
|
|
223
263
|
|
|
224
264
|
// Global variables are additionally checked against user option
|
|
225
265
|
if (isGlobal) {
|
|
226
|
-
if (!computeProbabilityMap(me.options.renameGlobals, name))
|
|
266
|
+
if (!me.computeProbabilityMap(me.options.renameGlobals, name))
|
|
227
267
|
return false;
|
|
228
268
|
}
|
|
229
269
|
|
|
230
|
-
if (
|
|
270
|
+
if (
|
|
271
|
+
!me.computeProbabilityMap(me.options.renameVariables, name, isGlobal)
|
|
272
|
+
)
|
|
231
273
|
return false;
|
|
232
274
|
|
|
233
275
|
return true;
|
|
@@ -5,6 +5,7 @@ import { HashFunction } from "../../templates/integrityTemplate";
|
|
|
5
5
|
import * as t from "@babel/types";
|
|
6
6
|
import Template from "../../templates/template";
|
|
7
7
|
import { NodePath } from "@babel/traverse";
|
|
8
|
+
import { NameGen } from "../../utils/NameGen";
|
|
8
9
|
|
|
9
10
|
export interface IntegrityInterface {
|
|
10
11
|
fnPath: NodePath<t.FunctionDeclaration>;
|
|
@@ -32,6 +33,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
32
33
|
},
|
|
33
34
|
});
|
|
34
35
|
|
|
36
|
+
const nameGen = new NameGen(me.options.identifierGenerator, {
|
|
37
|
+
avoidObjectPrototype: true,
|
|
38
|
+
avoidReserved: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
35
41
|
return {
|
|
36
42
|
visitor: {
|
|
37
43
|
Program: {
|
|
@@ -78,12 +84,16 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
78
84
|
|
|
79
85
|
var hashCode = HashFunction(codeTrimmed, seed);
|
|
80
86
|
|
|
87
|
+
const selfName = funcDecPath.node.id.name;
|
|
88
|
+
const selfCacheProperty = nameGen.generate();
|
|
89
|
+
const selfCacheString = `${selfName}.${selfCacheProperty}`;
|
|
90
|
+
|
|
81
91
|
// me.log(codeTrimmed, hashCode);
|
|
82
92
|
me.changeData.functions++;
|
|
83
93
|
|
|
84
94
|
funcDecPath.node.body = t.blockStatement(
|
|
85
95
|
new Template(`
|
|
86
|
-
var hash = ${obfuscatedHashFnName}(${newFunctionDeclaration.id.name}, ${seed});
|
|
96
|
+
var hash = ${selfCacheString} || (${selfCacheString} = ${obfuscatedHashFnName}(${newFunctionDeclaration.id.name}, ${seed}));
|
|
87
97
|
if(hash === ${hashCode}) {
|
|
88
98
|
{originalBody}
|
|
89
99
|
} else {
|
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
NativeFunctionTemplate,
|
|
27
27
|
StrictModeTemplate,
|
|
28
28
|
} from "../../templates/tamperProtectionTemplates";
|
|
29
|
-
import { computeProbabilityMap } from "../../probability";
|
|
30
29
|
|
|
31
30
|
export default ({ Plugin }: PluginArg): PluginObject => {
|
|
32
31
|
const me = Plugin(Order.Lock, {
|
|
@@ -36,6 +35,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
36
35
|
});
|
|
37
36
|
|
|
38
37
|
if (me.options.lock.startDate instanceof Date) {
|
|
38
|
+
// Ensure date is in the past
|
|
39
|
+
if (me.options.lock.startDate.getTime() > Date.now()) {
|
|
40
|
+
me.warn("lock.startDate is detected to be in the future");
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
me.options.lock.customLocks.push({
|
|
40
44
|
code: [
|
|
41
45
|
`
|
|
@@ -54,6 +58,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
if (me.options.lock.endDate instanceof Date) {
|
|
61
|
+
// Ensure date is in the future
|
|
62
|
+
if (me.options.lock.endDate.getTime() < Date.now()) {
|
|
63
|
+
me.warn("lock.endDate is detected to be in the past");
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
me.options.lock.customLocks.push({
|
|
58
67
|
code: [
|
|
59
68
|
`
|
|
@@ -107,6 +116,13 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
107
116
|
}
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
// Prepack Breaker
|
|
120
|
+
if(Math.random() > 1) {
|
|
121
|
+
while(true) {
|
|
122
|
+
console.log("Prepack Breaker");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
110
126
|
return namedFunction();
|
|
111
127
|
}
|
|
112
128
|
)();
|
|
@@ -127,7 +143,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
127
143
|
const timesMap = new WeakMap<CustomLock, number>();
|
|
128
144
|
|
|
129
145
|
let countermeasuresNode: NodePath<t.Identifier>;
|
|
130
|
-
let invokeCountermeasuresFnName;
|
|
146
|
+
let invokeCountermeasuresFnName: string;
|
|
131
147
|
|
|
132
148
|
if (me.options.lock.countermeasures) {
|
|
133
149
|
invokeCountermeasuresFnName = me.getPlaceholder("invokeCountermeasures");
|
|
@@ -149,15 +165,13 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
149
165
|
};
|
|
150
166
|
me.globalState.lock.createCountermeasuresCode = createCountermeasuresCode;
|
|
151
167
|
|
|
152
|
-
|
|
153
|
-
let times = timesMap.get(customLock);
|
|
168
|
+
const defaultMaxCount = me.options.lock.defaultMaxCount ?? 25;
|
|
154
169
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
170
|
+
function applyLockToBlock(path: NodePath<t.Block>, customLock: CustomLock) {
|
|
171
|
+
let times = timesMap.get(customLock) || 0;
|
|
158
172
|
|
|
159
|
-
let maxCount = customLock.maxCount
|
|
160
|
-
let minCount = customLock.minCount
|
|
173
|
+
let maxCount = customLock.maxCount ?? defaultMaxCount; // 25 is default max count
|
|
174
|
+
let minCount = customLock.minCount ?? 1; // 1 is default min count
|
|
161
175
|
|
|
162
176
|
if (maxCount >= 0 && times > maxCount) {
|
|
163
177
|
// Limit creation, allowing -1 to disable the limit entirely
|
|
@@ -356,7 +370,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
356
370
|
// Don't apply to invokeCountermeasures function (Intended)
|
|
357
371
|
if (me.obfuscator.isInternalVariable(functionName)) return;
|
|
358
372
|
|
|
359
|
-
if (
|
|
373
|
+
if (
|
|
374
|
+
!me.computeProbabilityMap(me.options.lock.integrity, functionName)
|
|
375
|
+
)
|
|
360
376
|
return;
|
|
361
377
|
|
|
362
378
|
var newFnName = me.getPlaceholder();
|
package/src/transforms/minify.ts
CHANGED
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
isUndefined,
|
|
9
9
|
} from "../utils/ast-utils";
|
|
10
10
|
import { Binding, Scope } from "@babel/traverse";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
NO_REMOVE,
|
|
13
|
+
NodeSymbol,
|
|
14
|
+
placeholderVariablePrefix,
|
|
15
|
+
UNSAFE,
|
|
16
|
+
} from "../constants";
|
|
12
17
|
|
|
13
18
|
const identifierMap = new Map<string, () => t.Expression>();
|
|
14
19
|
identifierMap.set("undefined", () =>
|
|
@@ -18,6 +23,45 @@ identifierMap.set("Infinity", () =>
|
|
|
18
23
|
t.binaryExpression("/", t.numericLiteral(1), t.numericLiteral(0))
|
|
19
24
|
);
|
|
20
25
|
|
|
26
|
+
function trySimpleDestructuring(id, init) {
|
|
27
|
+
// Simple array/object destructuring
|
|
28
|
+
if (id.isArrayPattern() && init.isArrayExpression()) {
|
|
29
|
+
const elements = id.get("elements");
|
|
30
|
+
const initElements = init.get("elements");
|
|
31
|
+
|
|
32
|
+
if (elements.length === 1 && initElements.length === 1) {
|
|
33
|
+
id.replaceWith(elements[0]);
|
|
34
|
+
init.replaceWith(initElements[0]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (id.isObjectPattern() && init.isObjectExpression()) {
|
|
39
|
+
const properties = id.get("properties");
|
|
40
|
+
const initProperties = init.get("properties");
|
|
41
|
+
|
|
42
|
+
if (properties.length === 1 && initProperties.length === 1) {
|
|
43
|
+
const firstProperty = properties[0];
|
|
44
|
+
const firstInitProperty = initProperties[0];
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
firstProperty.isObjectProperty() &&
|
|
48
|
+
firstInitProperty.isObjectProperty()
|
|
49
|
+
) {
|
|
50
|
+
const firstKey = firstProperty.get("key");
|
|
51
|
+
const firstInitKey = firstInitProperty.get("key");
|
|
52
|
+
if (
|
|
53
|
+
firstKey.isIdentifier() &&
|
|
54
|
+
firstInitKey.isIdentifier() &&
|
|
55
|
+
firstKey.node.name === firstInitKey.node.name
|
|
56
|
+
) {
|
|
57
|
+
id.replaceWith(firstProperty.node.value);
|
|
58
|
+
init.replaceWith(firstInitProperty.node.value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
21
65
|
/**
|
|
22
66
|
* Minify removes unnecessary code and shortens the length for file size.
|
|
23
67
|
*
|
|
@@ -120,6 +164,19 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
120
164
|
}
|
|
121
165
|
},
|
|
122
166
|
},
|
|
167
|
+
// "a" + "b" -> "ab"
|
|
168
|
+
BinaryExpression: {
|
|
169
|
+
exit(path) {
|
|
170
|
+
if (path.node.operator !== "+") return;
|
|
171
|
+
|
|
172
|
+
const left = path.get("left");
|
|
173
|
+
const right = path.get("right");
|
|
174
|
+
|
|
175
|
+
if (!left.isStringLiteral() || !right.isStringLiteral()) return;
|
|
176
|
+
|
|
177
|
+
path.replaceWith(t.stringLiteral(left.node.value + right.node.value));
|
|
178
|
+
},
|
|
179
|
+
},
|
|
123
180
|
// a["key"] -> a.key
|
|
124
181
|
MemberExpression: {
|
|
125
182
|
exit(path) {
|
|
@@ -215,7 +272,8 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
215
272
|
const id = path.get("id");
|
|
216
273
|
if (
|
|
217
274
|
id.isIdentifier() &&
|
|
218
|
-
!id.node.name.startsWith(placeholderVariablePrefix)
|
|
275
|
+
!id.node.name.startsWith(placeholderVariablePrefix) &&
|
|
276
|
+
!(path.node as NodeSymbol)[NO_REMOVE]
|
|
219
277
|
) {
|
|
220
278
|
const binding = path.scope.getBinding(id.node.name);
|
|
221
279
|
if (
|
|
@@ -241,42 +299,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
241
299
|
const id = path.get("id");
|
|
242
300
|
const init = path.get("init");
|
|
243
301
|
|
|
244
|
-
|
|
245
|
-
if (id.isArrayPattern() && init.isArrayExpression()) {
|
|
246
|
-
const elements = id.get("elements");
|
|
247
|
-
const initElements = init.get("elements");
|
|
248
|
-
|
|
249
|
-
if (elements.length === 1 && initElements.length === 1) {
|
|
250
|
-
id.replaceWith(elements[0]);
|
|
251
|
-
init.replaceWith(initElements[0]);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (id.isObjectPattern() && init.isObjectExpression()) {
|
|
256
|
-
const properties = id.get("properties");
|
|
257
|
-
const initProperties = init.get("properties");
|
|
258
|
-
|
|
259
|
-
if (properties.length === 1 && initProperties.length === 1) {
|
|
260
|
-
const firstProperty = properties[0];
|
|
261
|
-
const firstInitProperty = initProperties[0];
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
firstProperty.isObjectProperty() &&
|
|
265
|
-
firstInitProperty.isObjectProperty()
|
|
266
|
-
) {
|
|
267
|
-
const firstKey = firstProperty.get("key");
|
|
268
|
-
const firstInitKey = firstInitProperty.get("key");
|
|
269
|
-
if (
|
|
270
|
-
firstKey.isIdentifier() &&
|
|
271
|
-
firstInitKey.isIdentifier() &&
|
|
272
|
-
firstKey.node.name === firstInitKey.node.name
|
|
273
|
-
) {
|
|
274
|
-
id.replaceWith(firstProperty.node.value);
|
|
275
|
-
init.replaceWith(firstInitProperty.node.value);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
302
|
+
trySimpleDestructuring(id, init);
|
|
280
303
|
|
|
281
304
|
// Remove unused variables
|
|
282
305
|
// Can only remove if it's pure
|
|
@@ -285,6 +308,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
285
308
|
const fn = getParentFunctionOrProgram(path);
|
|
286
309
|
if ((fn.node as NodeSymbol)[UNSAFE]) return;
|
|
287
310
|
|
|
311
|
+
// Node explicitly marked as not to be removed
|
|
312
|
+
if ((id as NodeSymbol)[NO_REMOVE]) return;
|
|
313
|
+
|
|
288
314
|
const binding = path.scope.getBinding(id.node.name);
|
|
289
315
|
|
|
290
316
|
if (
|
|
@@ -304,6 +330,27 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
304
330
|
}
|
|
305
331
|
},
|
|
306
332
|
},
|
|
333
|
+
// Simple destructuring
|
|
334
|
+
// Simple arithmetic operations
|
|
335
|
+
AssignmentExpression: {
|
|
336
|
+
exit(path) {
|
|
337
|
+
if (path.node.operator === "=") {
|
|
338
|
+
trySimpleDestructuring(path.get("left"), path.get("right"));
|
|
339
|
+
}
|
|
340
|
+
if (path.node.operator === "+=") {
|
|
341
|
+
const left = path.get("left");
|
|
342
|
+
const right = path.get("right");
|
|
343
|
+
|
|
344
|
+
// a += 1 -> a++
|
|
345
|
+
if (right.isNumericLiteral({ value: 1 })) {
|
|
346
|
+
if (left.isIdentifier() || left.isMemberExpression()) {
|
|
347
|
+
path.replaceWith(t.updateExpression("++", left.node));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
|
|
307
354
|
// return undefined->return
|
|
308
355
|
ReturnStatement: {
|
|
309
356
|
exit(path) {
|
|
@@ -2,9 +2,8 @@ import { PluginArg, PluginObject } from "./plugin";
|
|
|
2
2
|
import { Order } from "../order";
|
|
3
3
|
import { NodePath } from "@babel/traverse";
|
|
4
4
|
import * as t from "@babel/types";
|
|
5
|
-
import { getBlock } from "../utils/ast-utils";
|
|
6
5
|
import { chance, getRandomString } from "../utils/random-utils";
|
|
7
|
-
import
|
|
6
|
+
import PredicateGen from "../utils/PredicateGen";
|
|
8
7
|
|
|
9
8
|
export default ({ Plugin }: PluginArg): PluginObject => {
|
|
10
9
|
const me = Plugin(Order.OpaquePredicates, {
|
|
@@ -13,12 +12,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
13
12
|
},
|
|
14
13
|
});
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
const controlObject = me.getControlObject(getBlock(path));
|
|
18
|
-
|
|
19
|
-
var trueValue = controlObject.createTruePredicate();
|
|
15
|
+
const predicateGen = new PredicateGen(me);
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
function createTruePredicate(path: NodePath) {
|
|
18
|
+
return predicateGen.generateTrueExpression(path);
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
let active = true;
|
|
@@ -27,13 +24,13 @@ export default ({ Plugin }: PluginArg): PluginObject => {
|
|
|
27
24
|
if (!active) return false;
|
|
28
25
|
if (path.find((p) => me.isSkipped(p))) return false;
|
|
29
26
|
|
|
30
|
-
if (!computeProbabilityMap(me.options.opaquePredicates)) return false;
|
|
27
|
+
if (!me.computeProbabilityMap(me.options.opaquePredicates)) return false;
|
|
31
28
|
|
|
32
29
|
transformCount++;
|
|
33
30
|
|
|
34
31
|
const depth = path.getAncestry().length;
|
|
35
32
|
|
|
36
|
-
return chance(
|
|
33
|
+
return chance(500 - transformCount - depth * 100);
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
function wrapWithPredicate(path: NodePath) {
|
package/src/transforms/pack.ts
CHANGED
|
@@ -10,22 +10,43 @@ import {
|
|
|
10
10
|
GEN_NODE,
|
|
11
11
|
NodeSymbol,
|
|
12
12
|
reservedIdentifiers,
|
|
13
|
+
reservedNodeModuleIdentifiers,
|
|
13
14
|
variableFunctionName,
|
|
14
15
|
WITH_STATEMENT,
|
|
15
16
|
} from "../constants";
|
|
16
17
|
import { PluginArg, PluginObject } from "./plugin";
|
|
17
18
|
import { Order } from "../order";
|
|
18
19
|
|
|
20
|
+
export interface PackInterface {
|
|
21
|
+
objectName: string;
|
|
22
|
+
mappings: Map<string, string>;
|
|
23
|
+
setterPropsNeeded: Set<string>;
|
|
24
|
+
typeofMappings: Map<string, string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
export default function pack({ Plugin }: PluginArg): PluginObject {
|
|
20
28
|
const me = Plugin(Order.Pack, {
|
|
21
29
|
changeData: {
|
|
22
30
|
globals: 0,
|
|
23
31
|
},
|
|
24
32
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
|
|
34
|
+
// RGF functions will re-use parent Pack Interface
|
|
35
|
+
let packInterface = me.obfuscator.parentObfuscator?.packInterface;
|
|
36
|
+
|
|
37
|
+
// Create new Pack Interface (root)
|
|
38
|
+
if (!packInterface) {
|
|
39
|
+
packInterface = {
|
|
40
|
+
objectName: me.obfuscator.nameGen.generate(),
|
|
41
|
+
mappings: new Map<string, string>(),
|
|
42
|
+
setterPropsNeeded: new Set<string>(),
|
|
43
|
+
typeofMappings: new Map<string, string>(),
|
|
44
|
+
};
|
|
45
|
+
me.obfuscator.packInterface = packInterface;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { objectName, mappings, setterPropsNeeded, typeofMappings } =
|
|
49
|
+
packInterface;
|
|
29
50
|
|
|
30
51
|
const prependNodes: t.Statement[] = [];
|
|
31
52
|
|
|
@@ -53,10 +74,23 @@ export default function pack({ Plugin }: PluginArg): PluginObject {
|
|
|
53
74
|
|
|
54
75
|
const identifierName = path.node.name;
|
|
55
76
|
if (reservedIdentifiers.has(identifierName)) return;
|
|
56
|
-
if (
|
|
77
|
+
if (
|
|
78
|
+
me.options.target === "node" &&
|
|
79
|
+
reservedNodeModuleIdentifiers.has(identifierName)
|
|
80
|
+
) {
|
|
81
|
+
// Allow module.exports and require
|
|
82
|
+
} else {
|
|
83
|
+
if (me.options.globalVariables.has(identifierName)) return;
|
|
84
|
+
}
|
|
57
85
|
if (identifierName === variableFunctionName) return;
|
|
86
|
+
if (identifierName === objectName) return;
|
|
58
87
|
|
|
59
88
|
if (!path.scope.hasGlobal(identifierName)) return;
|
|
89
|
+
if (path.scope.hasBinding(identifierName)) return;
|
|
90
|
+
|
|
91
|
+
// Check user's custom implementation
|
|
92
|
+
if (!me.computeProbabilityMap(me.options.pack, identifierName))
|
|
93
|
+
return;
|
|
60
94
|
|
|
61
95
|
if (
|
|
62
96
|
path.key === "argument" &&
|
|
@@ -105,6 +139,8 @@ export default function pack({ Plugin }: PluginArg): PluginObject {
|
|
|
105
139
|
// Final AST handler
|
|
106
140
|
// Very last step in the obfuscation process
|
|
107
141
|
finalASTHandler(ast) {
|
|
142
|
+
if (me.obfuscator.parentObfuscator) return ast; // Only for root obfuscator
|
|
143
|
+
|
|
108
144
|
// Create object expression
|
|
109
145
|
// Very similar to flatten, maybe refactor to use the same code
|
|
110
146
|
const objectProperties: t.ObjectMethod[] = [];
|