js-confuser 2.0.0 → 2.0.1
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/ISSUE_TEMPLATE/bug_report.md +43 -43
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/workflows/node.js.yml +28 -28
- package/.prettierrc +4 -4
- package/CHANGELOG.md +1015 -987
- package/CODE_OF_CONDUCT.md +131 -131
- package/CONTRIBUTING.md +52 -52
- package/LICENSE +21 -21
- package/Migration.md +72 -72
- package/README.md +86 -86
- package/dist/constants.js +43 -43
- package/dist/index.js +14 -23
- package/dist/obfuscator.js +31 -25
- package/dist/order.js +4 -4
- package/dist/presets.js +31 -31
- package/dist/templates/integrityTemplate.js +4 -4
- package/dist/templates/template.js +1 -2
- package/dist/transforms/astScrambler.js +1 -2
- package/dist/transforms/calculator.js +1 -2
- package/dist/transforms/controlFlowFlattening.js +60 -41
- package/dist/transforms/deadCode.js +1 -2
- package/dist/transforms/dispatcher.js +4 -5
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +1 -2
- package/dist/transforms/extraction/objectExtraction.js +1 -2
- package/dist/transforms/finalizer.js +1 -2
- package/dist/transforms/flatten.js +1 -2
- package/dist/transforms/identifier/globalConcealing.js +15 -2
- package/dist/transforms/identifier/movedDeclarations.js +8 -7
- package/dist/transforms/identifier/renameVariables.js +7 -7
- package/dist/transforms/lock/integrity.js +8 -9
- package/dist/transforms/lock/lock.js +1 -2
- package/dist/transforms/minify.js +11 -29
- package/dist/transforms/opaquePredicates.js +1 -2
- package/dist/transforms/pack.js +1 -2
- package/dist/transforms/plugin.js +18 -19
- package/dist/transforms/preparation.js +16 -16
- package/dist/transforms/renameLabels.js +1 -2
- package/dist/transforms/rgf.js +8 -9
- package/dist/transforms/shuffle.js +1 -2
- package/dist/transforms/string/encoding.js +1 -2
- package/dist/transforms/string/stringCompression.js +3 -4
- package/dist/transforms/string/stringConcealing.js +1 -2
- package/dist/transforms/string/stringEncoding.js +1 -2
- package/dist/transforms/variableMasking.js +1 -2
- package/dist/utils/NameGen.js +2 -2
- package/dist/utils/PredicateGen.js +1 -2
- package/dist/utils/ast-utils.js +87 -88
- package/dist/utils/function-utils.js +8 -8
- package/dist/utils/node.js +5 -6
- package/dist/utils/object-utils.js +4 -4
- package/dist/utils/random-utils.js +20 -20
- package/dist/utils/static-utils.js +1 -2
- package/dist/validateOptions.js +4 -7
- package/index.d.ts +17 -17
- package/package.json +61 -59
- package/src/constants.ts +168 -168
- package/src/index.ts +118 -118
- package/src/obfuscationResult.ts +49 -49
- package/src/obfuscator.ts +501 -497
- package/src/options.ts +407 -407
- package/src/order.ts +54 -54
- package/src/presets.ts +125 -125
- package/src/templates/bufferToStringTemplate.ts +57 -57
- package/src/templates/deadCodeTemplates.ts +1185 -1185
- package/src/templates/getGlobalTemplate.ts +76 -76
- package/src/templates/integrityTemplate.ts +64 -64
- package/src/templates/setFunctionLengthTemplate.ts +11 -11
- package/src/templates/stringCompressionTemplate.ts +20 -20
- package/src/templates/tamperProtectionTemplates.ts +120 -120
- package/src/templates/template.ts +224 -224
- package/src/transforms/astScrambler.ts +99 -99
- package/src/transforms/calculator.ts +99 -99
- package/src/transforms/controlFlowFlattening.ts +1716 -1680
- package/src/transforms/deadCode.ts +82 -82
- package/src/transforms/dispatcher.ts +450 -450
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +156 -158
- package/src/transforms/extraction/objectExtraction.ts +186 -186
- package/src/transforms/finalizer.ts +74 -74
- package/src/transforms/flatten.ts +421 -418
- package/src/transforms/identifier/globalConcealing.ts +315 -295
- package/src/transforms/identifier/movedDeclarations.ts +252 -251
- package/src/transforms/identifier/renameVariables.ts +328 -321
- package/src/transforms/lock/integrity.ts +117 -117
- package/src/transforms/lock/lock.ts +418 -418
- package/src/transforms/minify.ts +615 -629
- package/src/transforms/opaquePredicates.ts +100 -100
- package/src/transforms/pack.ts +239 -239
- package/src/transforms/plugin.ts +173 -173
- package/src/transforms/preparation.ts +349 -347
- package/src/transforms/renameLabels.ts +175 -175
- package/src/transforms/rgf.ts +322 -322
- package/src/transforms/shuffle.ts +82 -82
- package/src/transforms/string/encoding.ts +144 -144
- package/src/transforms/string/stringCompression.ts +128 -128
- package/src/transforms/string/stringConcealing.ts +312 -312
- package/src/transforms/string/stringEncoding.ts +80 -80
- package/src/transforms/string/stringSplitting.ts +77 -77
- package/src/transforms/variableMasking.ts +257 -257
- package/src/utils/IntGen.ts +33 -33
- package/src/utils/NameGen.ts +116 -116
- package/src/utils/PredicateGen.ts +61 -61
- package/src/utils/ast-utils.ts +663 -663
- package/src/utils/function-utils.ts +50 -50
- package/src/utils/gen-utils.ts +48 -48
- package/src/utils/node.ts +78 -78
- package/src/utils/object-utils.ts +21 -21
- package/src/utils/random-utils.ts +93 -93
- package/src/utils/static-utils.ts +66 -66
- package/src/validateOptions.ts +256 -259
- package/tsconfig.json +13 -14
- package/dist/probability.js +0 -1
- package/dist/transforms/functionOutlining.js +0 -230
- package/dist/utils/ControlObject.js +0 -125
|
@@ -1,418 +1,418 @@
|
|
|
1
|
-
import { NodePath } from "@babel/traverse";
|
|
2
|
-
import { PluginArg, PluginObject } from "../plugin";
|
|
3
|
-
import { Order } from "../../order";
|
|
4
|
-
import { chance, choice } from "../../utils/random-utils";
|
|
5
|
-
import Template from "../../templates/template";
|
|
6
|
-
import * as t from "@babel/types";
|
|
7
|
-
import { CustomLock } from "../../options";
|
|
8
|
-
import {
|
|
9
|
-
getFunctionName,
|
|
10
|
-
getParentFunctionOrProgram,
|
|
11
|
-
isDefiningIdentifier,
|
|
12
|
-
isVariableIdentifier,
|
|
13
|
-
prependProgram,
|
|
14
|
-
} from "../../utils/ast-utils";
|
|
15
|
-
import { INTEGRITY, NodeIntegrity } from "./integrity";
|
|
16
|
-
import { HashTemplate } from "../../templates/integrityTemplate";
|
|
17
|
-
import {
|
|
18
|
-
MULTI_TRANSFORM,
|
|
19
|
-
NodeSymbol,
|
|
20
|
-
PREDICTABLE,
|
|
21
|
-
SKIP,
|
|
22
|
-
UNSAFE,
|
|
23
|
-
} from "../../constants";
|
|
24
|
-
import {
|
|
25
|
-
IndexOfTemplate,
|
|
26
|
-
NativeFunctionTemplate,
|
|
27
|
-
StrictModeTemplate,
|
|
28
|
-
} from "../../templates/tamperProtectionTemplates";
|
|
29
|
-
|
|
30
|
-
export default ({ Plugin }: PluginArg): PluginObject => {
|
|
31
|
-
const me = Plugin(Order.Lock, {
|
|
32
|
-
changeData: {
|
|
33
|
-
locksInserted: 0,
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
|
|
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
|
-
|
|
43
|
-
me.options.lock.customLocks.push({
|
|
44
|
-
code: [
|
|
45
|
-
`
|
|
46
|
-
if(Date.now()<${me.options.lock.startDate.getTime()}) {
|
|
47
|
-
{countermeasures}
|
|
48
|
-
}
|
|
49
|
-
`,
|
|
50
|
-
`
|
|
51
|
-
if((new Date()).getTime()<${me.options.lock.startDate.getTime()}) {
|
|
52
|
-
{countermeasures}
|
|
53
|
-
}
|
|
54
|
-
`,
|
|
55
|
-
],
|
|
56
|
-
percentagePerBlock: 0.5,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
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
|
-
|
|
66
|
-
me.options.lock.customLocks.push({
|
|
67
|
-
code: [
|
|
68
|
-
`
|
|
69
|
-
if(Date.now()>${me.options.lock.endDate.getTime()}) {
|
|
70
|
-
{countermeasures}
|
|
71
|
-
}
|
|
72
|
-
`,
|
|
73
|
-
`
|
|
74
|
-
if((new Date()).getTime()>${me.options.lock.endDate.getTime()}) {
|
|
75
|
-
{countermeasures}
|
|
76
|
-
}
|
|
77
|
-
`,
|
|
78
|
-
],
|
|
79
|
-
percentagePerBlock: 0.5,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (me.options.lock.domainLock) {
|
|
84
|
-
var domainArray = Array.isArray(me.options.lock.domainLock)
|
|
85
|
-
? me.options.lock.domainLock
|
|
86
|
-
: [me.options.lock.domainLock];
|
|
87
|
-
|
|
88
|
-
for (const regexString of domainArray) {
|
|
89
|
-
me.options.lock.customLocks.push({
|
|
90
|
-
code: new Template(`
|
|
91
|
-
if(!new RegExp({regexString}).test(window.location.href)) {
|
|
92
|
-
{countermeasures}
|
|
93
|
-
}
|
|
94
|
-
`).setDefaultVariables({
|
|
95
|
-
regexString: () => t.stringLiteral(regexString.toString()),
|
|
96
|
-
}),
|
|
97
|
-
percentagePerBlock: 0.5,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (me.options.lock.selfDefending) {
|
|
103
|
-
me.options.lock.customLocks.push({
|
|
104
|
-
code: `
|
|
105
|
-
(
|
|
106
|
-
function(){
|
|
107
|
-
// Breaks any code formatter
|
|
108
|
-
var namedFunction = function(){
|
|
109
|
-
const test = function(){
|
|
110
|
-
const regExp= new RegExp('\\n');
|
|
111
|
-
return regExp['test'](namedFunction)
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
if(test()) {
|
|
115
|
-
{countermeasures}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return namedFunction();
|
|
120
|
-
}
|
|
121
|
-
)();
|
|
122
|
-
`,
|
|
123
|
-
percentagePerBlock: 0.5,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (me.options.lock.antiDebug) {
|
|
128
|
-
me.options.lock.customLocks.push({
|
|
129
|
-
code: `
|
|
130
|
-
debugger;
|
|
131
|
-
`,
|
|
132
|
-
percentagePerBlock: 0.5,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const timesMap = new WeakMap<CustomLock, number>();
|
|
137
|
-
|
|
138
|
-
let countermeasuresNode: NodePath<t.Identifier>;
|
|
139
|
-
let invokeCountermeasuresFnName: string;
|
|
140
|
-
|
|
141
|
-
if (me.options.lock.countermeasures) {
|
|
142
|
-
invokeCountermeasuresFnName = me.getPlaceholder("invokeCountermeasures");
|
|
143
|
-
|
|
144
|
-
me.globalState.internals.invokeCountermeasuresFnName =
|
|
145
|
-
invokeCountermeasuresFnName;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
var createCountermeasuresCode = () => {
|
|
149
|
-
if (invokeCountermeasuresFnName) {
|
|
150
|
-
return new Template(`${invokeCountermeasuresFnName}()`).compile();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (me.options.lock.countermeasures === false) {
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return new Template(`while(true){}`).compile();
|
|
158
|
-
};
|
|
159
|
-
me.globalState.lock.createCountermeasuresCode = createCountermeasuresCode;
|
|
160
|
-
|
|
161
|
-
const defaultMaxCount = me.options.lock.defaultMaxCount ?? 25;
|
|
162
|
-
|
|
163
|
-
function applyLockToBlock(path: NodePath<t.Block>, customLock: CustomLock) {
|
|
164
|
-
let times = timesMap.get(customLock) || 0;
|
|
165
|
-
|
|
166
|
-
let maxCount = customLock.maxCount ?? defaultMaxCount; // 25 is default max count
|
|
167
|
-
let minCount = customLock.minCount ?? 1; // 1 is default min count
|
|
168
|
-
|
|
169
|
-
if (maxCount >= 0 && times > maxCount) {
|
|
170
|
-
// Limit creation, allowing -1 to disable the limit entirely
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// The Program always gets a lock
|
|
175
|
-
// Else based on the percentage
|
|
176
|
-
// Try to reach the minimum count
|
|
177
|
-
if (
|
|
178
|
-
!path.isProgram() &&
|
|
179
|
-
!chance(customLock.percentagePerBlock * 100) &&
|
|
180
|
-
times >= minCount
|
|
181
|
-
) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Increment the times
|
|
186
|
-
timesMap.set(customLock, times + 1);
|
|
187
|
-
|
|
188
|
-
const lockCode = Array.isArray(customLock.code)
|
|
189
|
-
? choice(customLock.code)
|
|
190
|
-
: customLock.code;
|
|
191
|
-
|
|
192
|
-
const template =
|
|
193
|
-
typeof lockCode === "string" ? new Template(lockCode) : lockCode;
|
|
194
|
-
const lockNodes = template.compile({
|
|
195
|
-
countermeasures: () => createCountermeasuresCode(),
|
|
196
|
-
});
|
|
197
|
-
var p = path.unshiftContainer("body", lockNodes);
|
|
198
|
-
p.forEach((p) => p.skip());
|
|
199
|
-
|
|
200
|
-
me.changeData.locksInserted++;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
visitor: {
|
|
205
|
-
BindingIdentifier(path) {
|
|
206
|
-
if (path.node.name !== me.options.lock.countermeasures) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Exclude labels
|
|
211
|
-
if (!isVariableIdentifier(path)) return;
|
|
212
|
-
|
|
213
|
-
if (!isDefiningIdentifier(path)) {
|
|
214
|
-
// Reassignments are not allowed
|
|
215
|
-
|
|
216
|
-
me.error("Countermeasures function cannot be reassigned");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (countermeasuresNode) {
|
|
220
|
-
// Disallow multiple countermeasures functions
|
|
221
|
-
|
|
222
|
-
me.error(
|
|
223
|
-
"Countermeasures function was already defined, it must have a unique name from the rest of your code"
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
path.scope.getBinding(path.node.name).scope !==
|
|
229
|
-
path.scope.getProgramParent()
|
|
230
|
-
) {
|
|
231
|
-
me.error(
|
|
232
|
-
"Countermeasures function must be defined at the global level"
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
countermeasuresNode = path;
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
Block: {
|
|
240
|
-
exit(path) {
|
|
241
|
-
var customLock = choice(me.options.lock.customLocks);
|
|
242
|
-
if (customLock) {
|
|
243
|
-
applyLockToBlock(path, customLock);
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
Program: {
|
|
249
|
-
exit(path) {
|
|
250
|
-
// Insert nativeFunctionCheck
|
|
251
|
-
if (me.options.lock.tamperProtection) {
|
|
252
|
-
// Disallow strict mode
|
|
253
|
-
// Tamper Protection uses non-strict mode features:
|
|
254
|
-
// - eval() with local scope assignments
|
|
255
|
-
const directives = path.get("directives");
|
|
256
|
-
for (var directive of directives) {
|
|
257
|
-
if (directive.node.value.value === "use strict") {
|
|
258
|
-
me.error(
|
|
259
|
-
"Tamper Protection cannot be applied to code in strict mode. Disable strict mode by removing the 'use strict' directive, or disable Tamper Protection."
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
var nativeFunctionName =
|
|
265
|
-
me.getPlaceholder() + "_nativeFunctionCheck";
|
|
266
|
-
|
|
267
|
-
me.obfuscator.globalState.internals.nativeFunctionName =
|
|
268
|
-
nativeFunctionName;
|
|
269
|
-
|
|
270
|
-
// Ensure program is not in strict mode
|
|
271
|
-
// Tamper Protection forces non-strict mode
|
|
272
|
-
prependProgram(
|
|
273
|
-
path,
|
|
274
|
-
StrictModeTemplate.compile({
|
|
275
|
-
nativeFunctionName,
|
|
276
|
-
countermeasures: createCountermeasuresCode(),
|
|
277
|
-
})
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
const nativeFunctionDeclaration = NativeFunctionTemplate.single({
|
|
281
|
-
nativeFunctionName,
|
|
282
|
-
countermeasures: createCountermeasuresCode(),
|
|
283
|
-
IndexOfTemplate: IndexOfTemplate,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
// Checks function's toString() value for [native code] signature
|
|
287
|
-
prependProgram(path, nativeFunctionDeclaration);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Insert invokeCountermeasures function
|
|
291
|
-
if (invokeCountermeasuresFnName) {
|
|
292
|
-
if (!countermeasuresNode) {
|
|
293
|
-
me.error(
|
|
294
|
-
"Countermeasures function named '" +
|
|
295
|
-
me.options.lock.countermeasures +
|
|
296
|
-
"' was not found."
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
var hasInvoked = me.getPlaceholder("hasInvoked");
|
|
301
|
-
var statements = new Template(`
|
|
302
|
-
var ${hasInvoked} = false;
|
|
303
|
-
function ${invokeCountermeasuresFnName}(){
|
|
304
|
-
if(${hasInvoked}) return;
|
|
305
|
-
${hasInvoked} = true;
|
|
306
|
-
${me.options.lock.countermeasures}();
|
|
307
|
-
}
|
|
308
|
-
`)
|
|
309
|
-
.addSymbols(MULTI_TRANSFORM)
|
|
310
|
-
.compile();
|
|
311
|
-
|
|
312
|
-
prependProgram(path, statements).forEach((p) => p.skip());
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (me.options.lock.integrity) {
|
|
316
|
-
const hashFnName = me.getPlaceholder() + "_hash";
|
|
317
|
-
const imulFnName = me.getPlaceholder() + "_imul";
|
|
318
|
-
|
|
319
|
-
const { sensitivityRegex } = me.globalState.lock.integrity;
|
|
320
|
-
me.globalState.internals.integrityHashName = hashFnName;
|
|
321
|
-
|
|
322
|
-
const hashCode = HashTemplate.compile({
|
|
323
|
-
imul: imulFnName,
|
|
324
|
-
name: hashFnName,
|
|
325
|
-
hashingUtilFnName: me.getPlaceholder(),
|
|
326
|
-
sensitivityRegex: () =>
|
|
327
|
-
t.newExpression(t.identifier("RegExp"), [
|
|
328
|
-
t.stringLiteral(sensitivityRegex.source),
|
|
329
|
-
t.stringLiteral(sensitivityRegex.flags),
|
|
330
|
-
]),
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
prependProgram(path, hashCode);
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
// Integrity first pass
|
|
339
|
-
// Functions are prepared for Integrity by simply extracting the function body
|
|
340
|
-
// The extracted function is hashed in the 'integrity' plugin
|
|
341
|
-
FunctionDeclaration: {
|
|
342
|
-
exit(funcDecPath) {
|
|
343
|
-
if (!me.options.lock.integrity) return;
|
|
344
|
-
|
|
345
|
-
// Mark functions for integrity
|
|
346
|
-
// Don't apply to async or generator functions
|
|
347
|
-
if (funcDecPath.node.async || funcDecPath.node.generator) return;
|
|
348
|
-
|
|
349
|
-
if (funcDecPath.find((p) => !!(p.node as NodeSymbol)[SKIP])) return;
|
|
350
|
-
|
|
351
|
-
var program = getParentFunctionOrProgram(funcDecPath);
|
|
352
|
-
// Only top-level functions
|
|
353
|
-
if (!program.isProgram()) return;
|
|
354
|
-
|
|
355
|
-
// Check user's custom implementation
|
|
356
|
-
const functionName = getFunctionName(funcDecPath);
|
|
357
|
-
// Don't apply to the countermeasures function (Intended)
|
|
358
|
-
if (
|
|
359
|
-
me.options.lock.countermeasures &&
|
|
360
|
-
functionName === me.options.lock.countermeasures
|
|
361
|
-
)
|
|
362
|
-
return;
|
|
363
|
-
// Don't apply to invokeCountermeasures function (Intended)
|
|
364
|
-
if (me.obfuscator.isInternalVariable(functionName)) return;
|
|
365
|
-
|
|
366
|
-
if (
|
|
367
|
-
!me.computeProbabilityMap(me.options.lock.integrity, functionName)
|
|
368
|
-
)
|
|
369
|
-
return;
|
|
370
|
-
|
|
371
|
-
var newFnName = me.getPlaceholder();
|
|
372
|
-
var newFunctionDeclaration = t.functionDeclaration(
|
|
373
|
-
t.identifier(newFnName),
|
|
374
|
-
funcDecPath.node.params,
|
|
375
|
-
funcDecPath.node.body
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
// Clone semantic symbols like (UNSAFE, PREDICTABLE, MULTI_TRANSFORM, etc)
|
|
379
|
-
const source = funcDecPath.node;
|
|
380
|
-
Object.getOwnPropertySymbols(source).forEach((symbol) => {
|
|
381
|
-
newFunctionDeclaration[symbol] = source[symbol];
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
(newFunctionDeclaration as NodeSymbol)[SKIP] = true;
|
|
385
|
-
|
|
386
|
-
var [newFnPath] = program.unshiftContainer(
|
|
387
|
-
"body",
|
|
388
|
-
newFunctionDeclaration
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
// Function simply calls the new function
|
|
392
|
-
// In the case Integrity cannot transform the function, the original behavior is preserved
|
|
393
|
-
funcDecPath.node.body = t.blockStatement(
|
|
394
|
-
new Template(`
|
|
395
|
-
return ${newFnName}(...arguments);
|
|
396
|
-
`).compile(),
|
|
397
|
-
funcDecPath.node.body.directives
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
// Parameters no longer needed, using 'arguments' instead
|
|
401
|
-
funcDecPath.node.params = [];
|
|
402
|
-
|
|
403
|
-
// Mark the function as unsafe - use of 'arguments' is unsafe
|
|
404
|
-
(funcDecPath.node as NodeSymbol)[UNSAFE] = true;
|
|
405
|
-
|
|
406
|
-
// Params changed - function is no longer predictable
|
|
407
|
-
(funcDecPath.node as NodeSymbol)[PREDICTABLE] = false;
|
|
408
|
-
|
|
409
|
-
// Mark the function for integrity
|
|
410
|
-
(funcDecPath.node as NodeIntegrity)[INTEGRITY] = {
|
|
411
|
-
fnPath: newFnPath,
|
|
412
|
-
fnName: newFnName,
|
|
413
|
-
};
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
};
|
|
1
|
+
import { NodePath } from "@babel/traverse";
|
|
2
|
+
import { PluginArg, PluginObject } from "../plugin";
|
|
3
|
+
import { Order } from "../../order";
|
|
4
|
+
import { chance, choice } from "../../utils/random-utils";
|
|
5
|
+
import Template from "../../templates/template";
|
|
6
|
+
import * as t from "@babel/types";
|
|
7
|
+
import { CustomLock } from "../../options";
|
|
8
|
+
import {
|
|
9
|
+
getFunctionName,
|
|
10
|
+
getParentFunctionOrProgram,
|
|
11
|
+
isDefiningIdentifier,
|
|
12
|
+
isVariableIdentifier,
|
|
13
|
+
prependProgram,
|
|
14
|
+
} from "../../utils/ast-utils";
|
|
15
|
+
import { INTEGRITY, NodeIntegrity } from "./integrity";
|
|
16
|
+
import { HashTemplate } from "../../templates/integrityTemplate";
|
|
17
|
+
import {
|
|
18
|
+
MULTI_TRANSFORM,
|
|
19
|
+
NodeSymbol,
|
|
20
|
+
PREDICTABLE,
|
|
21
|
+
SKIP,
|
|
22
|
+
UNSAFE,
|
|
23
|
+
} from "../../constants";
|
|
24
|
+
import {
|
|
25
|
+
IndexOfTemplate,
|
|
26
|
+
NativeFunctionTemplate,
|
|
27
|
+
StrictModeTemplate,
|
|
28
|
+
} from "../../templates/tamperProtectionTemplates";
|
|
29
|
+
|
|
30
|
+
export default ({ Plugin }: PluginArg): PluginObject => {
|
|
31
|
+
const me = Plugin(Order.Lock, {
|
|
32
|
+
changeData: {
|
|
33
|
+
locksInserted: 0,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
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
|
+
|
|
43
|
+
me.options.lock.customLocks.push({
|
|
44
|
+
code: [
|
|
45
|
+
`
|
|
46
|
+
if(Date.now()<${me.options.lock.startDate.getTime()}) {
|
|
47
|
+
{countermeasures}
|
|
48
|
+
}
|
|
49
|
+
`,
|
|
50
|
+
`
|
|
51
|
+
if((new Date()).getTime()<${me.options.lock.startDate.getTime()}) {
|
|
52
|
+
{countermeasures}
|
|
53
|
+
}
|
|
54
|
+
`,
|
|
55
|
+
],
|
|
56
|
+
percentagePerBlock: 0.5,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
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
|
+
|
|
66
|
+
me.options.lock.customLocks.push({
|
|
67
|
+
code: [
|
|
68
|
+
`
|
|
69
|
+
if(Date.now()>${me.options.lock.endDate.getTime()}) {
|
|
70
|
+
{countermeasures}
|
|
71
|
+
}
|
|
72
|
+
`,
|
|
73
|
+
`
|
|
74
|
+
if((new Date()).getTime()>${me.options.lock.endDate.getTime()}) {
|
|
75
|
+
{countermeasures}
|
|
76
|
+
}
|
|
77
|
+
`,
|
|
78
|
+
],
|
|
79
|
+
percentagePerBlock: 0.5,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (me.options.lock.domainLock) {
|
|
84
|
+
var domainArray = Array.isArray(me.options.lock.domainLock)
|
|
85
|
+
? me.options.lock.domainLock
|
|
86
|
+
: [me.options.lock.domainLock];
|
|
87
|
+
|
|
88
|
+
for (const regexString of domainArray) {
|
|
89
|
+
me.options.lock.customLocks.push({
|
|
90
|
+
code: new Template(`
|
|
91
|
+
if(!new RegExp({regexString}).test(window.location.href)) {
|
|
92
|
+
{countermeasures}
|
|
93
|
+
}
|
|
94
|
+
`).setDefaultVariables({
|
|
95
|
+
regexString: () => t.stringLiteral(regexString.toString()),
|
|
96
|
+
}),
|
|
97
|
+
percentagePerBlock: 0.5,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (me.options.lock.selfDefending) {
|
|
103
|
+
me.options.lock.customLocks.push({
|
|
104
|
+
code: `
|
|
105
|
+
(
|
|
106
|
+
function(){
|
|
107
|
+
// Breaks any code formatter
|
|
108
|
+
var namedFunction = function(){
|
|
109
|
+
const test = function(){
|
|
110
|
+
const regExp= new RegExp('\\n');
|
|
111
|
+
return regExp['test'](namedFunction)
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
if(test()) {
|
|
115
|
+
{countermeasures}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return namedFunction();
|
|
120
|
+
}
|
|
121
|
+
)();
|
|
122
|
+
`,
|
|
123
|
+
percentagePerBlock: 0.5,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (me.options.lock.antiDebug) {
|
|
128
|
+
me.options.lock.customLocks.push({
|
|
129
|
+
code: `
|
|
130
|
+
debugger;
|
|
131
|
+
`,
|
|
132
|
+
percentagePerBlock: 0.5,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const timesMap = new WeakMap<CustomLock, number>();
|
|
137
|
+
|
|
138
|
+
let countermeasuresNode: NodePath<t.Identifier>;
|
|
139
|
+
let invokeCountermeasuresFnName: string;
|
|
140
|
+
|
|
141
|
+
if (me.options.lock.countermeasures) {
|
|
142
|
+
invokeCountermeasuresFnName = me.getPlaceholder("invokeCountermeasures");
|
|
143
|
+
|
|
144
|
+
me.globalState.internals.invokeCountermeasuresFnName =
|
|
145
|
+
invokeCountermeasuresFnName;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var createCountermeasuresCode = () => {
|
|
149
|
+
if (invokeCountermeasuresFnName) {
|
|
150
|
+
return new Template(`${invokeCountermeasuresFnName}()`).compile();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (me.options.lock.countermeasures === false) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return new Template(`while(true){}`).compile();
|
|
158
|
+
};
|
|
159
|
+
me.globalState.lock.createCountermeasuresCode = createCountermeasuresCode;
|
|
160
|
+
|
|
161
|
+
const defaultMaxCount = me.options.lock.defaultMaxCount ?? 25;
|
|
162
|
+
|
|
163
|
+
function applyLockToBlock(path: NodePath<t.Block>, customLock: CustomLock) {
|
|
164
|
+
let times = timesMap.get(customLock) || 0;
|
|
165
|
+
|
|
166
|
+
let maxCount = customLock.maxCount ?? defaultMaxCount; // 25 is default max count
|
|
167
|
+
let minCount = customLock.minCount ?? 1; // 1 is default min count
|
|
168
|
+
|
|
169
|
+
if (maxCount >= 0 && times > maxCount) {
|
|
170
|
+
// Limit creation, allowing -1 to disable the limit entirely
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// The Program always gets a lock
|
|
175
|
+
// Else based on the percentage
|
|
176
|
+
// Try to reach the minimum count
|
|
177
|
+
if (
|
|
178
|
+
!path.isProgram() &&
|
|
179
|
+
!chance(customLock.percentagePerBlock * 100) &&
|
|
180
|
+
times >= minCount
|
|
181
|
+
) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Increment the times
|
|
186
|
+
timesMap.set(customLock, times + 1);
|
|
187
|
+
|
|
188
|
+
const lockCode = Array.isArray(customLock.code)
|
|
189
|
+
? choice(customLock.code)
|
|
190
|
+
: customLock.code;
|
|
191
|
+
|
|
192
|
+
const template =
|
|
193
|
+
typeof lockCode === "string" ? new Template(lockCode) : lockCode;
|
|
194
|
+
const lockNodes = template.compile({
|
|
195
|
+
countermeasures: () => createCountermeasuresCode(),
|
|
196
|
+
});
|
|
197
|
+
var p = path.unshiftContainer("body", lockNodes);
|
|
198
|
+
p.forEach((p) => p.skip());
|
|
199
|
+
|
|
200
|
+
me.changeData.locksInserted++;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
visitor: {
|
|
205
|
+
BindingIdentifier(path) {
|
|
206
|
+
if (path.node.name !== me.options.lock.countermeasures) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Exclude labels
|
|
211
|
+
if (!isVariableIdentifier(path)) return;
|
|
212
|
+
|
|
213
|
+
if (!isDefiningIdentifier(path)) {
|
|
214
|
+
// Reassignments are not allowed
|
|
215
|
+
|
|
216
|
+
me.error("Countermeasures function cannot be reassigned");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (countermeasuresNode) {
|
|
220
|
+
// Disallow multiple countermeasures functions
|
|
221
|
+
|
|
222
|
+
me.error(
|
|
223
|
+
"Countermeasures function was already defined, it must have a unique name from the rest of your code"
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
path.scope.getBinding(path.node.name).scope !==
|
|
229
|
+
path.scope.getProgramParent()
|
|
230
|
+
) {
|
|
231
|
+
me.error(
|
|
232
|
+
"Countermeasures function must be defined at the global level"
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
countermeasuresNode = path;
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
Block: {
|
|
240
|
+
exit(path) {
|
|
241
|
+
var customLock = choice(me.options.lock.customLocks);
|
|
242
|
+
if (customLock) {
|
|
243
|
+
applyLockToBlock(path, customLock);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
Program: {
|
|
249
|
+
exit(path) {
|
|
250
|
+
// Insert nativeFunctionCheck
|
|
251
|
+
if (me.options.lock.tamperProtection) {
|
|
252
|
+
// Disallow strict mode
|
|
253
|
+
// Tamper Protection uses non-strict mode features:
|
|
254
|
+
// - eval() with local scope assignments
|
|
255
|
+
const directives = path.get("directives");
|
|
256
|
+
for (var directive of directives) {
|
|
257
|
+
if (directive.node.value.value === "use strict") {
|
|
258
|
+
me.error(
|
|
259
|
+
"Tamper Protection cannot be applied to code in strict mode. Disable strict mode by removing the 'use strict' directive, or disable Tamper Protection."
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
var nativeFunctionName =
|
|
265
|
+
me.getPlaceholder() + "_nativeFunctionCheck";
|
|
266
|
+
|
|
267
|
+
me.obfuscator.globalState.internals.nativeFunctionName =
|
|
268
|
+
nativeFunctionName;
|
|
269
|
+
|
|
270
|
+
// Ensure program is not in strict mode
|
|
271
|
+
// Tamper Protection forces non-strict mode
|
|
272
|
+
prependProgram(
|
|
273
|
+
path,
|
|
274
|
+
StrictModeTemplate.compile({
|
|
275
|
+
nativeFunctionName,
|
|
276
|
+
countermeasures: createCountermeasuresCode(),
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
const nativeFunctionDeclaration = NativeFunctionTemplate.single({
|
|
281
|
+
nativeFunctionName,
|
|
282
|
+
countermeasures: createCountermeasuresCode(),
|
|
283
|
+
IndexOfTemplate: IndexOfTemplate,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Checks function's toString() value for [native code] signature
|
|
287
|
+
prependProgram(path, nativeFunctionDeclaration);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Insert invokeCountermeasures function
|
|
291
|
+
if (invokeCountermeasuresFnName) {
|
|
292
|
+
if (!countermeasuresNode) {
|
|
293
|
+
me.error(
|
|
294
|
+
"Countermeasures function named '" +
|
|
295
|
+
me.options.lock.countermeasures +
|
|
296
|
+
"' was not found."
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
var hasInvoked = me.getPlaceholder("hasInvoked");
|
|
301
|
+
var statements = new Template(`
|
|
302
|
+
var ${hasInvoked} = false;
|
|
303
|
+
function ${invokeCountermeasuresFnName}(){
|
|
304
|
+
if(${hasInvoked}) return;
|
|
305
|
+
${hasInvoked} = true;
|
|
306
|
+
${me.options.lock.countermeasures}();
|
|
307
|
+
}
|
|
308
|
+
`)
|
|
309
|
+
.addSymbols(MULTI_TRANSFORM)
|
|
310
|
+
.compile();
|
|
311
|
+
|
|
312
|
+
prependProgram(path, statements).forEach((p) => p.skip());
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (me.options.lock.integrity) {
|
|
316
|
+
const hashFnName = me.getPlaceholder() + "_hash";
|
|
317
|
+
const imulFnName = me.getPlaceholder() + "_imul";
|
|
318
|
+
|
|
319
|
+
const { sensitivityRegex } = me.globalState.lock.integrity;
|
|
320
|
+
me.globalState.internals.integrityHashName = hashFnName;
|
|
321
|
+
|
|
322
|
+
const hashCode = HashTemplate.compile({
|
|
323
|
+
imul: imulFnName,
|
|
324
|
+
name: hashFnName,
|
|
325
|
+
hashingUtilFnName: me.getPlaceholder(),
|
|
326
|
+
sensitivityRegex: () =>
|
|
327
|
+
t.newExpression(t.identifier("RegExp"), [
|
|
328
|
+
t.stringLiteral(sensitivityRegex.source),
|
|
329
|
+
t.stringLiteral(sensitivityRegex.flags),
|
|
330
|
+
]),
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
prependProgram(path, hashCode);
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
// Integrity first pass
|
|
339
|
+
// Functions are prepared for Integrity by simply extracting the function body
|
|
340
|
+
// The extracted function is hashed in the 'integrity' plugin
|
|
341
|
+
FunctionDeclaration: {
|
|
342
|
+
exit(funcDecPath) {
|
|
343
|
+
if (!me.options.lock.integrity) return;
|
|
344
|
+
|
|
345
|
+
// Mark functions for integrity
|
|
346
|
+
// Don't apply to async or generator functions
|
|
347
|
+
if (funcDecPath.node.async || funcDecPath.node.generator) return;
|
|
348
|
+
|
|
349
|
+
if (funcDecPath.find((p) => !!(p.node as NodeSymbol)[SKIP])) return;
|
|
350
|
+
|
|
351
|
+
var program = getParentFunctionOrProgram(funcDecPath);
|
|
352
|
+
// Only top-level functions
|
|
353
|
+
if (!program.isProgram()) return;
|
|
354
|
+
|
|
355
|
+
// Check user's custom implementation
|
|
356
|
+
const functionName = getFunctionName(funcDecPath);
|
|
357
|
+
// Don't apply to the countermeasures function (Intended)
|
|
358
|
+
if (
|
|
359
|
+
me.options.lock.countermeasures &&
|
|
360
|
+
functionName === me.options.lock.countermeasures
|
|
361
|
+
)
|
|
362
|
+
return;
|
|
363
|
+
// Don't apply to invokeCountermeasures function (Intended)
|
|
364
|
+
if (me.obfuscator.isInternalVariable(functionName)) return;
|
|
365
|
+
|
|
366
|
+
if (
|
|
367
|
+
!me.computeProbabilityMap(me.options.lock.integrity, functionName)
|
|
368
|
+
)
|
|
369
|
+
return;
|
|
370
|
+
|
|
371
|
+
var newFnName = me.getPlaceholder();
|
|
372
|
+
var newFunctionDeclaration = t.functionDeclaration(
|
|
373
|
+
t.identifier(newFnName),
|
|
374
|
+
funcDecPath.node.params,
|
|
375
|
+
funcDecPath.node.body
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// Clone semantic symbols like (UNSAFE, PREDICTABLE, MULTI_TRANSFORM, etc)
|
|
379
|
+
const source = funcDecPath.node;
|
|
380
|
+
Object.getOwnPropertySymbols(source).forEach((symbol) => {
|
|
381
|
+
newFunctionDeclaration[symbol] = source[symbol];
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
(newFunctionDeclaration as NodeSymbol)[SKIP] = true;
|
|
385
|
+
|
|
386
|
+
var [newFnPath] = program.unshiftContainer(
|
|
387
|
+
"body",
|
|
388
|
+
newFunctionDeclaration
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
// Function simply calls the new function
|
|
392
|
+
// In the case Integrity cannot transform the function, the original behavior is preserved
|
|
393
|
+
funcDecPath.node.body = t.blockStatement(
|
|
394
|
+
new Template(`
|
|
395
|
+
return ${newFnName}(...arguments);
|
|
396
|
+
`).compile(),
|
|
397
|
+
funcDecPath.node.body.directives
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// Parameters no longer needed, using 'arguments' instead
|
|
401
|
+
funcDecPath.node.params = [];
|
|
402
|
+
|
|
403
|
+
// Mark the function as unsafe - use of 'arguments' is unsafe
|
|
404
|
+
(funcDecPath.node as NodeSymbol)[UNSAFE] = true;
|
|
405
|
+
|
|
406
|
+
// Params changed - function is no longer predictable
|
|
407
|
+
(funcDecPath.node as NodeSymbol)[PREDICTABLE] = false;
|
|
408
|
+
|
|
409
|
+
// Mark the function for integrity
|
|
410
|
+
(funcDecPath.node as NodeIntegrity)[INTEGRITY] = {
|
|
411
|
+
fnPath: newFnPath,
|
|
412
|
+
fnName: newFnName,
|
|
413
|
+
};
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
};
|