js-confuser 1.7.2 → 2.0.0-alpha.0

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 (263) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
  2. package/.github/workflows/node.js.yml +1 -1
  3. package/CHANGELOG.md +105 -0
  4. package/Migration.md +57 -0
  5. package/README.md +23 -913
  6. package/dist/constants.js +69 -13
  7. package/dist/index.js +108 -152
  8. package/dist/obfuscator.js +316 -118
  9. package/dist/options.js +1 -109
  10. package/dist/order.js +30 -30
  11. package/dist/presets.js +47 -45
  12. package/dist/probability.js +25 -32
  13. package/dist/templates/bufferToStringTemplate.js +9 -0
  14. package/dist/templates/deadCodeTemplates.js +9 -0
  15. package/dist/templates/getGlobalTemplate.js +19 -0
  16. package/dist/templates/integrityTemplate.js +30 -0
  17. package/dist/templates/setFunctionLengthTemplate.js +9 -0
  18. package/dist/templates/stringCompressionTemplate.js +10 -0
  19. package/dist/templates/tamperProtectionTemplates.js +21 -0
  20. package/dist/templates/template.js +213 -93
  21. package/dist/transforms/astScrambler.js +100 -0
  22. package/dist/transforms/calculator.js +70 -127
  23. package/dist/transforms/controlFlowFlattening.js +1182 -0
  24. package/dist/transforms/deadCode.js +62 -577
  25. package/dist/transforms/dispatcher.js +300 -309
  26. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
  27. package/dist/transforms/extraction/objectExtraction.js +131 -215
  28. package/dist/transforms/finalizer.js +56 -59
  29. package/dist/transforms/flatten.js +275 -276
  30. package/dist/transforms/functionOutlining.js +230 -0
  31. package/dist/transforms/identifier/globalConcealing.js +217 -103
  32. package/dist/transforms/identifier/movedDeclarations.js +167 -91
  33. package/dist/transforms/identifier/renameVariables.js +240 -187
  34. package/dist/transforms/lock/integrity.js +61 -184
  35. package/dist/transforms/lock/lock.js +263 -303
  36. package/dist/transforms/minify.js +431 -436
  37. package/dist/transforms/opaquePredicates.js +65 -118
  38. package/dist/transforms/pack.js +160 -0
  39. package/dist/transforms/plugin.js +179 -0
  40. package/dist/transforms/preparation.js +263 -163
  41. package/dist/transforms/renameLabels.js +132 -56
  42. package/dist/transforms/rgf.js +142 -240
  43. package/dist/transforms/shuffle.js +52 -145
  44. package/dist/transforms/string/encoding.js +45 -173
  45. package/dist/transforms/string/stringCompression.js +81 -126
  46. package/dist/transforms/string/stringConcealing.js +189 -224
  47. package/dist/transforms/string/stringEncoding.js +32 -40
  48. package/dist/transforms/string/stringSplitting.js +54 -55
  49. package/dist/transforms/variableMasking.js +232 -0
  50. package/dist/utils/ControlObject.js +125 -0
  51. package/dist/utils/IntGen.js +46 -0
  52. package/dist/utils/NameGen.js +106 -0
  53. package/dist/utils/ast-utils.js +560 -0
  54. package/dist/utils/function-utils.js +56 -0
  55. package/dist/utils/gen-utils.js +48 -0
  56. package/dist/utils/node.js +77 -0
  57. package/dist/utils/object-utils.js +21 -0
  58. package/dist/utils/random-utils.js +91 -0
  59. package/dist/utils/static-utils.js +64 -0
  60. package/dist/validateOptions.js +122 -0
  61. package/index.d.ts +1 -17
  62. package/package.json +27 -22
  63. package/src/constants.ts +139 -77
  64. package/src/index.ts +70 -163
  65. package/src/obfuscationResult.ts +43 -0
  66. package/src/obfuscator.ts +328 -135
  67. package/src/options.ts +154 -623
  68. package/src/order.ts +14 -14
  69. package/src/presets.ts +39 -34
  70. package/src/probability.ts +21 -36
  71. package/src/templates/{bufferToString.ts → bufferToStringTemplate.ts} +5 -54
  72. package/src/templates/deadCodeTemplates.ts +1185 -0
  73. package/src/templates/getGlobalTemplate.ts +72 -0
  74. package/src/templates/integrityTemplate.ts +69 -0
  75. package/src/templates/setFunctionLengthTemplate.ts +11 -0
  76. package/src/templates/stringCompressionTemplate.ts +42 -0
  77. package/src/templates/tamperProtectionTemplates.ts +116 -0
  78. package/src/templates/template.ts +183 -92
  79. package/src/transforms/astScrambler.ts +99 -0
  80. package/src/transforms/calculator.ts +96 -224
  81. package/src/transforms/controlFlowFlattening.ts +1594 -0
  82. package/src/transforms/deadCode.ts +85 -628
  83. package/src/transforms/dispatcher.ts +431 -636
  84. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -299
  85. package/src/transforms/extraction/objectExtraction.ts +160 -333
  86. package/src/transforms/finalizer.ts +63 -64
  87. package/src/transforms/flatten.ts +439 -557
  88. package/src/transforms/functionOutlining.ts +225 -0
  89. package/src/transforms/identifier/globalConcealing.ts +261 -189
  90. package/src/transforms/identifier/movedDeclarations.ts +228 -142
  91. package/src/transforms/identifier/renameVariables.ts +252 -258
  92. package/src/transforms/lock/integrity.ts +84 -260
  93. package/src/transforms/lock/lock.ts +342 -491
  94. package/src/transforms/minify.ts +523 -663
  95. package/src/transforms/opaquePredicates.ts +90 -229
  96. package/src/transforms/pack.ts +195 -0
  97. package/src/transforms/plugin.ts +185 -0
  98. package/src/transforms/preparation.ts +337 -215
  99. package/src/transforms/renameLabels.ts +176 -77
  100. package/src/transforms/rgf.ts +293 -386
  101. package/src/transforms/shuffle.ts +80 -254
  102. package/src/transforms/string/encoding.ts +26 -129
  103. package/src/transforms/string/stringCompression.ts +118 -236
  104. package/src/transforms/string/stringConcealing.ts +255 -339
  105. package/src/transforms/string/stringEncoding.ts +28 -47
  106. package/src/transforms/string/stringSplitting.ts +61 -75
  107. package/src/transforms/variableMasking.ts +257 -0
  108. package/src/utils/ControlObject.ts +141 -0
  109. package/src/utils/IntGen.ts +33 -0
  110. package/src/utils/NameGen.ts +106 -0
  111. package/src/utils/ast-utils.ts +667 -0
  112. package/src/utils/function-utils.ts +50 -0
  113. package/src/utils/gen-utils.ts +48 -0
  114. package/src/utils/node.ts +78 -0
  115. package/src/utils/object-utils.ts +21 -0
  116. package/src/utils/random-utils.ts +79 -0
  117. package/src/utils/static-utils.ts +66 -0
  118. package/src/validateOptions.ts +256 -0
  119. package/tsconfig.json +13 -8
  120. package/babel.config.js +0 -12
  121. package/dev.js +0 -8
  122. package/dist/compiler.js +0 -34
  123. package/dist/parser.js +0 -59
  124. package/dist/precedence.js +0 -66
  125. package/dist/templates/bufferToString.js +0 -108
  126. package/dist/templates/crash.js +0 -59
  127. package/dist/templates/es5.js +0 -137
  128. package/dist/templates/functionLength.js +0 -34
  129. package/dist/templates/globals.js +0 -9
  130. package/dist/transforms/antiTooling.js +0 -88
  131. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +0 -1281
  132. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +0 -131
  133. package/dist/transforms/es5/antiClass.js +0 -164
  134. package/dist/transforms/es5/antiDestructuring.js +0 -193
  135. package/dist/transforms/es5/antiES6Object.js +0 -185
  136. package/dist/transforms/es5/antiSpreadOperator.js +0 -35
  137. package/dist/transforms/es5/antiTemplate.js +0 -66
  138. package/dist/transforms/es5/es5.js +0 -123
  139. package/dist/transforms/extraction/classExtraction.js +0 -83
  140. package/dist/transforms/identifier/globalAnalysis.js +0 -70
  141. package/dist/transforms/identifier/variableAnalysis.js +0 -104
  142. package/dist/transforms/lock/antiDebug.js +0 -76
  143. package/dist/transforms/stack.js +0 -343
  144. package/dist/transforms/transform.js +0 -350
  145. package/dist/traverse.js +0 -110
  146. package/dist/util/compare.js +0 -145
  147. package/dist/util/gen.js +0 -564
  148. package/dist/util/guard.js +0 -9
  149. package/dist/util/identifiers.js +0 -355
  150. package/dist/util/insert.js +0 -362
  151. package/dist/util/math.js +0 -19
  152. package/dist/util/object.js +0 -40
  153. package/dist/util/random.js +0 -130
  154. package/dist/util/scope.js +0 -20
  155. package/docs/ControlFlowFlattening.md +0 -595
  156. package/docs/Countermeasures.md +0 -63
  157. package/docs/ES5.md +0 -197
  158. package/docs/Integrity.md +0 -75
  159. package/docs/RGF.md +0 -419
  160. package/samples/example.js +0 -15
  161. package/samples/high.js +0 -1
  162. package/samples/input.js +0 -3
  163. package/samples/javascriptobfuscator.com.js +0 -8
  164. package/samples/jscrambler_advanced.js +0 -1894
  165. package/samples/jscrambler_light.js +0 -1134
  166. package/samples/low.js +0 -1
  167. package/samples/medium.js +0 -1
  168. package/samples/obfuscator.io.js +0 -1686
  169. package/samples/preemptive.com.js +0 -16
  170. package/src/compiler.ts +0 -35
  171. package/src/parser.ts +0 -49
  172. package/src/precedence.ts +0 -61
  173. package/src/templates/crash.ts +0 -55
  174. package/src/templates/es5.ts +0 -131
  175. package/src/templates/functionLength.ts +0 -32
  176. package/src/templates/globals.ts +0 -3
  177. package/src/transforms/antiTooling.ts +0 -102
  178. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2146
  179. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
  180. package/src/transforms/es5/antiClass.ts +0 -272
  181. package/src/transforms/es5/antiDestructuring.ts +0 -294
  182. package/src/transforms/es5/antiES6Object.ts +0 -267
  183. package/src/transforms/es5/antiSpreadOperator.ts +0 -56
  184. package/src/transforms/es5/antiTemplate.ts +0 -98
  185. package/src/transforms/es5/es5.ts +0 -149
  186. package/src/transforms/extraction/classExtraction.ts +0 -168
  187. package/src/transforms/identifier/globalAnalysis.ts +0 -85
  188. package/src/transforms/identifier/variableAnalysis.ts +0 -118
  189. package/src/transforms/lock/antiDebug.ts +0 -112
  190. package/src/transforms/stack.ts +0 -551
  191. package/src/transforms/transform.ts +0 -453
  192. package/src/traverse.ts +0 -120
  193. package/src/types.ts +0 -131
  194. package/src/util/compare.ts +0 -181
  195. package/src/util/gen.ts +0 -651
  196. package/src/util/guard.ts +0 -7
  197. package/src/util/identifiers.ts +0 -494
  198. package/src/util/insert.ts +0 -419
  199. package/src/util/math.ts +0 -15
  200. package/src/util/object.ts +0 -39
  201. package/src/util/random.ts +0 -141
  202. package/src/util/scope.ts +0 -21
  203. package/test/code/Cash.src.js +0 -1011
  204. package/test/code/Cash.test.ts +0 -49
  205. package/test/code/Dynamic.src.js +0 -118
  206. package/test/code/Dynamic.test.ts +0 -49
  207. package/test/code/ES6.src.js +0 -235
  208. package/test/code/ES6.test.ts +0 -42
  209. package/test/code/NewFeatures.test.ts +0 -19
  210. package/test/code/StrictMode.src.js +0 -65
  211. package/test/code/StrictMode.test.js +0 -37
  212. package/test/compare.test.ts +0 -104
  213. package/test/index.test.ts +0 -249
  214. package/test/options.test.ts +0 -132
  215. package/test/presets.test.ts +0 -22
  216. package/test/probability.test.ts +0 -44
  217. package/test/templates/template.test.ts +0 -14
  218. package/test/transforms/antiTooling.test.ts +0 -52
  219. package/test/transforms/calculator.test.ts +0 -78
  220. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
  221. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
  222. package/test/transforms/deadCode.test.ts +0 -85
  223. package/test/transforms/dispatcher.test.ts +0 -457
  224. package/test/transforms/es5/antiClass.test.ts +0 -427
  225. package/test/transforms/es5/antiDestructuring.test.ts +0 -157
  226. package/test/transforms/es5/antiES6Object.test.ts +0 -245
  227. package/test/transforms/es5/antiTemplate.test.ts +0 -116
  228. package/test/transforms/es5/es5.test.ts +0 -110
  229. package/test/transforms/extraction/classExtraction.test.ts +0 -86
  230. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
  231. package/test/transforms/extraction/objectExtraction.test.ts +0 -491
  232. package/test/transforms/flatten.test.ts +0 -721
  233. package/test/transforms/hexadecimalNumbers.test.ts +0 -62
  234. package/test/transforms/identifier/globalConcealing.test.ts +0 -72
  235. package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
  236. package/test/transforms/identifier/renameVariables.test.ts +0 -621
  237. package/test/transforms/lock/antiDebug.test.ts +0 -66
  238. package/test/transforms/lock/browserLock.test.ts +0 -129
  239. package/test/transforms/lock/countermeasures.test.ts +0 -100
  240. package/test/transforms/lock/integrity.test.ts +0 -161
  241. package/test/transforms/lock/lock.test.ts +0 -204
  242. package/test/transforms/lock/osLock.test.ts +0 -312
  243. package/test/transforms/lock/selfDefending.test.ts +0 -68
  244. package/test/transforms/minify.test.ts +0 -575
  245. package/test/transforms/opaquePredicates.test.ts +0 -43
  246. package/test/transforms/preparation.test.ts +0 -157
  247. package/test/transforms/renameLabels.test.ts +0 -95
  248. package/test/transforms/rgf.test.ts +0 -378
  249. package/test/transforms/shuffle.test.ts +0 -135
  250. package/test/transforms/stack.test.ts +0 -573
  251. package/test/transforms/string/stringCompression.test.ts +0 -120
  252. package/test/transforms/string/stringConcealing.test.ts +0 -299
  253. package/test/transforms/string/stringEncoding.test.ts +0 -95
  254. package/test/transforms/string/stringSplitting.test.ts +0 -135
  255. package/test/transforms/transform.test.ts +0 -66
  256. package/test/traverse.test.ts +0 -139
  257. package/test/util/compare.test.ts +0 -34
  258. package/test/util/gen.test.ts +0 -121
  259. package/test/util/identifiers.test.ts +0 -253
  260. package/test/util/insert.test.ts +0 -142
  261. package/test/util/math.test.ts +0 -5
  262. package/test/util/random.test.ts +0 -71
  263. /package/dist/{types.js → obfuscationResult.js} +0 -0
@@ -1,1281 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _assert = require("assert");
8
- var _order = require("../../order");
9
- var _probability = require("../../probability");
10
- var _template = _interopRequireDefault(require("../../templates/template"));
11
- var _traverse = require("../../traverse");
12
- var _gen = require("../../util/gen");
13
- var _identifiers = require("../../util/identifiers");
14
- var _insert = require("../../util/insert");
15
- var _random = require("../../util/random");
16
- var _transform = _interopRequireDefault(require("../transform"));
17
- var _expressionObfuscation = _interopRequireDefault(require("./expressionObfuscation"));
18
- var _constants = require("../../constants");
19
- var _compare = require("../../util/compare");
20
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
22
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
23
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
24
- const flattenStructures = new Set(["IfStatement", "ForStatement", "WhileStatement", "DoWhileStatement"]);
25
-
26
- /**
27
- * A chunk represents a small segment of code
28
- */
29
-
30
- /**
31
- * Breaks functions into DAGs (Directed Acyclic Graphs)
32
- *
33
- * - 1. Break functions into chunks
34
- * - 2. Shuffle chunks but remember their original position
35
- * - 3. Create a Switch statement inside a While loop, each case is a chunk, and the while loops exits on the last transition.
36
- *
37
- * The Switch statement:
38
- *
39
- * - 1. The state variable controls which case will run next
40
- * - 2. At the end of each case, the state variable is updated to the next block of code.
41
- * - 3. The while loop continues until the the state variable is the end state.
42
- */
43
- class ControlFlowFlattening extends _transform.default {
44
- constructor(o) {
45
- super(o, _order.ObfuscateOrder.ControlFlowFlattening);
46
- // in Debug mode, the output is much easier to read
47
- _defineProperty(this, "isDebug", false);
48
- _defineProperty(this, "flattenControlStructures", true);
49
- // Flatten if-statements, for-loops, etc
50
- _defineProperty(this, "addToControlObject", true);
51
- // var control = { str1, num1 }
52
- _defineProperty(this, "mangleNumberLiterals", true);
53
- // 50 => state + X
54
- _defineProperty(this, "mangleBooleanLiterals", true);
55
- // true => state == X
56
- _defineProperty(this, "mangleIdentifiers", true);
57
- // console => (state == X ? console : _)
58
- _defineProperty(this, "outlineStatements", true);
59
- // Tries to outline entire chunks
60
- _defineProperty(this, "outlineExpressions", true);
61
- // Tries to outline expressions found in chunks
62
- _defineProperty(this, "addComplexTest", true);
63
- // case s != 49 && s - 10:
64
- _defineProperty(this, "addFakeTest", true);
65
- // case 100: case 490: case 510: ...
66
- _defineProperty(this, "addDeadCode", true);
67
- // add fakes chunks of code
68
- _defineProperty(this, "addOpaquePredicates", true);
69
- // predicate ? REAL : FAKE
70
- _defineProperty(this, "addFlaggedLabels", true);
71
- // s=NEXT_STATE,flag=true,break
72
- // Limit amount of mangling
73
- _defineProperty(this, "mangledExpressionsMade", 0);
74
- // Amount of blocks changed by Control Flow Flattening
75
- _defineProperty(this, "cffCount", 0);
76
- if (!this.isDebug) {
77
- this.before.push(new _expressionObfuscation.default(o));
78
- } else {
79
- console.warn("Debug mode enabled");
80
- }
81
- }
82
- match(object, parents) {
83
- return (0, _traverse.isBlock)(object) && (!parents[0] || !flattenStructures.has(parents[0].type)) && (!parents[1] || !flattenStructures.has(parents[1].type));
84
- }
85
- transform(object, parents) {
86
- var _this = this;
87
- // Must be at least 3 statements or more
88
- if (object.body.length < 3) {
89
- return;
90
- }
91
- // No 'let'/'const' allowed (These won't work in Switch cases!)
92
- if ((0, _identifiers.containsLexicallyBoundVariables)(object, parents)) {
93
- return;
94
- }
95
- // Check user's threshold setting
96
- if (!(0, _probability.ComputeProbabilityMap)(this.options.controlFlowFlattening, x => x)) {
97
- return;
98
- }
99
- var objectBody = (0, _insert.getBlockBody)(object.body);
100
- if (!objectBody.length) {
101
- return;
102
- }
103
-
104
- // Purely for naming purposes
105
- var cffIndex = this.cffCount++;
106
-
107
- // The controlVar is an object containing:
108
- // - Strings found in chunks
109
- // - Numbers found in chunks
110
- // - Helper functions to adjust the state
111
- // - Outlined expressions changed into functions
112
- var controlVar = this.getPlaceholder() + `_c${cffIndex}_CONTROL`;
113
- var controlProperties = [];
114
- var controlConstantMap = new Map();
115
- var controlGen = this.getGenerator("mangled");
116
- var controlTestKey = controlGen.generate();
117
-
118
- // This 'controlVar' can be accessed by child-nodes
119
- object.$controlVar = controlVar;
120
- object.$controlConstantMap = controlConstantMap;
121
- object.$controlProperties = controlProperties;
122
- object.$controlGen = controlGen;
123
- return () => {
124
- (0, _assert.ok)(Array.isArray(objectBody));
125
-
126
- // The state variable names (and quantity)
127
- var stateVars = Array(this.isDebug ? 1 : (0, _random.getRandomInteger)(2, 5)).fill(0).map((_, i) => this.getPlaceholder() + `_c${cffIndex}_S${i}`);
128
-
129
- // How often should chunks be split up?
130
- // Percentage between 10% and 90% based on block size
131
- var splitPercent = Math.max(10, 90 - objectBody.length * 5);
132
-
133
- // Find functions and import declarations
134
- var importDeclarations = [];
135
- var functionDeclarationNames = new Set();
136
- var functionDeclarationValues = new Map();
137
-
138
- // Find all parent control-nodes
139
- const allControlNodes = [object];
140
- parents.filter(x => x.$controlVar).forEach(node => allControlNodes.push(node));
141
- const addControlMapConstant = literalValue => {
142
- var _selectedControlConst;
143
- // Choose a random control node to add to
144
- var controlNode = (0, _random.choice)(allControlNodes);
145
- var selectedControlVar = controlNode.$controlVar;
146
- var selectedControlConstantMap = controlNode.$controlConstantMap;
147
- var selectedControlProperties = controlNode.$controlProperties;
148
- var key = (_selectedControlConst = selectedControlConstantMap.get(literalValue)) === null || _selectedControlConst === void 0 ? void 0 : _selectedControlConst.key;
149
-
150
- // Not found, create
151
- if (!key) {
152
- key = controlNode.$controlGen.generate();
153
- selectedControlConstantMap.set(literalValue, {
154
- key: key
155
- });
156
- selectedControlProperties.push((0, _gen.Property)((0, _gen.Literal)(key), (0, _gen.Literal)(literalValue), false));
157
- }
158
- return getControlMember(key, selectedControlVar);
159
- };
160
-
161
- // Helper function to easily make control object accessors
162
- const getControlMember = function (key) {
163
- let objectName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : controlVar;
164
- return (0, _gen.MemberExpression)((0, _gen.Identifier)(objectName), (0, _gen.Literal)(key), true);
165
- };
166
-
167
- // This function recursively calls itself to flatten and split up code into 'chunks'
168
- const flattenBody = (body, startingLabel) => {
169
- var chunks = [];
170
- var currentBody = [];
171
- var currentLabel = startingLabel;
172
-
173
- // This function ends the current chunk being created ('currentBody')
174
- const finishCurrentChunk = function (pointingLabel, newLabel) {
175
- let addGotoStatement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
176
- if (!newLabel) {
177
- newLabel = _this.getPlaceholder();
178
- }
179
- if (!pointingLabel) {
180
- pointingLabel = newLabel;
181
- }
182
- if (addGotoStatement) {
183
- currentBody.push({
184
- type: "GotoStatement",
185
- label: pointingLabel
186
- });
187
- }
188
- chunks.push({
189
- label: currentLabel,
190
- body: [...currentBody]
191
- });
192
-
193
- // Random chance of this chunk being flagged (First label cannot be flagged)
194
- if (!_this.isDebug && _this.addFlaggedLabels && currentLabel !== startLabel && (0, _random.chance)(25)) {
195
- flaggedLabels[currentLabel] = {
196
- flagKey: controlGen.generate(),
197
- flagValue: (0, _random.choice)([true, false])
198
- };
199
- }
200
- (0, _traverse.walk)(currentBody, [], (o, p) => {
201
- if (o.type === "Literal" && !_this.isDebug) {
202
- // Add strings to the control object
203
- if (_this.addToControlObject && typeof o.value === "string" && o.value.length >= 3 && o.value.length <= 100 && !(0, _compare.isModuleSource)(o, p) && !(0, _compare.isDirective)(o, p) && !o.regex && (0, _random.chance)(50 - controlConstantMap.size - _this.mangledExpressionsMade / 100)) {
204
- return () => {
205
- _this.replaceIdentifierOrLiteral(o, addControlMapConstant(o.value), p);
206
- };
207
- }
208
-
209
- // Add numbers to the control object
210
- if (_this.addToControlObject && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100_000 && (0, _random.chance)(50 - controlConstantMap.size - _this.mangledExpressionsMade / 100)) {
211
- return () => {
212
- _this.replaceIdentifierOrLiteral(o, addControlMapConstant(o.value), p);
213
- };
214
- }
215
- }
216
- });
217
- currentLabel = newLabel;
218
- currentBody = [];
219
- };
220
- if (body !== objectBody) {
221
- // This code is nested. Move function declarations up
222
-
223
- var newBody = [];
224
- for (var stmt of body) {
225
- if (stmt.type === "FunctionDeclaration") {
226
- newBody.unshift(stmt);
227
- } else {
228
- newBody.push(stmt);
229
- }
230
- }
231
- body = newBody;
232
- }
233
- body.forEach((stmt, i) => {
234
- if (stmt.type === "ImportDeclaration") {
235
- // The 'importDeclarations' hold statements that are required to be left untouched at the top of the block
236
- importDeclarations.push(stmt);
237
- return;
238
- } else if (stmt.type === "FunctionDeclaration") {
239
- var functionName = stmt.id.name;
240
- stmt.type = "FunctionExpression";
241
- stmt.id = null;
242
- functionDeclarationNames.add(functionName);
243
- if (objectBody === body) {
244
- functionDeclarationValues.set(functionName, stmt);
245
- return;
246
- } else {
247
- currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(functionName), stmt)));
248
- }
249
- return;
250
- } else if (stmt.directive) {
251
- if (objectBody === body) {
252
- importDeclarations.push(stmt);
253
- } else {
254
- this.error(new Error("Unimplemented directive support."));
255
- }
256
- return;
257
- }
258
- if (stmt.type == "GotoStatement" && i !== body.length - 1) {
259
- finishCurrentChunk(stmt.label);
260
- return;
261
- }
262
-
263
- // The Preparation transform adds labels to every Control-Flow node
264
- if (this.flattenControlStructures && stmt.type == "LabeledStatement") {
265
- var lbl = stmt.label.name;
266
- var control = stmt.body;
267
- var isSwitchStatement = control.type === "SwitchStatement";
268
- if (isSwitchStatement || (control.type == "ForStatement" || control.type == "WhileStatement" || control.type == "DoWhileStatement") && control.body.type == "BlockStatement") {
269
- if (isSwitchStatement) {
270
- if (control.cases.length == 0) {
271
- currentBody.push(stmt);
272
- return;
273
- }
274
- }
275
- var isLoop = !isSwitchStatement;
276
- var supportContinueStatement = isLoop;
277
- var testPath = this.getPlaceholder();
278
- var updatePath = this.getPlaceholder();
279
- var bodyPath = this.getPlaceholder();
280
- var afterPath = this.getPlaceholder();
281
- var possible = true;
282
- var toReplace = [];
283
-
284
- // Find all break; and continue; statements and change them into 'GotoStatement's
285
- (0, _traverse.walk)(control.body || control.cases, [], (o, p) => {
286
- if (o.type === "BreakStatement" || o.type === "ContinueStatement") {
287
- var allowedLabels = new Set(p.filter(x => x.type === "LabeledStatement" && x.body.type === "SwitchStatement").map(x => x.label.name));
288
- var isUnsupportedContinue = !supportContinueStatement && o.type === "ContinueStatement";
289
- var isInvalidLabel = !o.label || o.label.name !== lbl && !allowedLabels.has(o.label.name);
290
-
291
- // This seems like the best solution:
292
- if (isUnsupportedContinue || isInvalidLabel) {
293
- possible = false;
294
- return "EXIT";
295
- }
296
- if (o.label.name === lbl) {
297
- return () => {
298
- toReplace.push([o, {
299
- type: "GotoStatement",
300
- label: o.type == "BreakStatement" ? afterPath : updatePath
301
- }]);
302
- };
303
- }
304
- }
305
- });
306
- if (!possible) {
307
- currentBody.push(stmt);
308
- return;
309
- }
310
- toReplace.forEach(v => this.replace(v[0], v[1]));
311
- if (isSwitchStatement) {
312
- var switchDiscriminantName = this.getPlaceholder() + "_switchD"; // Stores the value of the discriminant
313
- var switchTestName = this.getPlaceholder() + "_switchT"; // Set to true when a Switch case is matched
314
-
315
- currentBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(switchDiscriminantName, control.discriminant)));
316
- currentBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(switchTestName, (0, _gen.Literal)(false))));
317
-
318
- // case labels are:
319
- // `${caseLabelPrefix}_test_${index}`
320
- // `${caseLabelPrefix}_entry_${index}`
321
- var caseLabelPrefix = this.getPlaceholder();
322
- var defaultCaseIndex = control.cases.findIndex(x => x.test === null);
323
- control.cases.forEach((switchCase, i) => {
324
- var testPath = caseLabelPrefix + "_test_" + i;
325
- var entryPath = caseLabelPrefix + "_entry_" + i;
326
- var nextEntryPath = i === control.cases.length - 1 // Last path goes to afterPath
327
- ? afterPath // Else go to next entry path (fall-through behavior)
328
- : caseLabelPrefix + "_entry_" + (i + 1);
329
- var nextTestPath = i === control.cases.length - 1 ? afterPath : caseLabelPrefix + "_test_" + (i + 1);
330
- finishCurrentChunk(testPath, testPath, i == 0);
331
- if (switchCase.test) {
332
- // Check the case condition and goto statement
333
- currentBody.push((0, _gen.IfStatement)((0, _gen.BinaryExpression)("===", (0, _gen.Identifier)(switchDiscriminantName), switchCase.test), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(switchTestName), (0, _gen.Literal)(true))), {
334
- type: "GotoStatement",
335
- label: entryPath
336
- }]));
337
- } else {
338
- // Default case: No test needed.
339
- }
340
-
341
- // If default case, on last test, if no case was matched, goto default case
342
- if (i === control.cases.length - 1 && defaultCaseIndex !== -1) {
343
- currentBody.push((0, _gen.IfStatement)((0, _gen.UnaryExpression)("!", (0, _gen.Identifier)(switchTestName)), [{
344
- type: "GotoStatement",
345
- label: caseLabelPrefix + "_entry_" + defaultCaseIndex
346
- }]));
347
- }
348
-
349
- // Jump to next test
350
- currentBody.push({
351
- type: "GotoStatement",
352
- label: nextTestPath
353
- });
354
- chunks.push(...flattenBody([...switchCase.consequent, {
355
- type: "GotoStatement",
356
- label: nextEntryPath
357
- }], entryPath));
358
- });
359
- finishCurrentChunk(afterPath, afterPath, false);
360
- return;
361
- } else if (isLoop) {
362
- var isPostTest = control.type == "DoWhileStatement";
363
-
364
- // add initializing section to current chunk
365
- if (control.init) {
366
- if (control.init.type == "VariableDeclaration") {
367
- currentBody.push(control.init);
368
- } else {
369
- currentBody.push((0, _gen.ExpressionStatement)(control.init));
370
- }
371
- }
372
-
373
- // create new label called `testPath` and have current chunk point to it (goto testPath)
374
- finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
375
- currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(controlTestKey), control.test || (0, _gen.Literal)(true))));
376
- finishCurrentChunk();
377
- currentBody.push((0, _gen.IfStatement)(getControlMember(controlTestKey), [{
378
- type: "GotoStatement",
379
- label: bodyPath
380
- }]));
381
-
382
- // create new label called `bodyPath` and have test body point to afterPath (goto afterPath)
383
- finishCurrentChunk(afterPath, bodyPath);
384
- var innerBothPath = this.getPlaceholder();
385
- chunks.push(...flattenBody([...control.body.body, {
386
- type: "GotoStatement",
387
- label: updatePath
388
- }], innerBothPath));
389
- finishCurrentChunk(innerBothPath, updatePath);
390
- if (control.update) {
391
- currentBody.push((0, _gen.ExpressionStatement)(control.update));
392
- }
393
- finishCurrentChunk(testPath, afterPath);
394
- return;
395
- }
396
- }
397
- }
398
- if (this.flattenControlStructures && stmt.type == "IfStatement" && stmt.consequent.type == "BlockStatement" && (!stmt.alternate || stmt.alternate.type == "BlockStatement")) {
399
- finishCurrentChunk();
400
- currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(controlTestKey), stmt.test)));
401
- finishCurrentChunk();
402
- var hasAlternate = !!stmt.alternate;
403
- (0, _assert.ok)(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
404
- var yesPath = this.getPlaceholder();
405
- var noPath = this.getPlaceholder();
406
- var afterPath = this.getPlaceholder();
407
- currentBody.push((0, _gen.IfStatement)(getControlMember(controlTestKey), [{
408
- type: "GotoStatement",
409
- label: yesPath
410
- }]));
411
- chunks.push(...flattenBody([...stmt.consequent.body, {
412
- type: "GotoStatement",
413
- label: afterPath
414
- }], yesPath));
415
- if (hasAlternate) {
416
- chunks.push(...flattenBody([...stmt.alternate.body, {
417
- type: "GotoStatement",
418
- label: afterPath
419
- }], noPath));
420
- finishCurrentChunk(noPath, afterPath);
421
- } else {
422
- finishCurrentChunk(afterPath, afterPath);
423
- }
424
- return;
425
- }
426
- if (!currentBody.length || !(0, _random.chance)(splitPercent)) {
427
- currentBody.push(stmt);
428
- } else {
429
- // Start new chunk
430
- finishCurrentChunk();
431
- currentBody.push(stmt);
432
- }
433
- });
434
- finishCurrentChunk();
435
- chunks[chunks.length - 1].body.pop();
436
- return chunks;
437
- };
438
-
439
- /**
440
- * Executable code segments are broken down into `chunks` typically 1-3 statements each
441
- *
442
- * Chunked Code has a special `GotoStatement` node that get processed later on
443
- * This allows more complex control structures like `IfStatement`s and `ForStatement`s to be converted into basic
444
- * conditional jumps and flattened in the switch body
445
- *
446
- * IfStatement would be converted like this:
447
- *
448
- * MAIN:
449
- * if ( TEST ) {
450
- * GOTO consequent_label;
451
- * } else? {
452
- * GOTO alternate_label;
453
- * }
454
- * GOTO NEXT_CHUNK;
455
- */
456
- const chunks = [];
457
-
458
- // Flagged labels have addition code protecting the control state
459
- const flaggedLabels = Object.create(null);
460
-
461
- /**
462
- * label: switch(a+b+c){...break label...}
463
- */
464
- const switchLabel = this.getPlaceholder();
465
- const startLabel = this.getPlaceholder();
466
- chunks.push(...flattenBody(objectBody, startLabel));
467
- chunks[chunks.length - 1].body.push({
468
- type: "GotoStatement",
469
- label: "END_LABEL"
470
- });
471
- chunks.push({
472
- label: "END_LABEL",
473
- body: []
474
- });
475
- const endLabel = chunks[Object.keys(chunks).length - 1].label;
476
- if (!this.isDebug && this.addDeadCode) {
477
- // DEAD CODE 1/3: Add fake chunks that are never reached
478
- var fakeChunkCount = (0, _random.getRandomInteger)(1, 5);
479
- for (var i = 0; i < fakeChunkCount; i++) {
480
- // These chunks just jump somewhere random, they are never executed
481
- // so it could contain any code
482
- var fakeChunkBody = [
483
- // This a fake assignment expression
484
- (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)((0, _random.choice)(stateVars)), (0, _gen.Literal)((0, _random.getRandomInteger)(-150, 150)))), {
485
- type: "GotoStatement",
486
- label: (0, _random.choice)(chunks).label
487
- }];
488
- chunks.push({
489
- label: this.getPlaceholder(),
490
- body: fakeChunkBody,
491
- impossible: true
492
- });
493
- }
494
-
495
- // DEAD CODE 2/3: Add fake jumps to really mess with deobfuscators
496
- chunks.forEach(chunk => {
497
- if ((0, _random.chance)(25)) {
498
- var randomLabel = (0, _random.choice)(chunks).label;
499
-
500
- // The `false` literal will be mangled
501
- chunk.body.unshift((0, _gen.IfStatement)((0, _gen.Literal)(false), [{
502
- type: "GotoStatement",
503
- label: randomLabel,
504
- impossible: true
505
- }]));
506
- }
507
- });
508
-
509
- // DEAD CODE 3/3: Clone chunks but these chunks are never ran
510
- var cloneChunkCount = (0, _random.getRandomInteger)(1, 5);
511
- for (var i = 0; i < cloneChunkCount; i++) {
512
- var randomChunk = (0, _random.choice)(chunks);
513
- var clonedChunk = {
514
- body: (0, _insert.clone)(randomChunk.body),
515
- label: this.getPlaceholder(),
516
- impossible: true
517
- };
518
-
519
- // Don't double define functions
520
- var hasDeclaration = clonedChunk.body.find(stmt => {
521
- return stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration";
522
- });
523
- if (!hasDeclaration) {
524
- chunks.unshift(clonedChunk);
525
- }
526
- }
527
- }
528
-
529
- // Generate a unique 'state' number for each chunk
530
- var caseSelection = new Set();
531
- var uniqueStatesNeeded = chunks.length;
532
- do {
533
- var newState = (0, _random.getRandomInteger)(1, chunks.length * 15);
534
- if (this.isDebug) {
535
- newState = caseSelection.size;
536
- }
537
- caseSelection.add(newState);
538
- } while (caseSelection.size !== uniqueStatesNeeded);
539
- (0, _assert.ok)(caseSelection.size == uniqueStatesNeeded);
540
-
541
- /**
542
- * The accumulated state values
543
- *
544
- * index -> total state value
545
- */
546
- var caseStates = Array.from(caseSelection);
547
-
548
- /**
549
- * The individual state values for each label
550
- *
551
- * labels right now are just chunk indexes (numbers)
552
- *
553
- * but will expand to if statements and functions when `goto statement` obfuscation is added
554
- */
555
- var labelToStates = Object.create(null);
556
- var lastLabel;
557
- Object.values(chunks).forEach((chunk, i) => {
558
- var state = caseStates[i];
559
- var stateValues = Array(stateVars.length).fill(0).map((_, i) => lastLabel && (0, _random.chance)(95) // Try to make state changes not as drastic (If last label, re-use some of it's values)
560
- ? labelToStates[lastLabel][i] : (0, _random.getRandomInteger)(-500, 500));
561
- const getCurrentState = () => {
562
- return stateValues.reduce((a, b) => b + a, 0);
563
- };
564
- var correctIndex = (0, _random.getRandomInteger)(0, stateValues.length);
565
- stateValues[correctIndex] = state - (getCurrentState() - stateValues[correctIndex]);
566
- labelToStates[chunk.label] = stateValues;
567
- lastLabel = chunk.label;
568
- });
569
- var initStateValues = [...labelToStates[startLabel]];
570
- var endState = labelToStates[endLabel].reduce((a, b) => b + a, 0);
571
-
572
- // Creates a predicate based on the state-variables and control-object properties
573
- const createPredicate = stateValues => {
574
- this.mangledExpressionsMade++;
575
- var index = (0, _random.getRandomInteger)(0, stateVars.length);
576
- var compareValue = (0, _random.choice)([stateValues[index], (0, _random.getRandomInteger)(-100, 100)]);
577
-
578
- // 'state equality' test
579
- var test = (0, _gen.BinaryExpression)("==", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(compareValue, stateValues));
580
- var testValue = stateValues[index] === compareValue;
581
-
582
- // 'control' equality test
583
- if (controlConstantMap.size && (0, _random.chance)(50)) {
584
- var _controlConstantMap$g;
585
- // The controlMap maps LITERAL-values to STRING property names
586
- var actualValue = (0, _random.choice)(Array.from(controlConstantMap.keys()));
587
- var controlKey = (_controlConstantMap$g = controlConstantMap.get(actualValue)) === null || _controlConstantMap$g === void 0 ? void 0 : _controlConstantMap$g.key;
588
- var controlCompareValue = (0, _random.choice)([actualValue, stateValues[index], (0, _random.getRandomInteger)(-100, 100), controlGen.generate()]);
589
-
590
- // 'control equality' test
591
- test = (0, _gen.BinaryExpression)("==", getControlMember(controlKey), (0, _gen.Literal)(controlCompareValue));
592
- testValue = actualValue == controlCompareValue;
593
-
594
- // 'control typeof' test
595
- if ((0, _random.chance)(10)) {
596
- var compareTypeofValue = (0, _random.choice)(["number", "string", "object", "function", "undefined"]);
597
- test = (0, _gen.BinaryExpression)("==", (0, _gen.UnaryExpression)("typeof", getControlMember(controlKey)), (0, _gen.Literal)(compareTypeofValue));
598
- testValue = typeof actualValue === compareTypeofValue;
599
- }
600
-
601
- // 'control hasOwnProperty' test
602
- if ((0, _random.chance)(10)) {
603
- var hasOwnProperty = (0, _random.choice)([controlKey, controlGen.generate()]);
604
- test = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(controlVar), (0, _gen.Literal)("hasOwnProperty"), true), [(0, _gen.Literal)(hasOwnProperty)]);
605
- testValue = hasOwnProperty === controlKey;
606
- }
607
- }
608
- return {
609
- test,
610
- testValue
611
- };
612
- };
613
-
614
- // A "state-less" number literal is a Number Literal that is mangled in with the Control properties.
615
- // Example: X = CONTROL.Y + Z. These can be used anywhere because control properties are constant (unlike state variables)
616
- const createStatelessNumberLiteral = function (num) {
617
- var _selectedControlConst2;
618
- let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
619
- if (!controlConstantMap.size || depth > 4 || (0, _random.chance)(75 + depth * 5 + _this.mangledExpressionsMade / 25)) {
620
- // Add to control constant map?
621
- if ((0, _random.chance)(25 - controlConstantMap.size - _this.mangledExpressionsMade / 100)) {
622
- return addControlMapConstant(num);
623
- }
624
- return (0, _gen.Literal)(num);
625
- }
626
- _this.mangledExpressionsMade++;
627
- if (controlConstantMap.has(num)) {
628
- var _controlConstantMap$g2;
629
- return getControlMember((_controlConstantMap$g2 = controlConstantMap.get(num)) === null || _controlConstantMap$g2 === void 0 ? void 0 : _controlConstantMap$g2.key);
630
- }
631
- var allControlNodes = [object];
632
- parents.filter(x => x.$controlVar && x.$controlConstantMap.size > 0).forEach(node => allControlNodes.push(node));
633
- var controlNode = (0, _random.choice)(allControlNodes);
634
- var selectedControlConstantMap = controlNode.$controlConstantMap;
635
- var selectedControlVar = controlNode.$controlVar;
636
- var actualValue = (0, _random.choice)(Array.from(selectedControlConstantMap.keys()));
637
- var controlKey = (_selectedControlConst2 = selectedControlConstantMap.get(actualValue)) === null || _selectedControlConst2 === void 0 ? void 0 : _selectedControlConst2.key;
638
- if (typeof actualValue === "number") {
639
- var difference = actualValue - num;
640
- return (0, _gen.BinaryExpression)("-", getControlMember(controlKey, selectedControlVar), createStatelessNumberLiteral(difference, depth + 1));
641
- } else if (typeof actualValue === "string") {
642
- // 'control string length' test
643
- var compareValue = (0, _random.choice)([actualValue.length, (0, _random.getRandomInteger)(0, 50)]);
644
- var test = (0, _gen.BinaryExpression)("==", (0, _gen.MemberExpression)(getControlMember(controlKey, selectedControlVar), (0, _gen.Literal)("length"), true), createStatelessNumberLiteral(compareValue, depth + 1));
645
- var testValue = actualValue.length == compareValue;
646
- var consequent = createStatelessNumberLiteral(num, depth + 1);
647
- var alternate = (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100));
648
- return (0, _gen.ConditionalExpression)(test, testValue ? consequent : alternate, !testValue ? consequent : alternate);
649
- } else {
650
- throw new Error("Unknown: " + typeof actualValue);
651
- }
652
- };
653
-
654
- // A "state-bound" number literal is a Number Literal that is mangled in with the current state variables
655
- // Example: X = STATE + Y. This can only be used when the state-values are guaranteed to be known.
656
- const createStateBoundNumberLiteral = function (num, stateValues) {
657
- let depth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
658
- (0, _assert.ok)(Array.isArray(stateValues));
659
-
660
- // Base case: After 4 depth, OR random chance
661
- if (depth > 4 || (0, _random.chance)(75 + depth * 5 + _this.mangledExpressionsMade / 25)) {
662
- // Add this number to the control object?
663
- // Add to control constant map?
664
- if ((0, _random.chance)(25 - controlConstantMap.size)) {
665
- return addControlMapConstant(num);
666
- }
667
- return (0, _gen.Literal)(num);
668
- }
669
- _this.mangledExpressionsMade++;
670
- if ((0, _random.chance)(10)) {
671
- return createStatelessNumberLiteral(num, depth + 1);
672
- }
673
-
674
- // Terminated predicate
675
- if ((0, _random.chance)(50)) {
676
- var {
677
- test,
678
- testValue
679
- } = createPredicate(stateValues);
680
- var alternateNode = (0, _random.choice)([(0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100)), (0, _gen.Literal)(controlGen.generate()), getControlMember(controlGen.generate())]);
681
- return (0, _gen.ConditionalExpression)(test, testValue ? (0, _gen.Literal)(num) : alternateNode, !testValue ? (0, _gen.Literal)(num) : alternateNode);
682
- }
683
-
684
- // Recursive predicate
685
- var opposing = (0, _random.getRandomInteger)(0, stateVars.length);
686
- if ((0, _random.chance)(10)) {
687
- // state > compare ? real : fake
688
-
689
- var compareValue = (0, _random.choice)([stateValues[opposing], (0, _random.getRandomInteger)(-150, 150)]);
690
- var operator = (0, _random.choice)(["<", ">", "==", "!="]);
691
- var answer = {
692
- ">": compareValue > stateValues[opposing],
693
- "<": compareValue < stateValues[opposing],
694
- "==": compareValue === stateValues[opposing],
695
- "!=": compareValue !== stateValues[opposing]
696
- }[operator];
697
- var correct = createStateBoundNumberLiteral(num, stateValues, depth + 1);
698
- var incorrect = createStateBoundNumberLiteral((0, _random.getRandomInteger)(-150, 150), stateValues, depth + 1);
699
- return (0, _gen.ConditionalExpression)((0, _gen.BinaryExpression)(operator, createStateBoundNumberLiteral(compareValue, stateValues, depth + 1), (0, _gen.Identifier)(stateVars[opposing])), answer ? correct : incorrect, answer ? incorrect : correct);
700
- }
701
-
702
- // state + 10 = <REAL>
703
- var difference = num - stateValues[opposing];
704
- if (difference === 0) {
705
- return (0, _gen.Identifier)(stateVars[opposing]);
706
- }
707
- return (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)(stateVars[opposing]), createStateBoundNumberLiteral(difference, stateValues, depth + 1));
708
- };
709
- var outlinesCreated = 0;
710
- const isExpression = (object, parents) => {
711
- var fnIndex = parents.findIndex(x => (0, _insert.isFunction)(x));
712
- if (fnIndex != -1) {
713
- // This does NOT mutate
714
- parents = parents.slice(0, fnIndex);
715
- }
716
- var assignmentIndex = parents.findIndex(x => x.type === "AssignmentExpression");
717
-
718
- // Left-hand assignment validation
719
- if (assignmentIndex != -1) {
720
- if (parents[assignmentIndex].left === (parents[assignmentIndex - 1] || object)) {
721
- return false;
722
- }
723
- }
724
-
725
- // For in/of left validation
726
- var forInOfIndex = parents.findIndex(x => x.type === "ForInStatement" || x.type === "ForOfStatement");
727
- if (forInOfIndex != -1) {
728
- if (parents[forInOfIndex].left === (parents[forInOfIndex - 1] || object)) {
729
- return false;
730
- }
731
- }
732
-
733
- // Bound call-expression validation
734
- var callExpressionIndex = parents.findIndex(x => x.type === "CallExpression");
735
- if (callExpressionIndex != -1) {
736
- if (parents[callExpressionIndex].callee == (parents[callExpressionIndex - 1] || object)) {
737
- var callee = parents[callExpressionIndex].callee;
738
-
739
- // Detected bound call expression. Not supported.
740
- if (callee.type === "MemberExpression") {
741
- return false;
742
- }
743
- }
744
- }
745
-
746
- // Update-expression validation:
747
- var updateExpressionIndex = parents.findIndex(x => x.type === "UpdateExpression");
748
- if (updateExpressionIndex !== -1) return false;
749
- return true;
750
- };
751
-
752
- // This function checks if the expression or statements is possible to be outlined
753
- const canOutline = (object, parents) => {
754
- var isIllegal = false;
755
- var breakStatements = [];
756
- var returnStatements = [];
757
- if (!Array.isArray(object) && !isExpression(object, parents)) {
758
- return {
759
- isIllegal: true,
760
- breakStatements: [],
761
- returnStatements: []
762
- };
763
- }
764
- (0, _traverse.walk)(object, parents, (o, p) => {
765
- if (o.type === "ThisExpression" || o.type === "MetaProperty" || o.type === "Super") {
766
- isIllegal = true;
767
- return "EXIT";
768
- }
769
- if (o.type === "BreakStatement") {
770
- // This can be safely outlined
771
- if (o.label && o.label.name === switchLabel) {
772
- breakStatements.push([o, p]);
773
- } else {
774
- isIllegal = true;
775
- return "EXIT";
776
- }
777
- }
778
- if ((o.type === "ContinueStatement" || o.type === "AwaitExpression" || o.type === "YieldExpression" || o.type === "ReturnStatement" || o.type === "VariableDeclaration" || o.type === "FunctionDeclaration" || o.type === "ClassDeclaration") && !p.find(x => (0, _insert.isVarContext)(x))) {
779
- // This can be safely outlined
780
- if (o.type === "ReturnStatement") {
781
- returnStatements.push([o, p]);
782
- } else {
783
- isIllegal = true;
784
- return "EXIT";
785
- }
786
- }
787
- if (o.type === "Identifier") {
788
- if (o.name === "arguments") {
789
- isIllegal = true;
790
- return "EXIT";
791
- }
792
- }
793
- });
794
- return {
795
- isIllegal,
796
- breakStatements,
797
- returnStatements
798
- };
799
- };
800
- const createOutlineFunction = (body, stateValues, label) => {
801
- var key = controlGen.generate();
802
- var functionExpression = (0, _gen.FunctionExpression)([], body);
803
- if (!this.options.es5 && (0, _random.chance)(50)) {
804
- functionExpression.type = "ArrowFunctionExpression";
805
- }
806
- controlProperties.push((0, _gen.Property)((0, _gen.Literal)(key), functionExpression, false));
807
-
808
- // Add dead code to function
809
- if (!this.isDebug && (0, _random.chance)(25)) {
810
- var {
811
- test,
812
- testValue
813
- } = createPredicate(stateValues);
814
- var deadCodeVar = this.getPlaceholder();
815
- functionExpression.params.push((0, _gen.AssignmentPattern)((0, _gen.Identifier)(deadCodeVar), test));
816
- var alternate = [(0, _gen.ReturnStatement)((0, _random.choice)([(0, _gen.BinaryExpression)("==", (0, _gen.Identifier)((0, _random.choice)(stateVars)), (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100))), (0, _gen.Literal)(controlGen.generate()), (0, _gen.Identifier)("arguments"), (0, _gen.Identifier)((0, _random.choice)(stateVars)), (0, _gen.Identifier)(controlVar), (0, _gen.CallExpression)(getControlMember(controlGen.generate()), [])]))];
817
- functionExpression.body.body.unshift((0, _gen.IfStatement)(testValue ? (0, _gen.UnaryExpression)("!", (0, _gen.Identifier)(deadCodeVar)) : (0, _gen.Identifier)(deadCodeVar), alternate));
818
- }
819
- outlinesCreated++;
820
- return key;
821
- };
822
- const attemptOutlineStatements = (statements, parentBlock, stateValues, label) => {
823
- if (this.isDebug || !this.outlineStatements || (0, _random.chance)(75 + outlinesCreated - this.mangledExpressionsMade / 25)) {
824
- return;
825
- }
826
- var index = parentBlock.indexOf(statements[0]);
827
- if (index === -1) return;
828
- var outlineInfo = canOutline(statements, parentBlock);
829
- if (outlineInfo.isIllegal) return;
830
- var breakFlag = controlGen.generate();
831
- outlineInfo.breakStatements.forEach(_ref => {
832
- let [breakStatement, p] = _ref;
833
- this.replace(breakStatement, (0, _gen.ReturnStatement)((0, _gen.Literal)(breakFlag)));
834
- });
835
- var returnFlag = controlGen.generate();
836
- outlineInfo.returnStatements.forEach(_ref2 => {
837
- let [returnStatement, p] = _ref2;
838
- var argument = returnStatement.argument || (0, _gen.Identifier)("undefined");
839
- this.replace(returnStatement, (0, _gen.ReturnStatement)((0, _gen.ObjectExpression)([(0, _gen.Property)((0, _gen.Literal)(returnFlag), argument, false)])));
840
- });
841
-
842
- // Outline these statements!
843
- var key = createOutlineFunction((0, _insert.clone)(statements), stateValues, label);
844
- var callExpression = (0, _gen.CallExpression)(getControlMember(key), []);
845
- var newStatements = [];
846
- if (outlineInfo.breakStatements.length === 0 && outlineInfo.returnStatements.length === 0) {
847
- newStatements.push((0, _gen.ExpressionStatement)(callExpression));
848
- } else if (outlineInfo.returnStatements.length === 0) {
849
- newStatements.push((0, _gen.IfStatement)((0, _gen.BinaryExpression)("==", callExpression, (0, _gen.Literal)(breakFlag)), [(0, _gen.BreakStatement)(switchLabel)]));
850
- } else {
851
- var tempVar = this.getPlaceholder();
852
- newStatements.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(tempVar, callExpression)));
853
- const t = str => (0, _template.default)(str).single().expression;
854
- newStatements.push((0, _gen.IfStatement)(t(`${tempVar} === "${breakFlag}"`), [(0, _gen.BreakStatement)(switchLabel)], [(0, _gen.IfStatement)(t(`typeof ${tempVar} == "object"`), [(0, _gen.ReturnStatement)(t(`${tempVar}["${returnFlag}"]`))])]));
855
- }
856
-
857
- // Remove the original statements from the block and replace it with the call expression
858
- parentBlock.splice(index, statements.length, ...newStatements);
859
- };
860
- const attemptOutlineExpression = (expression, expressionParents, stateValues, label) => {
861
- if (this.isDebug || !this.outlineExpressions || (0, _random.chance)(75 + outlinesCreated - this.mangledExpressionsMade / 25)) {
862
- return;
863
- }
864
- var outlineInfo = canOutline(expression, expressionParents);
865
- if (outlineInfo.isIllegal || outlineInfo.breakStatements.length || outlineInfo.returnStatements.length) return;
866
-
867
- // Outline this expression!
868
- var key = createOutlineFunction([(0, _gen.ReturnStatement)((0, _insert.clone)(expression))], stateValues, label);
869
- var callExpression = (0, _gen.CallExpression)(getControlMember(key), []);
870
- this.replaceIdentifierOrLiteral(expression, callExpression, expressionParents);
871
- };
872
- const createTransitionExpression = (index, add, mutatingStateValues, label) => {
873
- var beforeStateValues = [...mutatingStateValues];
874
- var newValue = mutatingStateValues[index] + add;
875
- var expr = null;
876
- if (this.isDebug) {
877
- // state = NEW_STATE
878
- expr = (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(stateVars[index]), (0, _gen.Literal)(newValue));
879
- } else if ((0, _random.chance)(90)) {
880
- // state += (NEW_STATE - CURRENT_STATE)
881
- expr = (0, _gen.AssignmentExpression)("+=", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(add, mutatingStateValues));
882
- } else {
883
- // state *= 2
884
- // state -= DIFFERENCE
885
- var double = mutatingStateValues[index] * 2;
886
- var diff = double - newValue;
887
- var first = (0, _gen.AssignmentExpression)("*=", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(2, mutatingStateValues));
888
- mutatingStateValues[index] = double;
889
- expr = (0, _gen.SequenceExpression)([first, (0, _gen.AssignmentExpression)("-=", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(diff, mutatingStateValues))]);
890
- }
891
- mutatingStateValues[index] = newValue;
892
-
893
- // These are lower quality outlines vs. the entire transition outline
894
- if ((0, _random.chance)(50)) {
895
- attemptOutlineExpression(expr, [], [...beforeStateValues], label);
896
- }
897
- return expr;
898
- };
899
- var cases = [];
900
- chunks.forEach((chunk, i) => {
901
- // skip last case, its empty and never ran
902
- if (chunk.label === endLabel) {
903
- return;
904
- }
905
- (0, _assert.ok)(labelToStates[chunk.label]);
906
- var state = caseStates[i];
907
- var staticStateValues = [...labelToStates[chunk.label]];
908
- var potentialBranches = new Set();
909
- [...chunk.body].forEach(stmt => {
910
- (0, _traverse.walk)(stmt, [], (o, p) => {
911
- // This mangles certain literals with the state variables
912
- // Ex: A number literal (50) changed to a expression (stateVar + 40), when stateVar = 10
913
- if (!this.isDebug && o.type === "Literal" && !p.find(x => (0, _insert.isVarContext)(x))) {
914
- if (typeof o.value === "number" && Math.floor(o.value) === o.value &&
915
- // Only whole numbers
916
- Math.abs(o.value) < 100_000 &&
917
- // Hard-coded limit
918
- this.mangleNumberLiterals && (0, _random.chance)(50 - this.mangledExpressionsMade / 100)) {
919
- // 50 -> state1 - 10, when state1 = 60. The result is still 50
920
-
921
- return () => {
922
- this.replaceIdentifierOrLiteral(o, createStateBoundNumberLiteral(o.value, staticStateValues), p);
923
- };
924
- }
925
- if (typeof o.value === "boolean" && this.mangleBooleanLiterals && (0, _random.chance)(50 - this.mangledExpressionsMade / 100)) {
926
- // true -> state1 == 10, when state1 = 10. The result is still true
927
-
928
- // Choose a random state var to compare again
929
- var index = (0, _random.getRandomInteger)(0, stateVars.length);
930
- var compareValue = staticStateValues[index];
931
-
932
- // When false, always choose a different number, so the expression always equals false
933
- while (!o.value && compareValue === staticStateValues[index]) {
934
- compareValue = (0, _random.getRandomInteger)(-150, 150);
935
- }
936
- var mangledExpression = (0, _gen.BinaryExpression)("==", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(compareValue, staticStateValues));
937
- return () => {
938
- this.replaceIdentifierOrLiteral(o, mangledExpression, p);
939
- attemptOutlineExpression(o, p, staticStateValues, chunk.label);
940
- };
941
- }
942
- }
943
-
944
- // Mangle certain referenced identifiers
945
- // console.log("hi") -> (x ? console : window).log("hi"), when is x true. The result is the same
946
- if (!this.isDebug && o.type === "Identifier" && this.mangleIdentifiers && !_constants.reservedIdentifiers.has(o.name) && (0, _random.chance)(50 - this.mangledExpressionsMade / 100) && !p.find(x => (0, _insert.isVarContext)(x))) {
947
- // ONLY referenced identifiers (like actual variable names) can be changed
948
- var info = (0, _identifiers.getIdentifierInfo)(o, p);
949
- if (!info.spec.isReferenced || info.spec.isDefined || info.spec.isModified || info.spec.isExported) {
950
- return;
951
- }
952
-
953
- // TYPEOF expression check
954
- if (p[0] && p[0].type === "UnaryExpression" && p[0].operator === "typeof" && p[0].argument === o) {
955
- return;
956
- }
957
-
958
- // Update expression check
959
- if (p[0] && p[0].type === "UpdateExpression") {
960
- return;
961
- }
962
-
963
- // FOR-in/of initializer check
964
- if ((0, _insert.isForInitialize)(o, p) === "left-hand") {
965
- return;
966
- }
967
- var {
968
- test,
969
- testValue
970
- } = createPredicate(staticStateValues);
971
-
972
- // test && real
973
- var mangledExpression = (0, _gen.LogicalExpression)(testValue ? "&&" : "||", test, (0, _gen.Identifier)(o.name));
974
-
975
- // control.fake = real
976
- if ((0, _random.chance)(50)) {
977
- mangledExpression = (0, _gen.AssignmentExpression)("=", getControlMember(controlGen.generate()), (0, _gen.Identifier)(o.name));
978
- }
979
-
980
- // test ? real : fake
981
- if ((0, _random.chance)(50)) {
982
- var alternateName = (0, _random.choice)([controlVar, ...stateVars, ...this.options.globalVariables, ..._constants.reservedIdentifiers]);
983
-
984
- // Don't use 'arguments'
985
- if (alternateName === "arguments") alternateName = "undefined";
986
- mangledExpression = (0, _gen.ConditionalExpression)(test, (0, _gen.Identifier)(testValue ? o.name : alternateName), (0, _gen.Identifier)(!testValue ? o.name : alternateName));
987
- }
988
- return () => {
989
- this.replaceIdentifierOrLiteral(o, mangledExpression, p);
990
- };
991
- }
992
-
993
- // Function outlining: bring out certain expressions
994
- if (!this.isDebug && o.type && ["BinaryExpression", "LogicalExpression", "CallExpression", "AssignmentExpression", "MemberExpression", "ObjectExpression", "ConditionalExpression"].includes(o.type) && !(0, _random.chance)(p.length * 5) &&
995
- // The further down the tree the lower quality of expression
996
- !p.find(x => (0, _insert.isContext)(x) || x.$outlining)) {
997
- o.$outlining = true;
998
- return () => {
999
- attemptOutlineExpression(o, p, staticStateValues, chunk.label);
1000
- };
1001
- }
1002
-
1003
- // Opaque predicates: If Statements, Conditional Statements, Switch Case test
1004
- if (!this.isDebug && this.addOpaquePredicates && p[0] && (0, _random.chance)(50 - outlinesCreated - this.mangledExpressionsMade / 100)) {
1005
- var isTestExpression = p[0].type == "IfStatement" && p[0].test === o || p[0].type === "ConditionalExpression" && p[0].test === o || p[0].type === "SwitchCase" && p[0].test === o;
1006
- if (isTestExpression && !p.find(x => (0, _insert.isContext)(x))) {
1007
- return () => {
1008
- var {
1009
- test,
1010
- testValue
1011
- } = createPredicate(staticStateValues);
1012
- this.replace(o, (0, _gen.LogicalExpression)(testValue ? "&&" : "||", test, (0, _insert.clone)(o)));
1013
- };
1014
- }
1015
- }
1016
- if (o.type == "StateIdentifier") {
1017
- return () => {
1018
- (0, _assert.ok)(labelToStates[o.label]);
1019
- this.replace(o, (0, _gen.ArrayExpression)(labelToStates[o.label].map(_gen.Literal)));
1020
- };
1021
- }
1022
- if (o.type == "GotoStatement") {
1023
- return () => {
1024
- var blockIndex = p.findIndex(node => (0, _traverse.isBlock)(node) || node.type === "SwitchCase");
1025
- if (blockIndex === -1) {
1026
- var index = chunk.body.indexOf(stmt);
1027
- (0, _assert.ok)(index != -1);
1028
-
1029
- // Top level: Insert break statement in the chunk body
1030
- // This is OKAY because this forEach uses a cloned version of the body `[...chunk.body]`
1031
- chunk.body.splice(index + 1, 0, (0, _gen.BreakStatement)(switchLabel));
1032
- } else {
1033
- var block = p[blockIndex];
1034
- if (block.type === "SwitchCase") {
1035
- // Handle switch case break placement (Important!)
1036
- block.consequent.splice(block.consequent.indexOf(p[blockIndex - 2] || o) + 1, 0, (0, _gen.BreakStatement)(switchLabel));
1037
- } else {
1038
- // Standard block placement
1039
- var child = p[blockIndex - 2] || o;
1040
- var childIndex = block.body.indexOf(child);
1041
- block.body.splice(childIndex + 1, 0, (0, _gen.BreakStatement)(switchLabel));
1042
- }
1043
- }
1044
- if (!o.impossible) {
1045
- potentialBranches.add(o.label);
1046
- }
1047
- var mutatingStateValues = [...labelToStates[chunk.label]];
1048
- var nextStateValues = labelToStates[o.label];
1049
- (0, _assert.ok)(nextStateValues, o.label);
1050
- var transitionExpressions = [];
1051
- for (var stateValueIndex = 0; stateValueIndex < stateVars.length; stateValueIndex++) {
1052
- var diff = nextStateValues[stateValueIndex] - mutatingStateValues[stateValueIndex];
1053
-
1054
- // Only add if state value changed
1055
- // If pointing to itself then always add to ensure SequenceExpression isn't empty
1056
- if (diff !== 0 || o.label === chunk.label) {
1057
- transitionExpressions.push(createTransitionExpression(stateValueIndex, diff, mutatingStateValues, chunk.label));
1058
- }
1059
- }
1060
- (0, _assert.ok)(transitionExpressions.length !== 0);
1061
- var sequenceExpression = (0, _gen.SequenceExpression)(transitionExpressions);
1062
-
1063
- // Check if flagged and additional code here
1064
- if (typeof flaggedLabels[o.label] === "object") {
1065
- var {
1066
- flagKey,
1067
- flagValue
1068
- } = flaggedLabels[o.label];
1069
- sequenceExpression.expressions.push((0, _gen.AssignmentExpression)("=", getControlMember(flagKey), (0, _gen.Literal)(flagValue)));
1070
- }
1071
- attemptOutlineExpression(sequenceExpression, [], staticStateValues, chunk.label);
1072
- this.replace(o, (0, _gen.ExpressionStatement)(sequenceExpression));
1073
- };
1074
- }
1075
- });
1076
- });
1077
- attemptOutlineStatements(chunk.body, chunk.body, staticStateValues, chunk.label);
1078
- if (!chunk.impossible) {
1079
- // FUTURE OBFUSCATION IDEA: Update controlObject based on 'potentialBranches' code
1080
- // This idea would require a lot of work but would make some seriously effective obfuscation
1081
- // for protecting the data. In 'inactive' states the data could be overwritten to fake values
1082
- // And in the 'active' state the data would brought back just in time. This would require the controlObject
1083
- // state to be known in all chunks
1084
- }
1085
- var caseObject = {
1086
- body: chunk.body,
1087
- state: state,
1088
- label: chunk.label
1089
- };
1090
- cases.push(caseObject);
1091
- });
1092
- if (!this.isDebug && this.addDeadCode) {
1093
- // Add fake control object updates
1094
- chunks.forEach(chunk => {
1095
- if ((0, _random.chance)(10)) {
1096
- // These deadCode variants can NOT break the state/control variables
1097
- // They are executed!
1098
- var deadCodeChoices = [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(controlGen.generate()), (0, _gen.Literal)(controlGen.generate()))), (0, _gen.ExpressionStatement)((0, _gen.UnaryExpression)("delete", getControlMember(controlGen.generate())))];
1099
-
1100
- // These deadCode variants can make breaking changes
1101
- // because they are never ran
1102
- if (chunk.impossible) {
1103
- var randomControlKey = (0, _random.choice)(controlProperties.map(prop => {
1104
- var _prop$key;
1105
- return (_prop$key = prop.key) === null || _prop$key === void 0 ? void 0 : _prop$key.value;
1106
- }).filter(x => x && typeof x === "string")) || controlGen.generate();
1107
- deadCodeChoices = deadCodeChoices.concat([(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(controlVar), (0, _gen.Literal)(false))), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(controlVar), (0, _gen.Identifier)("undefined"))), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(randomControlKey), (0, _gen.Identifier)("undefined"))), (0, _gen.ExpressionStatement)((0, _gen.UnaryExpression)("delete", getControlMember(randomControlKey)))]);
1108
- }
1109
- chunk.body.unshift((0, _random.choice)(deadCodeChoices));
1110
- }
1111
- });
1112
- }
1113
- if (!this.isDebug) {
1114
- (0, _random.shuffle)(cases);
1115
- (0, _random.shuffle)(controlProperties);
1116
- }
1117
- var discriminant = (0, _template.default)(`${stateVars.join("+")}`).single().expression;
1118
- objectBody.length = 0;
1119
- // Perverse position of import declarations
1120
- for (var importDeclaration of importDeclarations) {
1121
- objectBody.push(importDeclaration);
1122
- }
1123
-
1124
- // As well as functions are brought up
1125
- for (var functionName of functionDeclarationNames) {
1126
- objectBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(functionName, functionDeclarationValues.get(functionName))));
1127
- }
1128
- var defaultCaseIndex = (0, _random.getRandomInteger)(0, cases.length);
1129
- var switchCases = [];
1130
- cases.forEach((caseObject, i) => {
1131
- var _caseObject$body$0$la;
1132
- // Empty case OR single break statement is skipped
1133
- if (caseObject.body.length === 0 || caseObject.body.length === 1 && caseObject.body[0].type === "BreakStatement" && ((_caseObject$body$0$la = caseObject.body[0].label) === null || _caseObject$body$0$la === void 0 ? void 0 : _caseObject$body$0$la.name) === switchLabel) return;
1134
- var test = (0, _gen.Literal)(caseObject.state);
1135
- var isEligibleForOutlining = false;
1136
-
1137
- // Check if Control Map has this value
1138
- if (!this.isDebug && controlConstantMap.has(caseObject.state)) {
1139
- var _controlConstantMap$g3;
1140
- test = getControlMember((_controlConstantMap$g3 = controlConstantMap.get(caseObject.state)) === null || _controlConstantMap$g3 === void 0 ? void 0 : _controlConstantMap$g3.key);
1141
- }
1142
-
1143
- // Create complex test expressions for each switch case
1144
- if (!this.isDebug && this.addComplexTest && (0, _random.chance)(25)) {
1145
- isEligibleForOutlining = true;
1146
-
1147
- // case STATE+X:
1148
- var stateVarIndex = (0, _random.getRandomInteger)(0, stateVars.length);
1149
- var stateValues = labelToStates[caseObject.label];
1150
- var difference = stateValues[stateVarIndex] - caseObject.state;
1151
- var conditionNodes = [];
1152
- var alreadyConditionedItems = new Set();
1153
-
1154
- // This code finds clash conditions and adds them to 'conditionNodes' array
1155
- Object.keys(labelToStates).forEach(label => {
1156
- if (label !== caseObject.label) {
1157
- var labelStates = labelToStates[label];
1158
- var totalState = labelStates.reduce((a, b) => a + b, 0);
1159
- if (totalState === labelStates[stateVarIndex] - difference) {
1160
- var differentIndex = labelStates.findIndex((v, i) => v !== stateValues[i]);
1161
- if (differentIndex !== -1) {
1162
- var expressionAsString = stateVars[differentIndex] + "!=" + labelStates[differentIndex];
1163
- if (!alreadyConditionedItems.has(expressionAsString)) {
1164
- alreadyConditionedItems.add(expressionAsString);
1165
- conditionNodes.push((0, _gen.BinaryExpression)("!=", (0, _gen.Identifier)(stateVars[differentIndex]), (0, _gen.Literal)(labelStates[differentIndex])));
1166
- }
1167
- } else {
1168
- conditionNodes.push((0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(totalState)));
1169
- }
1170
- }
1171
- }
1172
- });
1173
-
1174
- // case STATE!=Y && STATE+X
1175
- test = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)(stateVars[stateVarIndex]), (0, _gen.Literal)(difference));
1176
-
1177
- // Use the 'conditionNodes' to not cause state clashing issues
1178
- conditionNodes.forEach(conditionNode => {
1179
- test = (0, _gen.LogicalExpression)("&&", conditionNode, test);
1180
- });
1181
- }
1182
-
1183
- // A 'flagged' label has addition 'flagKey' that gets switched before jumped to
1184
- if (flaggedLabels[caseObject.label]) {
1185
- isEligibleForOutlining = true;
1186
- var {
1187
- flagKey,
1188
- flagValue
1189
- } = flaggedLabels[caseObject.label];
1190
- var alternateNum;
1191
- do {
1192
- alternateNum = (0, _random.getRandomInteger)(-1000, 1000 + chunks.length);
1193
- } while (caseSelection.has(alternateNum));
1194
- var alternate = (0, _gen.Literal)(alternateNum);
1195
-
1196
- // case FLAG ? <REAL> : <FAKE>:
1197
- test = (0, _gen.ConditionalExpression)(getControlMember(flagKey), flagValue ? test : alternate, !flagValue ? test : alternate);
1198
- }
1199
-
1200
- // Outline this switch case test
1201
- if (!this.isDebug && this.outlineExpressions && isEligibleForOutlining && (0, _random.chance)(75 - outlinesCreated - this.mangledExpressionsMade / 25)) {
1202
- this.mangledExpressionsMade++;
1203
-
1204
- // Selected a random parent node (or this node) to insert this function in
1205
- var selectedControlNode = (0, _random.choice)(allControlNodes);
1206
- var selectedControlProperties = selectedControlNode.$controlProperties;
1207
- var selectedControlVar = selectedControlNode.$controlVar;
1208
- var selectedControlGen = selectedControlNode.$controlGen;
1209
- var fnKey = selectedControlGen.generate();
1210
-
1211
- // Pass in the:
1212
- // - controlVar for 'flagged labels' code check
1213
- // - stateVars for 'complex test expressions'
1214
- // (Check which identifiers are actually needed)
1215
- var argumentList = [],
1216
- watchingFor = new Set([controlVar, ...stateVars]);
1217
- (0, _traverse.walk)(test, [], (o, p) => {
1218
- if (o.type === "Identifier" && watchingFor.has(o.name)) {
1219
- watchingFor.delete(o.name);
1220
- argumentList.push((0, _gen.Identifier)(o.name));
1221
- }
1222
- });
1223
- selectedControlProperties.push((0, _gen.Property)((0, _gen.Literal)(fnKey), (0, _gen.FunctionExpression)(argumentList, [(0, _gen.ReturnStatement)(test)]), true));
1224
-
1225
- // case control.a(control, s1, s2):
1226
- test = (0, _gen.CallExpression)(getControlMember(fnKey, selectedControlVar), (0, _insert.clone)(argumentList));
1227
- }
1228
-
1229
- // One random case gets to be default
1230
- if (!this.isDebug && i === defaultCaseIndex) test = null;
1231
- var testArray = [test];
1232
- if (!this.isDebug && this.addFakeTest && (0, _random.chance)(50)) {
1233
- // Add fake test
1234
- // case <FAKE>:
1235
- // case <REAL>:
1236
- // case <FAKE>:
1237
- var fakeTestCount = (0, _random.getRandomInteger)(1, 4);
1238
- for (var i = 0; i < fakeTestCount; i++) {
1239
- // Create a fake test number that doesn't interfere with the actual states
1240
- var fakeTestNum;
1241
- do {
1242
- fakeTestNum = (0, _random.getRandomInteger)(1, 1000 + caseSelection.size);
1243
- } while (caseSelection.has(fakeTestNum));
1244
-
1245
- // Add this fake test
1246
- testArray.push((0, _gen.Literal)(fakeTestNum));
1247
- }
1248
- (0, _random.shuffle)(testArray);
1249
- }
1250
- testArray.forEach((test, i) => {
1251
- var body = i === testArray.length - 1 ? caseObject.body : [];
1252
- switchCases.push((0, _gen.SwitchCase)(test, body));
1253
- });
1254
- });
1255
-
1256
- // switch(state) { case ... }
1257
- var switchStatement = (0, _gen.SwitchStatement)(discriminant, switchCases);
1258
- var declarations = [];
1259
-
1260
- // var state = START_STATE
1261
- declarations.push(...stateVars.map((stateVar, i) => {
1262
- return (0, _gen.VariableDeclarator)(stateVar, (0, _gen.Literal)(initStateValues[i]));
1263
- }));
1264
-
1265
- // var control = { strings, numbers, outlined functions, etc... }
1266
- var objectExpression = (0, _gen.ObjectExpression)(controlProperties);
1267
- declarations.push((0, _gen.VariableDeclarator)(controlVar, objectExpression));
1268
- objectBody.push(
1269
- // Use individual variable declarations instead so Stack can apply
1270
- ...declarations.map(declaration => (0, _gen.VariableDeclaration)(declaration, "var")));
1271
-
1272
- // while (state != END_STATE) {...}
1273
- var whileTest = (0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(endState));
1274
- objectBody.push((0, _gen.WhileStatement)(whileTest, [(0, _gen.LabeledStatement)(switchLabel, switchStatement)]));
1275
-
1276
- // mark this object for switch case obfuscation
1277
- switchStatement.$controlFlowFlattening = true;
1278
- };
1279
- }
1280
- }
1281
- exports.default = ControlFlowFlattening;