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.
Files changed (113) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +43 -43
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/workflows/node.js.yml +28 -28
  4. package/.prettierrc +4 -4
  5. package/CHANGELOG.md +1015 -987
  6. package/CODE_OF_CONDUCT.md +131 -131
  7. package/CONTRIBUTING.md +52 -52
  8. package/LICENSE +21 -21
  9. package/Migration.md +72 -72
  10. package/README.md +86 -86
  11. package/dist/constants.js +43 -43
  12. package/dist/index.js +14 -23
  13. package/dist/obfuscator.js +31 -25
  14. package/dist/order.js +4 -4
  15. package/dist/presets.js +31 -31
  16. package/dist/templates/integrityTemplate.js +4 -4
  17. package/dist/templates/template.js +1 -2
  18. package/dist/transforms/astScrambler.js +1 -2
  19. package/dist/transforms/calculator.js +1 -2
  20. package/dist/transforms/controlFlowFlattening.js +60 -41
  21. package/dist/transforms/deadCode.js +1 -2
  22. package/dist/transforms/dispatcher.js +4 -5
  23. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +1 -2
  24. package/dist/transforms/extraction/objectExtraction.js +1 -2
  25. package/dist/transforms/finalizer.js +1 -2
  26. package/dist/transforms/flatten.js +1 -2
  27. package/dist/transforms/identifier/globalConcealing.js +15 -2
  28. package/dist/transforms/identifier/movedDeclarations.js +8 -7
  29. package/dist/transforms/identifier/renameVariables.js +7 -7
  30. package/dist/transforms/lock/integrity.js +8 -9
  31. package/dist/transforms/lock/lock.js +1 -2
  32. package/dist/transforms/minify.js +11 -29
  33. package/dist/transforms/opaquePredicates.js +1 -2
  34. package/dist/transforms/pack.js +1 -2
  35. package/dist/transforms/plugin.js +18 -19
  36. package/dist/transforms/preparation.js +16 -16
  37. package/dist/transforms/renameLabels.js +1 -2
  38. package/dist/transforms/rgf.js +8 -9
  39. package/dist/transforms/shuffle.js +1 -2
  40. package/dist/transforms/string/encoding.js +1 -2
  41. package/dist/transforms/string/stringCompression.js +3 -4
  42. package/dist/transforms/string/stringConcealing.js +1 -2
  43. package/dist/transforms/string/stringEncoding.js +1 -2
  44. package/dist/transforms/variableMasking.js +1 -2
  45. package/dist/utils/NameGen.js +2 -2
  46. package/dist/utils/PredicateGen.js +1 -2
  47. package/dist/utils/ast-utils.js +87 -88
  48. package/dist/utils/function-utils.js +8 -8
  49. package/dist/utils/node.js +5 -6
  50. package/dist/utils/object-utils.js +4 -4
  51. package/dist/utils/random-utils.js +20 -20
  52. package/dist/utils/static-utils.js +1 -2
  53. package/dist/validateOptions.js +4 -7
  54. package/index.d.ts +17 -17
  55. package/package.json +61 -59
  56. package/src/constants.ts +168 -168
  57. package/src/index.ts +118 -118
  58. package/src/obfuscationResult.ts +49 -49
  59. package/src/obfuscator.ts +501 -497
  60. package/src/options.ts +407 -407
  61. package/src/order.ts +54 -54
  62. package/src/presets.ts +125 -125
  63. package/src/templates/bufferToStringTemplate.ts +57 -57
  64. package/src/templates/deadCodeTemplates.ts +1185 -1185
  65. package/src/templates/getGlobalTemplate.ts +76 -76
  66. package/src/templates/integrityTemplate.ts +64 -64
  67. package/src/templates/setFunctionLengthTemplate.ts +11 -11
  68. package/src/templates/stringCompressionTemplate.ts +20 -20
  69. package/src/templates/tamperProtectionTemplates.ts +120 -120
  70. package/src/templates/template.ts +224 -224
  71. package/src/transforms/astScrambler.ts +99 -99
  72. package/src/transforms/calculator.ts +99 -99
  73. package/src/transforms/controlFlowFlattening.ts +1716 -1680
  74. package/src/transforms/deadCode.ts +82 -82
  75. package/src/transforms/dispatcher.ts +450 -450
  76. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +156 -158
  77. package/src/transforms/extraction/objectExtraction.ts +186 -186
  78. package/src/transforms/finalizer.ts +74 -74
  79. package/src/transforms/flatten.ts +421 -418
  80. package/src/transforms/identifier/globalConcealing.ts +315 -295
  81. package/src/transforms/identifier/movedDeclarations.ts +252 -251
  82. package/src/transforms/identifier/renameVariables.ts +328 -321
  83. package/src/transforms/lock/integrity.ts +117 -117
  84. package/src/transforms/lock/lock.ts +418 -418
  85. package/src/transforms/minify.ts +615 -629
  86. package/src/transforms/opaquePredicates.ts +100 -100
  87. package/src/transforms/pack.ts +239 -239
  88. package/src/transforms/plugin.ts +173 -173
  89. package/src/transforms/preparation.ts +349 -347
  90. package/src/transforms/renameLabels.ts +175 -175
  91. package/src/transforms/rgf.ts +322 -322
  92. package/src/transforms/shuffle.ts +82 -82
  93. package/src/transforms/string/encoding.ts +144 -144
  94. package/src/transforms/string/stringCompression.ts +128 -128
  95. package/src/transforms/string/stringConcealing.ts +312 -312
  96. package/src/transforms/string/stringEncoding.ts +80 -80
  97. package/src/transforms/string/stringSplitting.ts +77 -77
  98. package/src/transforms/variableMasking.ts +257 -257
  99. package/src/utils/IntGen.ts +33 -33
  100. package/src/utils/NameGen.ts +116 -116
  101. package/src/utils/PredicateGen.ts +61 -61
  102. package/src/utils/ast-utils.ts +663 -663
  103. package/src/utils/function-utils.ts +50 -50
  104. package/src/utils/gen-utils.ts +48 -48
  105. package/src/utils/node.ts +78 -78
  106. package/src/utils/object-utils.ts +21 -21
  107. package/src/utils/random-utils.ts +93 -93
  108. package/src/utils/static-utils.ts +66 -66
  109. package/src/validateOptions.ts +256 -259
  110. package/tsconfig.json +13 -14
  111. package/dist/probability.js +0 -1
  112. package/dist/transforms/functionOutlining.js +0 -230
  113. 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
+ };