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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
- package/.github/workflows/node.js.yml +1 -1
- package/CHANGELOG.md +105 -0
- package/Migration.md +57 -0
- package/README.md +23 -913
- package/dist/constants.js +69 -13
- package/dist/index.js +108 -152
- package/dist/obfuscator.js +316 -118
- package/dist/options.js +1 -109
- package/dist/order.js +30 -30
- package/dist/presets.js +47 -45
- package/dist/probability.js +25 -32
- package/dist/templates/bufferToStringTemplate.js +9 -0
- package/dist/templates/deadCodeTemplates.js +9 -0
- package/dist/templates/getGlobalTemplate.js +19 -0
- package/dist/templates/integrityTemplate.js +30 -0
- package/dist/templates/setFunctionLengthTemplate.js +9 -0
- package/dist/templates/stringCompressionTemplate.js +10 -0
- package/dist/templates/tamperProtectionTemplates.js +21 -0
- package/dist/templates/template.js +213 -93
- package/dist/transforms/astScrambler.js +100 -0
- package/dist/transforms/calculator.js +70 -127
- package/dist/transforms/controlFlowFlattening.js +1182 -0
- package/dist/transforms/deadCode.js +62 -577
- package/dist/transforms/dispatcher.js +300 -309
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
- package/dist/transforms/extraction/objectExtraction.js +131 -215
- package/dist/transforms/finalizer.js +56 -59
- package/dist/transforms/flatten.js +275 -276
- package/dist/transforms/functionOutlining.js +230 -0
- package/dist/transforms/identifier/globalConcealing.js +217 -103
- package/dist/transforms/identifier/movedDeclarations.js +167 -91
- package/dist/transforms/identifier/renameVariables.js +240 -187
- package/dist/transforms/lock/integrity.js +61 -184
- package/dist/transforms/lock/lock.js +263 -303
- package/dist/transforms/minify.js +431 -436
- package/dist/transforms/opaquePredicates.js +65 -118
- package/dist/transforms/pack.js +160 -0
- package/dist/transforms/plugin.js +179 -0
- package/dist/transforms/preparation.js +263 -163
- package/dist/transforms/renameLabels.js +132 -56
- package/dist/transforms/rgf.js +142 -240
- package/dist/transforms/shuffle.js +52 -145
- package/dist/transforms/string/encoding.js +45 -173
- package/dist/transforms/string/stringCompression.js +81 -126
- package/dist/transforms/string/stringConcealing.js +189 -224
- package/dist/transforms/string/stringEncoding.js +32 -40
- package/dist/transforms/string/stringSplitting.js +54 -55
- package/dist/transforms/variableMasking.js +232 -0
- package/dist/utils/ControlObject.js +125 -0
- package/dist/utils/IntGen.js +46 -0
- package/dist/utils/NameGen.js +106 -0
- package/dist/utils/ast-utils.js +560 -0
- package/dist/utils/function-utils.js +56 -0
- package/dist/utils/gen-utils.js +48 -0
- package/dist/utils/node.js +77 -0
- package/dist/utils/object-utils.js +21 -0
- package/dist/utils/random-utils.js +91 -0
- package/dist/utils/static-utils.js +64 -0
- package/dist/validateOptions.js +122 -0
- package/index.d.ts +1 -17
- package/package.json +27 -22
- package/src/constants.ts +139 -77
- package/src/index.ts +70 -163
- package/src/obfuscationResult.ts +43 -0
- package/src/obfuscator.ts +328 -135
- package/src/options.ts +154 -623
- package/src/order.ts +14 -14
- package/src/presets.ts +39 -34
- package/src/probability.ts +21 -36
- package/src/templates/{bufferToString.ts → bufferToStringTemplate.ts} +5 -54
- package/src/templates/deadCodeTemplates.ts +1185 -0
- package/src/templates/getGlobalTemplate.ts +72 -0
- package/src/templates/integrityTemplate.ts +69 -0
- package/src/templates/setFunctionLengthTemplate.ts +11 -0
- package/src/templates/stringCompressionTemplate.ts +42 -0
- package/src/templates/tamperProtectionTemplates.ts +116 -0
- package/src/templates/template.ts +183 -92
- package/src/transforms/astScrambler.ts +99 -0
- package/src/transforms/calculator.ts +96 -224
- package/src/transforms/controlFlowFlattening.ts +1594 -0
- package/src/transforms/deadCode.ts +85 -628
- package/src/transforms/dispatcher.ts +431 -636
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -299
- package/src/transforms/extraction/objectExtraction.ts +160 -333
- package/src/transforms/finalizer.ts +63 -64
- package/src/transforms/flatten.ts +439 -557
- package/src/transforms/functionOutlining.ts +225 -0
- package/src/transforms/identifier/globalConcealing.ts +261 -189
- package/src/transforms/identifier/movedDeclarations.ts +228 -142
- package/src/transforms/identifier/renameVariables.ts +252 -258
- package/src/transforms/lock/integrity.ts +84 -260
- package/src/transforms/lock/lock.ts +342 -491
- package/src/transforms/minify.ts +523 -663
- package/src/transforms/opaquePredicates.ts +90 -229
- package/src/transforms/pack.ts +195 -0
- package/src/transforms/plugin.ts +185 -0
- package/src/transforms/preparation.ts +337 -215
- package/src/transforms/renameLabels.ts +176 -77
- package/src/transforms/rgf.ts +293 -386
- package/src/transforms/shuffle.ts +80 -254
- package/src/transforms/string/encoding.ts +26 -129
- package/src/transforms/string/stringCompression.ts +118 -236
- package/src/transforms/string/stringConcealing.ts +255 -339
- package/src/transforms/string/stringEncoding.ts +28 -47
- package/src/transforms/string/stringSplitting.ts +61 -75
- package/src/transforms/variableMasking.ts +257 -0
- package/src/utils/ControlObject.ts +141 -0
- package/src/utils/IntGen.ts +33 -0
- package/src/utils/NameGen.ts +106 -0
- package/src/utils/ast-utils.ts +667 -0
- package/src/utils/function-utils.ts +50 -0
- package/src/utils/gen-utils.ts +48 -0
- package/src/utils/node.ts +78 -0
- package/src/utils/object-utils.ts +21 -0
- package/src/utils/random-utils.ts +79 -0
- package/src/utils/static-utils.ts +66 -0
- package/src/validateOptions.ts +256 -0
- package/tsconfig.json +13 -8
- package/babel.config.js +0 -12
- package/dev.js +0 -8
- package/dist/compiler.js +0 -34
- package/dist/parser.js +0 -59
- package/dist/precedence.js +0 -66
- package/dist/templates/bufferToString.js +0 -108
- package/dist/templates/crash.js +0 -59
- package/dist/templates/es5.js +0 -137
- package/dist/templates/functionLength.js +0 -34
- package/dist/templates/globals.js +0 -9
- package/dist/transforms/antiTooling.js +0 -88
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +0 -1281
- package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +0 -131
- package/dist/transforms/es5/antiClass.js +0 -164
- package/dist/transforms/es5/antiDestructuring.js +0 -193
- package/dist/transforms/es5/antiES6Object.js +0 -185
- package/dist/transforms/es5/antiSpreadOperator.js +0 -35
- package/dist/transforms/es5/antiTemplate.js +0 -66
- package/dist/transforms/es5/es5.js +0 -123
- package/dist/transforms/extraction/classExtraction.js +0 -83
- package/dist/transforms/identifier/globalAnalysis.js +0 -70
- package/dist/transforms/identifier/variableAnalysis.js +0 -104
- package/dist/transforms/lock/antiDebug.js +0 -76
- package/dist/transforms/stack.js +0 -343
- package/dist/transforms/transform.js +0 -350
- package/dist/traverse.js +0 -110
- package/dist/util/compare.js +0 -145
- package/dist/util/gen.js +0 -564
- package/dist/util/guard.js +0 -9
- package/dist/util/identifiers.js +0 -355
- package/dist/util/insert.js +0 -362
- package/dist/util/math.js +0 -19
- package/dist/util/object.js +0 -40
- package/dist/util/random.js +0 -130
- package/dist/util/scope.js +0 -20
- package/docs/ControlFlowFlattening.md +0 -595
- package/docs/Countermeasures.md +0 -63
- package/docs/ES5.md +0 -197
- package/docs/Integrity.md +0 -75
- package/docs/RGF.md +0 -419
- package/samples/example.js +0 -15
- package/samples/high.js +0 -1
- package/samples/input.js +0 -3
- package/samples/javascriptobfuscator.com.js +0 -8
- package/samples/jscrambler_advanced.js +0 -1894
- package/samples/jscrambler_light.js +0 -1134
- package/samples/low.js +0 -1
- package/samples/medium.js +0 -1
- package/samples/obfuscator.io.js +0 -1686
- package/samples/preemptive.com.js +0 -16
- package/src/compiler.ts +0 -35
- package/src/parser.ts +0 -49
- package/src/precedence.ts +0 -61
- package/src/templates/crash.ts +0 -55
- package/src/templates/es5.ts +0 -131
- package/src/templates/functionLength.ts +0 -32
- package/src/templates/globals.ts +0 -3
- package/src/transforms/antiTooling.ts +0 -102
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2146
- package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
- package/src/transforms/es5/antiClass.ts +0 -272
- package/src/transforms/es5/antiDestructuring.ts +0 -294
- package/src/transforms/es5/antiES6Object.ts +0 -267
- package/src/transforms/es5/antiSpreadOperator.ts +0 -56
- package/src/transforms/es5/antiTemplate.ts +0 -98
- package/src/transforms/es5/es5.ts +0 -149
- package/src/transforms/extraction/classExtraction.ts +0 -168
- package/src/transforms/identifier/globalAnalysis.ts +0 -85
- package/src/transforms/identifier/variableAnalysis.ts +0 -118
- package/src/transforms/lock/antiDebug.ts +0 -112
- package/src/transforms/stack.ts +0 -551
- package/src/transforms/transform.ts +0 -453
- package/src/traverse.ts +0 -120
- package/src/types.ts +0 -131
- package/src/util/compare.ts +0 -181
- package/src/util/gen.ts +0 -651
- package/src/util/guard.ts +0 -7
- package/src/util/identifiers.ts +0 -494
- package/src/util/insert.ts +0 -419
- package/src/util/math.ts +0 -15
- package/src/util/object.ts +0 -39
- package/src/util/random.ts +0 -141
- package/src/util/scope.ts +0 -21
- package/test/code/Cash.src.js +0 -1011
- package/test/code/Cash.test.ts +0 -49
- package/test/code/Dynamic.src.js +0 -118
- package/test/code/Dynamic.test.ts +0 -49
- package/test/code/ES6.src.js +0 -235
- package/test/code/ES6.test.ts +0 -42
- package/test/code/NewFeatures.test.ts +0 -19
- package/test/code/StrictMode.src.js +0 -65
- package/test/code/StrictMode.test.js +0 -37
- package/test/compare.test.ts +0 -104
- package/test/index.test.ts +0 -249
- package/test/options.test.ts +0 -132
- package/test/presets.test.ts +0 -22
- package/test/probability.test.ts +0 -44
- package/test/templates/template.test.ts +0 -14
- package/test/transforms/antiTooling.test.ts +0 -52
- package/test/transforms/calculator.test.ts +0 -78
- package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
- package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
- package/test/transforms/deadCode.test.ts +0 -85
- package/test/transforms/dispatcher.test.ts +0 -457
- package/test/transforms/es5/antiClass.test.ts +0 -427
- package/test/transforms/es5/antiDestructuring.test.ts +0 -157
- package/test/transforms/es5/antiES6Object.test.ts +0 -245
- package/test/transforms/es5/antiTemplate.test.ts +0 -116
- package/test/transforms/es5/es5.test.ts +0 -110
- package/test/transforms/extraction/classExtraction.test.ts +0 -86
- package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
- package/test/transforms/extraction/objectExtraction.test.ts +0 -491
- package/test/transforms/flatten.test.ts +0 -721
- package/test/transforms/hexadecimalNumbers.test.ts +0 -62
- package/test/transforms/identifier/globalConcealing.test.ts +0 -72
- package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
- package/test/transforms/identifier/renameVariables.test.ts +0 -621
- package/test/transforms/lock/antiDebug.test.ts +0 -66
- package/test/transforms/lock/browserLock.test.ts +0 -129
- package/test/transforms/lock/countermeasures.test.ts +0 -100
- package/test/transforms/lock/integrity.test.ts +0 -161
- package/test/transforms/lock/lock.test.ts +0 -204
- package/test/transforms/lock/osLock.test.ts +0 -312
- package/test/transforms/lock/selfDefending.test.ts +0 -68
- package/test/transforms/minify.test.ts +0 -575
- package/test/transforms/opaquePredicates.test.ts +0 -43
- package/test/transforms/preparation.test.ts +0 -157
- package/test/transforms/renameLabels.test.ts +0 -95
- package/test/transforms/rgf.test.ts +0 -378
- package/test/transforms/shuffle.test.ts +0 -135
- package/test/transforms/stack.test.ts +0 -573
- package/test/transforms/string/stringCompression.test.ts +0 -120
- package/test/transforms/string/stringConcealing.test.ts +0 -299
- package/test/transforms/string/stringEncoding.test.ts +0 -95
- package/test/transforms/string/stringSplitting.test.ts +0 -135
- package/test/transforms/transform.test.ts +0 -66
- package/test/traverse.test.ts +0 -139
- package/test/util/compare.test.ts +0 -34
- package/test/util/gen.test.ts +0 -121
- package/test/util/identifiers.test.ts +0 -253
- package/test/util/insert.test.ts +0 -142
- package/test/util/math.test.ts +0 -5
- package/test/util/random.test.ts +0 -71
- /package/dist/{types.js → obfuscationResult.js} +0 -0
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _traverse = _interopRequireWildcard(require("@babel/traverse"));
|
|
8
|
+
var _order = require("../order");
|
|
9
|
+
var _probability = require("../probability");
|
|
10
|
+
var _astUtils = require("../utils/ast-utils");
|
|
11
|
+
var t = _interopRequireWildcard(require("@babel/types"));
|
|
12
|
+
var _node = require("../utils/node");
|
|
13
|
+
var _template = _interopRequireDefault(require("../templates/template"));
|
|
14
|
+
var _randomUtils = require("../utils/random-utils");
|
|
15
|
+
var _IntGen = require("../utils/IntGen");
|
|
16
|
+
var _assert = require("assert");
|
|
17
|
+
var _NameGen = require("../utils/NameGen");
|
|
18
|
+
var _constants = require("../constants");
|
|
19
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
20
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
21
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
|
|
22
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
23
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
24
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
25
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
26
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
27
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
28
|
+
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
29
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
30
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
31
|
+
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
32
|
+
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
33
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
34
|
+
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
35
|
+
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
36
|
+
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
37
|
+
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; }
|
|
38
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
39
|
+
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); }
|
|
40
|
+
/**
|
|
41
|
+
* Breaks functions into DAGs (Directed Acyclic Graphs)
|
|
42
|
+
*
|
|
43
|
+
* - 1. Break functions into chunks
|
|
44
|
+
* - 2. Shuffle chunks but remember their original position
|
|
45
|
+
* - 3. Create a Switch statement inside a While loop, each case is a chunk, and the while loops exits on the last transition.
|
|
46
|
+
*
|
|
47
|
+
* The Switch statement:
|
|
48
|
+
*
|
|
49
|
+
* - 1. The state variable controls which case will run next
|
|
50
|
+
* - 2. At the end of each case, the state variable is updated to the next block of code.
|
|
51
|
+
* - 3. The while loop continues until the the state variable is the end state.
|
|
52
|
+
*/
|
|
53
|
+
var _default = exports["default"] = function _default(_ref) {
|
|
54
|
+
var Plugin = _ref.Plugin;
|
|
55
|
+
var me = Plugin(_order.Order.ControlFlowFlattening, {
|
|
56
|
+
changeData: {
|
|
57
|
+
functions: 0,
|
|
58
|
+
blocks: 0,
|
|
59
|
+
ifStatements: 0,
|
|
60
|
+
deadCode: 0,
|
|
61
|
+
variables: 0
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// in Debug mode, the output is much easier to read
|
|
66
|
+
var isDebug = false;
|
|
67
|
+
var flattenIfStatements = true; // Converts IF-statements into equivalent 'goto style of code'
|
|
68
|
+
var flattenFunctionDeclarations = true; // Converts Function Declarations into equivalent 'goto style of code'
|
|
69
|
+
var addRelativeAssignments = true; // state += (NEW_STATE - CURRENT_STATE)
|
|
70
|
+
var addDeadCode = true; // add fakes chunks of code
|
|
71
|
+
var addFakeTests = true; // case 100: case 490: case 510: ...
|
|
72
|
+
var addComplexTests = true; // case s != 49 && s - 10:
|
|
73
|
+
var addPredicateTests = true; // case scope.A + 10: ...
|
|
74
|
+
var mangleNumericalLiterals = true; // 50 => state + X
|
|
75
|
+
var mangleBooleanLiterals = true; // true => state == X
|
|
76
|
+
var addWithStatement = true; // Disabling not supported yet
|
|
77
|
+
|
|
78
|
+
var cffPrefix = me.getPlaceholder();
|
|
79
|
+
|
|
80
|
+
// Amount of blocks changed by Control Flow Flattening
|
|
81
|
+
var cffCounter = 0;
|
|
82
|
+
var functionsModified = new Set();
|
|
83
|
+
return {
|
|
84
|
+
post: function post() {
|
|
85
|
+
functionsModified.forEach(function (node) {
|
|
86
|
+
node[_constants.UNSAFE] = true;
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
visitor: {
|
|
90
|
+
"Program|Function": {
|
|
91
|
+
exit: function exit(_path) {
|
|
92
|
+
var programOrFunctionPath = _path;
|
|
93
|
+
|
|
94
|
+
// Exclude loops
|
|
95
|
+
if (programOrFunctionPath.find(function (p) {
|
|
96
|
+
return p.isForStatement() || p.isWhile();
|
|
97
|
+
})) return;
|
|
98
|
+
var programPath = _path.isProgram() ? _path : null;
|
|
99
|
+
var functionPath = _path.isFunction() ? _path : null;
|
|
100
|
+
var blockPath;
|
|
101
|
+
if (programPath) {
|
|
102
|
+
blockPath = programPath;
|
|
103
|
+
} else {
|
|
104
|
+
var fnBlockPath = functionPath.get("body");
|
|
105
|
+
if (!fnBlockPath.isBlock()) return;
|
|
106
|
+
blockPath = fnBlockPath;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Don't apply to strict mode blocks
|
|
110
|
+
var strictModeEnforcingBlock = programOrFunctionPath.find(function (path) {
|
|
111
|
+
return (0, _astUtils.isStrictMode)(path);
|
|
112
|
+
});
|
|
113
|
+
if (strictModeEnforcingBlock) return;
|
|
114
|
+
|
|
115
|
+
// Must be at least 3 statements or more
|
|
116
|
+
if (blockPath.node.body.length < 3) return;
|
|
117
|
+
|
|
118
|
+
// Check user's threshold setting
|
|
119
|
+
if (!(0, _probability.computeProbabilityMap)(me.options.controlFlowFlattening)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Avoid unsafe functions
|
|
124
|
+
if (functionPath && functionPath.node[_constants.UNSAFE]) return;
|
|
125
|
+
programOrFunctionPath.scope.crawl();
|
|
126
|
+
var blockFnParent = (0, _astUtils.getParentFunctionOrProgram)(blockPath);
|
|
127
|
+
var hasIllegalNode = false;
|
|
128
|
+
var bindingNames = new Set();
|
|
129
|
+
blockPath.traverse({
|
|
130
|
+
"Super|MetaProperty|AwaitExpression|YieldExpression": function SuperMetaPropertyAwaitExpressionYieldExpression(path) {
|
|
131
|
+
if ((0, _astUtils.getParentFunctionOrProgram)(path).node === blockFnParent.node) {
|
|
132
|
+
hasIllegalNode = true;
|
|
133
|
+
path.stop();
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
VariableDeclaration: function VariableDeclaration(path) {
|
|
137
|
+
if (path.node.declarations.length !== 1) {
|
|
138
|
+
hasIllegalNode = true;
|
|
139
|
+
path.stop();
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
Identifier: function Identifier(path) {
|
|
143
|
+
if (path.node.name === _constants.variableFunctionName || path.node.name === "arguments") {
|
|
144
|
+
hasIllegalNode = true;
|
|
145
|
+
path.stop();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (!path.isBindingIdentifier()) return;
|
|
149
|
+
var binding = path.scope.getBinding(path.node.name);
|
|
150
|
+
if (!binding) return;
|
|
151
|
+
var fnParent = path.getFunctionParent();
|
|
152
|
+
if (path.key === "id" && path.parentPath.isFunctionDeclaration()) {
|
|
153
|
+
fnParent = path.parentPath.getFunctionParent();
|
|
154
|
+
}
|
|
155
|
+
if (fnParent !== functionPath) return;
|
|
156
|
+
if (!(0, _astUtils.isDefiningIdentifier)(path)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (bindingNames.has(path.node.name)) {
|
|
160
|
+
hasIllegalNode = true;
|
|
161
|
+
path.stop();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
bindingNames.add(path.node.name);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (hasIllegalNode) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
me.changeData.blocks++;
|
|
171
|
+
|
|
172
|
+
// Limit how many numbers get entangled
|
|
173
|
+
var mangledLiteralsCreated = 0;
|
|
174
|
+
var cffIndex = ++cffCounter; // Start from 1
|
|
175
|
+
var prefix = cffPrefix + "_" + cffIndex;
|
|
176
|
+
var withIdentifier = function withIdentifier(suffix) {
|
|
177
|
+
var name;
|
|
178
|
+
if (isDebug) {
|
|
179
|
+
name = prefix + "_" + suffix;
|
|
180
|
+
} else {
|
|
181
|
+
name = me.obfuscator.nameGen.generate(false);
|
|
182
|
+
}
|
|
183
|
+
var id = t.identifier(name);
|
|
184
|
+
id[_constants.NO_RENAME] = cffIndex;
|
|
185
|
+
return id;
|
|
186
|
+
};
|
|
187
|
+
var mainFnName = withIdentifier("main");
|
|
188
|
+
var scopeVar = withIdentifier("scope");
|
|
189
|
+
var stateVars = new Array(isDebug ? 1 : (0, _randomUtils.getRandomInteger)(2, 5)).fill("").map(function (_, i) {
|
|
190
|
+
return withIdentifier("state_".concat(i));
|
|
191
|
+
});
|
|
192
|
+
var argVar = withIdentifier("_arg");
|
|
193
|
+
var _didReturnVar = withIdentifier("return");
|
|
194
|
+
var basicBlocks = new Map();
|
|
195
|
+
|
|
196
|
+
// Map labels to states
|
|
197
|
+
var stateIntGen = new _IntGen.IntGen();
|
|
198
|
+
var defaultBlockPath = blockPath;
|
|
199
|
+
var scopeCounter = 0;
|
|
200
|
+
var scopeNameGen = new _NameGen.NameGen(me.options.identifierGenerator);
|
|
201
|
+
if (!isDebug) {
|
|
202
|
+
scopeNameGen = me.obfuscator.nameGen;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Create 'with' object - Determines which scope gets top-level variable access
|
|
206
|
+
var withProperty = isDebug ? "with" : scopeNameGen.generate(false);
|
|
207
|
+
var withMemberExpression = new _template["default"]("".concat(scopeVar.name, "[\"").concat(withProperty, "\"]")).expression();
|
|
208
|
+
withMemberExpression.object[_constants.NO_RENAME] = cffIndex;
|
|
209
|
+
|
|
210
|
+
// Create 'resetWith' function - Safely resets the 'with' object to none
|
|
211
|
+
var resetWithProperty = isDebug ? "resetWith" : scopeNameGen.generate(false);
|
|
212
|
+
var resetWithMemberExpression = new _template["default"]("".concat(scopeVar.name, "[\"").concat(resetWithProperty, "\"]")).expression();
|
|
213
|
+
resetWithMemberExpression.object[_constants.NO_RENAME] = cffIndex;
|
|
214
|
+
var ScopeManager = /*#__PURE__*/function () {
|
|
215
|
+
function ScopeManager(scope, initializingBasicBlock) {
|
|
216
|
+
_classCallCheck(this, ScopeManager);
|
|
217
|
+
_defineProperty(this, "isNotUsed", true);
|
|
218
|
+
_defineProperty(this, "requiresInitializing", true);
|
|
219
|
+
_defineProperty(this, "nameMap", new Map());
|
|
220
|
+
_defineProperty(this, "nameGen", addWithStatement ? me.obfuscator.nameGen : new _NameGen.NameGen(me.options.identifierGenerator));
|
|
221
|
+
this.scope = scope;
|
|
222
|
+
this.initializingBasicBlock = initializingBasicBlock;
|
|
223
|
+
this.propertyName = isDebug ? "_" + scopeCounter++ : scopeNameGen.generate();
|
|
224
|
+
}
|
|
225
|
+
return _createClass(ScopeManager, [{
|
|
226
|
+
key: "findBestWithDiscriminant",
|
|
227
|
+
value: function findBestWithDiscriminant(basicBlock) {
|
|
228
|
+
var _this$parent;
|
|
229
|
+
if (basicBlock !== this.initializingBasicBlock) {
|
|
230
|
+
if (this.nameMap.size > 0) return this;
|
|
231
|
+
}
|
|
232
|
+
return (_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.findBestWithDiscriminant(basicBlock);
|
|
233
|
+
}
|
|
234
|
+
}, {
|
|
235
|
+
key: "getNewName",
|
|
236
|
+
value: function getNewName(name, originalNode) {
|
|
237
|
+
if (!this.nameMap.has(name)) {
|
|
238
|
+
var newName = this.nameGen.generate(false);
|
|
239
|
+
if (isDebug) {
|
|
240
|
+
newName = "_" + name;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// console.log(name, newName);
|
|
244
|
+
|
|
245
|
+
this.nameMap.set(name, newName);
|
|
246
|
+
me.changeData.variables++;
|
|
247
|
+
|
|
248
|
+
// console.log(
|
|
249
|
+
// "Renaming " +
|
|
250
|
+
// name +
|
|
251
|
+
// " to " +
|
|
252
|
+
// newName +
|
|
253
|
+
// " : " +
|
|
254
|
+
// this.scope.path.type
|
|
255
|
+
// );
|
|
256
|
+
|
|
257
|
+
return newName;
|
|
258
|
+
}
|
|
259
|
+
return this.nameMap.get(name);
|
|
260
|
+
}
|
|
261
|
+
}, {
|
|
262
|
+
key: "getScopeObject",
|
|
263
|
+
value: function getScopeObject() {
|
|
264
|
+
return t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(this.propertyName), true);
|
|
265
|
+
}
|
|
266
|
+
}, {
|
|
267
|
+
key: "getInitializingStatement",
|
|
268
|
+
value: function getInitializingStatement() {
|
|
269
|
+
return t.expressionStatement(t.assignmentExpression("=", this.getScopeObject(), this.getInitializingObjectExpression()));
|
|
270
|
+
}
|
|
271
|
+
}, {
|
|
272
|
+
key: "getInitializingObjectExpression",
|
|
273
|
+
value: function getInitializingObjectExpression() {
|
|
274
|
+
return isDebug ? new _template["default"]("\n ({\n identity: \"".concat(this.propertyName, "\"\n })\n ")).expression() : new _template["default"]("Object[\"create\"](null)").expression();
|
|
275
|
+
}
|
|
276
|
+
}, {
|
|
277
|
+
key: "getMemberExpression",
|
|
278
|
+
value: function getMemberExpression(name) {
|
|
279
|
+
var memberExpression = t.memberExpression(this.getScopeObject(), t.stringLiteral(name), true);
|
|
280
|
+
return memberExpression;
|
|
281
|
+
}
|
|
282
|
+
}, {
|
|
283
|
+
key: "parent",
|
|
284
|
+
get: function get() {
|
|
285
|
+
return scopeToScopeManager.get(this.scope.parent);
|
|
286
|
+
}
|
|
287
|
+
}, {
|
|
288
|
+
key: "getObjectExpression",
|
|
289
|
+
value: function getObjectExpression(refreshLabel) {
|
|
290
|
+
var refreshScope = basicBlocks.get(refreshLabel).scopeManager;
|
|
291
|
+
var propertyMap = {};
|
|
292
|
+
var cursor = this.scope;
|
|
293
|
+
while (cursor) {
|
|
294
|
+
var parentScopeManager = scopeToScopeManager.get(cursor);
|
|
295
|
+
if (parentScopeManager) {
|
|
296
|
+
propertyMap[parentScopeManager.propertyName] = t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(parentScopeManager.propertyName), true);
|
|
297
|
+
}
|
|
298
|
+
cursor = cursor.parent;
|
|
299
|
+
}
|
|
300
|
+
propertyMap[refreshScope.propertyName] = refreshScope.getInitializingObjectExpression();
|
|
301
|
+
var properties = [];
|
|
302
|
+
for (var key in propertyMap) {
|
|
303
|
+
properties.push(t.objectProperty(t.stringLiteral(key), propertyMap[key], true));
|
|
304
|
+
}
|
|
305
|
+
return t.objectExpression(properties);
|
|
306
|
+
}
|
|
307
|
+
}, {
|
|
308
|
+
key: "hasOwnName",
|
|
309
|
+
value: function hasOwnName(name) {
|
|
310
|
+
return this.nameMap.has(name);
|
|
311
|
+
}
|
|
312
|
+
}]);
|
|
313
|
+
}();
|
|
314
|
+
var getImpossibleBasicBlocks = function getImpossibleBasicBlocks() {
|
|
315
|
+
return Array.from(basicBlocks.values()).filter(function (block) {
|
|
316
|
+
return block.options.impossible;
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
var scopeToScopeManager = new Map();
|
|
320
|
+
/**
|
|
321
|
+
* A Basic Block is a sequence of instructions with no diversion except at the entry and exit points.
|
|
322
|
+
*/
|
|
323
|
+
var BasicBlock = /*#__PURE__*/function () {
|
|
324
|
+
function BasicBlock(label, parentPath) {
|
|
325
|
+
var _this = this;
|
|
326
|
+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
327
|
+
_classCallCheck(this, BasicBlock);
|
|
328
|
+
_defineProperty(this, "allowWithDiscriminant", true);
|
|
329
|
+
this.label = label;
|
|
330
|
+
this.parentPath = parentPath;
|
|
331
|
+
this.options = options;
|
|
332
|
+
this.createPath();
|
|
333
|
+
if (isDebug) {
|
|
334
|
+
// States in debug mode are just 1, 2, 3, ...
|
|
335
|
+
this.totalState = basicBlocks.size + 1;
|
|
336
|
+
} else {
|
|
337
|
+
this.totalState = stateIntGen.generate();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Correct state values
|
|
341
|
+
// Start with random numbers
|
|
342
|
+
this.stateValues = stateVars.map(function () {
|
|
343
|
+
return (0, _randomUtils.getRandomInteger)(-250, 250);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Try to re-use old state values to make diffs smaller
|
|
347
|
+
if (basicBlocks.size > 1) {
|
|
348
|
+
var lastBlock = _toConsumableArray(basicBlocks.values()).at(-1);
|
|
349
|
+
this.stateValues = lastBlock.stateValues.map(function (oldValue, i) {
|
|
350
|
+
return (0, _randomUtils.choice)([oldValue, _this.stateValues[i]]);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Correct one of the values so that the accumulated sum is equal to the state
|
|
355
|
+
var correctIndex = (0, _randomUtils.getRandomInteger)(0, this.stateValues.length);
|
|
356
|
+
var getCurrentState = function getCurrentState() {
|
|
357
|
+
return _this.stateValues.reduce(function (a, b) {
|
|
358
|
+
return a + b;
|
|
359
|
+
}, 0);
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// Correct the value
|
|
363
|
+
this.stateValues[correctIndex] = this.totalState - (getCurrentState() - this.stateValues[correctIndex]);
|
|
364
|
+
(0, _assert.ok)(getCurrentState() === this.totalState);
|
|
365
|
+
|
|
366
|
+
// Store basic block
|
|
367
|
+
basicBlocks.set(label, this);
|
|
368
|
+
|
|
369
|
+
// Create a new scope manager if it doesn't exist
|
|
370
|
+
if (!scopeToScopeManager.has(this.scope)) {
|
|
371
|
+
scopeToScopeManager.set(this.scope, new ScopeManager(this.scope, this));
|
|
372
|
+
}
|
|
373
|
+
this.initializedScope = this.scopeManager;
|
|
374
|
+
}
|
|
375
|
+
return _createClass(BasicBlock, [{
|
|
376
|
+
key: "withDiscriminant",
|
|
377
|
+
get: function get() {
|
|
378
|
+
if (!this.allowWithDiscriminant) return null;
|
|
379
|
+
return this.bestWithDiscriminant;
|
|
380
|
+
}
|
|
381
|
+
}, {
|
|
382
|
+
key: "createPath",
|
|
383
|
+
value: function createPath() {
|
|
384
|
+
var newPath = _traverse.NodePath.get({
|
|
385
|
+
hub: this.parentPath.hub,
|
|
386
|
+
parentPath: this.parentPath,
|
|
387
|
+
parent: this.parentPath.node,
|
|
388
|
+
container: this.parentPath.node.body,
|
|
389
|
+
listKey: "body",
|
|
390
|
+
// Set the correct list key
|
|
391
|
+
key: "virtual" // Set the index of the new node
|
|
392
|
+
});
|
|
393
|
+
newPath.scope = this.parentPath.scope;
|
|
394
|
+
newPath.parentPath = this.parentPath;
|
|
395
|
+
newPath.node = t.blockStatement([]);
|
|
396
|
+
this.thisPath = newPath;
|
|
397
|
+
this.thisNode = newPath.node;
|
|
398
|
+
}
|
|
399
|
+
}, {
|
|
400
|
+
key: "insertAfter",
|
|
401
|
+
value: function insertAfter(newNode) {
|
|
402
|
+
this.body.push(newNode);
|
|
403
|
+
}
|
|
404
|
+
}, {
|
|
405
|
+
key: "scope",
|
|
406
|
+
get: function get() {
|
|
407
|
+
return this.parentPath.scope;
|
|
408
|
+
}
|
|
409
|
+
}, {
|
|
410
|
+
key: "scopeManager",
|
|
411
|
+
get: function get() {
|
|
412
|
+
return scopeToScopeManager.get(this.scope);
|
|
413
|
+
}
|
|
414
|
+
}, {
|
|
415
|
+
key: "body",
|
|
416
|
+
get: function get() {
|
|
417
|
+
return this.thisPath.node.body;
|
|
418
|
+
}
|
|
419
|
+
}, {
|
|
420
|
+
key: "createFalsePredicate",
|
|
421
|
+
value: function createFalsePredicate() {
|
|
422
|
+
var predicate = this.createPredicate();
|
|
423
|
+
if (predicate.value) {
|
|
424
|
+
// Make predicate false
|
|
425
|
+
return t.unaryExpression("!", predicate.node);
|
|
426
|
+
}
|
|
427
|
+
return predicate.node;
|
|
428
|
+
}
|
|
429
|
+
}, {
|
|
430
|
+
key: "createTruePredicate",
|
|
431
|
+
value: function createTruePredicate() {
|
|
432
|
+
var predicate = this.createPredicate();
|
|
433
|
+
if (!predicate.value) {
|
|
434
|
+
// Make predicate true
|
|
435
|
+
return t.unaryExpression("!", predicate.node);
|
|
436
|
+
}
|
|
437
|
+
return predicate.node;
|
|
438
|
+
}
|
|
439
|
+
}, {
|
|
440
|
+
key: "createPredicate",
|
|
441
|
+
value: function createPredicate() {
|
|
442
|
+
var stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
|
|
443
|
+
var stateValue = this.stateValues[stateVarIndex];
|
|
444
|
+
var compareValue = (0, _randomUtils.choice)([stateValue, (0, _randomUtils.getRandomInteger)(-250, 250)]);
|
|
445
|
+
var operator = (0, _randomUtils.choice)(["==", "!=", "<", ">"]);
|
|
446
|
+
var compareResult;
|
|
447
|
+
switch (operator) {
|
|
448
|
+
case "==":
|
|
449
|
+
compareResult = stateValue === compareValue;
|
|
450
|
+
break;
|
|
451
|
+
case "!=":
|
|
452
|
+
compareResult = stateValue !== compareValue;
|
|
453
|
+
break;
|
|
454
|
+
case "<":
|
|
455
|
+
compareResult = stateValue < compareValue;
|
|
456
|
+
break;
|
|
457
|
+
case ">":
|
|
458
|
+
compareResult = stateValue > compareValue;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
node: t.binaryExpression(operator, (0, _node.deepClone)(stateVars[stateVarIndex]), (0, _node.numericLiteral)(compareValue)),
|
|
463
|
+
value: compareResult
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}, {
|
|
467
|
+
key: "identifier",
|
|
468
|
+
value: function identifier(identifierName, scopeManager) {
|
|
469
|
+
if (this.withDiscriminant && this.withDiscriminant === scopeManager) {
|
|
470
|
+
var id = t.identifier(identifierName);
|
|
471
|
+
id[_constants.NO_RENAME] = cffIndex;
|
|
472
|
+
id[_constants.WITH_STATEMENT] = true;
|
|
473
|
+
return id;
|
|
474
|
+
}
|
|
475
|
+
return scopeManager.getMemberExpression(identifierName);
|
|
476
|
+
}
|
|
477
|
+
}]);
|
|
478
|
+
}();
|
|
479
|
+
/**
|
|
480
|
+
* Stage 1: Flatten the code into Basic Blocks
|
|
481
|
+
*
|
|
482
|
+
* This involves transforming the Control Flow / Scopes into blocks with 'goto' statements
|
|
483
|
+
*
|
|
484
|
+
* - A block is simply a sequence of statements
|
|
485
|
+
* - A block can have a 'goto' statement to another block
|
|
486
|
+
* - A block original scope is preserved
|
|
487
|
+
*
|
|
488
|
+
* 'goto' & Scopes are transformed in Stage 2
|
|
489
|
+
*/
|
|
490
|
+
var switchLabel = me.getPlaceholder();
|
|
491
|
+
var breakStatement = function breakStatement() {
|
|
492
|
+
return t.breakStatement(t.identifier(switchLabel));
|
|
493
|
+
};
|
|
494
|
+
var startLabel = me.getPlaceholder();
|
|
495
|
+
var endLabel = me.getPlaceholder();
|
|
496
|
+
var currentBasicBlock = new BasicBlock(startLabel, blockPath);
|
|
497
|
+
currentBasicBlock.allowWithDiscriminant = false;
|
|
498
|
+
var gotoFunctionName = "GOTO__" + me.getPlaceholder() + "__IF_YOU_CAN_READ_THIS_THERE_IS_A_BUG";
|
|
499
|
+
function GotoControlStatement(label) {
|
|
500
|
+
return new _template["default"]("\n ".concat(gotoFunctionName, "(\"").concat(label, "\");\n ")).single();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Ends the current block and starts a new one
|
|
504
|
+
function endCurrentBasicBlock() {
|
|
505
|
+
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
506
|
+
_ref2$jumpToNext = _ref2.jumpToNext,
|
|
507
|
+
jumpToNext = _ref2$jumpToNext === void 0 ? true : _ref2$jumpToNext,
|
|
508
|
+
_ref2$nextLabel = _ref2.nextLabel,
|
|
509
|
+
nextLabel = _ref2$nextLabel === void 0 ? me.getPlaceholder() : _ref2$nextLabel,
|
|
510
|
+
_ref2$prevJumpTo = _ref2.prevJumpTo,
|
|
511
|
+
prevJumpTo = _ref2$prevJumpTo === void 0 ? null : _ref2$prevJumpTo,
|
|
512
|
+
_ref2$nextBlockPath = _ref2.nextBlockPath,
|
|
513
|
+
nextBlockPath = _ref2$nextBlockPath === void 0 ? null : _ref2$nextBlockPath;
|
|
514
|
+
(0, _assert.ok)(nextBlockPath);
|
|
515
|
+
if (prevJumpTo) {
|
|
516
|
+
currentBasicBlock.insertAfter(GotoControlStatement(prevJumpTo));
|
|
517
|
+
} else if (jumpToNext) {
|
|
518
|
+
currentBasicBlock.insertAfter(GotoControlStatement(nextLabel));
|
|
519
|
+
}
|
|
520
|
+
currentBasicBlock = new BasicBlock(nextLabel, nextBlockPath);
|
|
521
|
+
}
|
|
522
|
+
var prependNodes = [];
|
|
523
|
+
var functionExpressions = [];
|
|
524
|
+
function flattenIntoBasicBlocks(bodyIn) {
|
|
525
|
+
// if (!Array.isArray(bodyIn) && bodyIn.isBlock()) {
|
|
526
|
+
// currentBasicBlock.parentPath = bodyIn;
|
|
527
|
+
// }
|
|
528
|
+
var body = Array.isArray(bodyIn) ? bodyIn : bodyIn.get("body");
|
|
529
|
+
var nextBlockPath = Array.isArray(bodyIn) ? currentBasicBlock.parentPath : bodyIn;
|
|
530
|
+
var _loop = function _loop() {
|
|
531
|
+
var statement = body[index];
|
|
532
|
+
|
|
533
|
+
// Keep Imports before everything else
|
|
534
|
+
if (statement.isImportDeclaration()) {
|
|
535
|
+
prependNodes.push(statement.node);
|
|
536
|
+
return 0; // continue
|
|
537
|
+
}
|
|
538
|
+
if (statement.isFunctionDeclaration()) {
|
|
539
|
+
var fnName = statement.node.id.name;
|
|
540
|
+
var isIllegal = false;
|
|
541
|
+
if (!flattenFunctionDeclarations || statement.node.async || statement.node.generator || statement.node[_constants.UNSAFE]) {
|
|
542
|
+
isIllegal = true;
|
|
543
|
+
}
|
|
544
|
+
var oldBasicBlock = currentBasicBlock;
|
|
545
|
+
var _fnLabel = me.getPlaceholder();
|
|
546
|
+
var sm = currentBasicBlock.scopeManager;
|
|
547
|
+
var rename = sm.getNewName(fnName);
|
|
548
|
+
sm.scope.bindings[fnName].kind = "var";
|
|
549
|
+
var hoistedBasicBlock = Array.from(basicBlocks.values()).find(function (block) {
|
|
550
|
+
return block.parentPath === currentBasicBlock.parentPath;
|
|
551
|
+
});
|
|
552
|
+
if (isIllegal) {
|
|
553
|
+
hoistedBasicBlock.body.unshift(statement.node);
|
|
554
|
+
return 0; // continue
|
|
555
|
+
}
|
|
556
|
+
me.changeData.functions++;
|
|
557
|
+
var functionExpression = t.functionExpression(null, [], t.blockStatement([]));
|
|
558
|
+
functionExpressions.push([fnName, _fnLabel, currentBasicBlock, functionExpression]);
|
|
559
|
+
|
|
560
|
+
// Change the function declaration to a variable declaration
|
|
561
|
+
hoistedBasicBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(fnName), functionExpression)]));
|
|
562
|
+
var blockStatement = statement.get("body");
|
|
563
|
+
endCurrentBasicBlock({
|
|
564
|
+
nextLabel: _fnLabel,
|
|
565
|
+
nextBlockPath: blockStatement,
|
|
566
|
+
jumpToNext: false
|
|
567
|
+
});
|
|
568
|
+
var fnTopBlock = currentBasicBlock;
|
|
569
|
+
|
|
570
|
+
// Implicit return
|
|
571
|
+
blockStatement.node.body.push(t.returnStatement(t.identifier("undefined")));
|
|
572
|
+
flattenIntoBasicBlocks(blockStatement);
|
|
573
|
+
scopeToScopeManager.get(statement.scope).requiresInitializing = false;
|
|
574
|
+
basicBlocks.get(_fnLabel).allowWithDiscriminant = false;
|
|
575
|
+
|
|
576
|
+
// Debug label
|
|
577
|
+
if (isDebug) {
|
|
578
|
+
fnTopBlock.body.unshift(t.expressionStatement(t.stringLiteral("Function " + statement.node.id.name + " -> Renamed to " + rename)));
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Unpack parameters
|
|
582
|
+
if (statement.node.params.length > 0) {
|
|
583
|
+
fnTopBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern(statement.node.params), (0, _node.deepClone)(argVar))]));
|
|
584
|
+
|
|
585
|
+
// Change bindings from 'param' to 'var'
|
|
586
|
+
statement.get("params").forEach(function (param) {
|
|
587
|
+
var ids = param.getBindingIdentifierPaths();
|
|
588
|
+
// Loop over the record of binding identifiers
|
|
589
|
+
for (var identifierName in ids) {
|
|
590
|
+
var identifierPath = ids[identifierName];
|
|
591
|
+
if (identifierPath.getFunctionParent() === statement) {
|
|
592
|
+
var binding = statement.scope.getBinding(identifierName);
|
|
593
|
+
if (binding) {
|
|
594
|
+
binding.kind = "var";
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
currentBasicBlock = oldBasicBlock;
|
|
601
|
+
return 0; // continue
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Convert IF statements into Basic Blocks
|
|
605
|
+
if (statement.isIfStatement() && flattenIfStatements) {
|
|
606
|
+
var test = statement.get("test");
|
|
607
|
+
var consequent = statement.get("consequent");
|
|
608
|
+
var alternate = statement.get("alternate");
|
|
609
|
+
|
|
610
|
+
// Both consequent and alternate are blocks
|
|
611
|
+
if (consequent.isBlockStatement() && (!alternate.node || alternate.isBlockStatement())) {
|
|
612
|
+
me.changeData.ifStatements++;
|
|
613
|
+
var consequentLabel = me.getPlaceholder();
|
|
614
|
+
var alternateLabel = alternate.node ? me.getPlaceholder() : null;
|
|
615
|
+
var afterPath = me.getPlaceholder();
|
|
616
|
+
currentBasicBlock.insertAfter(t.ifStatement(test.node, GotoControlStatement(consequentLabel), alternateLabel ? GotoControlStatement(alternateLabel) : GotoControlStatement(afterPath)));
|
|
617
|
+
var _oldBasicBlock = currentBasicBlock;
|
|
618
|
+
endCurrentBasicBlock({
|
|
619
|
+
jumpToNext: false,
|
|
620
|
+
nextLabel: consequentLabel,
|
|
621
|
+
nextBlockPath: consequent
|
|
622
|
+
});
|
|
623
|
+
flattenIntoBasicBlocks(consequent);
|
|
624
|
+
currentBasicBlock.initializedScope = _oldBasicBlock.scopeManager;
|
|
625
|
+
if (alternate.isBlockStatement()) {
|
|
626
|
+
endCurrentBasicBlock({
|
|
627
|
+
prevJumpTo: afterPath,
|
|
628
|
+
nextLabel: alternateLabel,
|
|
629
|
+
nextBlockPath: alternate
|
|
630
|
+
});
|
|
631
|
+
flattenIntoBasicBlocks(alternate);
|
|
632
|
+
}
|
|
633
|
+
endCurrentBasicBlock({
|
|
634
|
+
prevJumpTo: afterPath,
|
|
635
|
+
nextLabel: afterPath,
|
|
636
|
+
nextBlockPath: _oldBasicBlock.parentPath
|
|
637
|
+
});
|
|
638
|
+
return 0; // continue
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (Number(index) === body.length - 1 && statement.isExpressionStatement() && statement.findParent(function (p) {
|
|
642
|
+
return p.isBlock();
|
|
643
|
+
}) === blockPath) {
|
|
644
|
+
// Return the result of the last expression for eval() purposes
|
|
645
|
+
currentBasicBlock.insertAfter(t.returnStatement(statement.get("expression").node));
|
|
646
|
+
return 0; // continue
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// 3 or more statements should be split more
|
|
650
|
+
if (currentBasicBlock.body.length > 1 && (0, _randomUtils.chance)(50 + currentBasicBlock.body.length)) {
|
|
651
|
+
endCurrentBasicBlock({
|
|
652
|
+
nextBlockPath: nextBlockPath
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// console.log(currentBasicBlock.thisPath.type);
|
|
657
|
+
// console.log(currentBasicBlock.body);
|
|
658
|
+
currentBasicBlock.body.push(statement.node);
|
|
659
|
+
},
|
|
660
|
+
_ret;
|
|
661
|
+
for (var index in body) {
|
|
662
|
+
_ret = _loop();
|
|
663
|
+
if (_ret === 0) continue;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Convert our code into Basic Blocks
|
|
668
|
+
flattenIntoBasicBlocks(blockPath.get("body"));
|
|
669
|
+
|
|
670
|
+
// Ensure always jumped to the Program end
|
|
671
|
+
endCurrentBasicBlock({
|
|
672
|
+
jumpToNext: true,
|
|
673
|
+
nextLabel: endLabel,
|
|
674
|
+
nextBlockPath: defaultBlockPath
|
|
675
|
+
});
|
|
676
|
+
basicBlocks.get(endLabel).allowWithDiscriminant = false;
|
|
677
|
+
|
|
678
|
+
// Add with / reset with logic
|
|
679
|
+
basicBlocks.get(startLabel).body.unshift(new _template["default"]("\n {resetWithMemberExpression} = function(newStateValues){\n {withMemberExpression} = undefined;\n {arrayPattern} = newStateValues\n }\n ").single({
|
|
680
|
+
arrayPattern: t.arrayPattern((0, _node.deepClone)(stateVars)),
|
|
681
|
+
resetWithMemberExpression: (0, _node.deepClone)(resetWithMemberExpression),
|
|
682
|
+
withMemberExpression: (0, _node.deepClone)(withMemberExpression)
|
|
683
|
+
}));
|
|
684
|
+
if (!isDebug && addDeadCode) {
|
|
685
|
+
// DEAD CODE 1/3: Add fake chunks that are never reached
|
|
686
|
+
var fakeChunkCount = (0, _randomUtils.getRandomInteger)(1, 5);
|
|
687
|
+
for (var i = 0; i < fakeChunkCount; i++) {
|
|
688
|
+
// These chunks just jump somewhere random, they are never executed
|
|
689
|
+
// so it could contain any code
|
|
690
|
+
var fakeBlock = new BasicBlock(me.getPlaceholder(), blockPath, {
|
|
691
|
+
impossible: true
|
|
692
|
+
});
|
|
693
|
+
var fakeJump = void 0;
|
|
694
|
+
do {
|
|
695
|
+
fakeJump = (0, _randomUtils.choice)(Array.from(basicBlocks.keys()));
|
|
696
|
+
} while (fakeJump === fakeBlock.label);
|
|
697
|
+
fakeBlock.insertAfter(GotoControlStatement(fakeJump));
|
|
698
|
+
me.changeData.deadCode++;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// DEAD CODE 2/3: Add fake jumps to really mess with deobfuscators
|
|
702
|
+
// "irreducible control flow"
|
|
703
|
+
basicBlocks.forEach(function (basicBlock) {
|
|
704
|
+
if ((0, _randomUtils.chance)(30 - basicBlocks.size)) {
|
|
705
|
+
var randomLabel = (0, _randomUtils.choice)(Array.from(basicBlocks.keys()));
|
|
706
|
+
|
|
707
|
+
// The `false` literal will be mangled
|
|
708
|
+
basicBlock.insertAfter(new _template["default"]("\n if({predicate}){\n {goto}\n }\n ").single({
|
|
709
|
+
"goto": GotoControlStatement(randomLabel),
|
|
710
|
+
predicate: basicBlock.createFalsePredicate()
|
|
711
|
+
}));
|
|
712
|
+
me.changeData.deadCode++;
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
// DEAD CODE 3/3: Clone chunks but these chunks are never ran
|
|
716
|
+
var cloneChunkCount = (0, _randomUtils.getRandomInteger)(1, 5);
|
|
717
|
+
var _loop2 = function _loop2() {
|
|
718
|
+
var randomChunk = (0, _randomUtils.choice)(Array.from(basicBlocks.values()));
|
|
719
|
+
|
|
720
|
+
// Don't double define functions
|
|
721
|
+
var hasDeclaration = randomChunk.body.find(function (stmt) {
|
|
722
|
+
return t.isDeclaration(stmt);
|
|
723
|
+
});
|
|
724
|
+
if (!hasDeclaration) {
|
|
725
|
+
var clonedChunk = new BasicBlock(me.getPlaceholder(), randomChunk.parentPath, {
|
|
726
|
+
impossible: true
|
|
727
|
+
});
|
|
728
|
+
randomChunk.thisNode.body.map(function (x) {
|
|
729
|
+
return (0, _node.deepClone)(x);
|
|
730
|
+
}).forEach(function (node) {
|
|
731
|
+
if (node.type === "EmptyStatement") return;
|
|
732
|
+
clonedChunk.insertAfter(node);
|
|
733
|
+
});
|
|
734
|
+
me.changeData.deadCode++;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
for (var _i = 0; _i < cloneChunkCount; _i++) {
|
|
738
|
+
_loop2();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Select scope managers for the with statement
|
|
743
|
+
var _iterator = _createForOfIteratorHelper(basicBlocks.values()),
|
|
744
|
+
_step;
|
|
745
|
+
try {
|
|
746
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
747
|
+
var _basicBlock2$initiali;
|
|
748
|
+
var _basicBlock2 = _step.value;
|
|
749
|
+
_basicBlock2.bestWithDiscriminant = (_basicBlock2$initiali = _basicBlock2.initializedScope) === null || _basicBlock2$initiali === void 0 ? void 0 : _basicBlock2$initiali.findBestWithDiscriminant(_basicBlock2);
|
|
750
|
+
if (isDebug && _basicBlock2.withDiscriminant) {
|
|
751
|
+
_basicBlock2.body.unshift(t.expressionStatement(t.stringLiteral("With " + _basicBlock2.withDiscriminant.propertyName)));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Stage 2: Transform 'goto' statements into valid JavaScript
|
|
757
|
+
*
|
|
758
|
+
* - 'goto' is replaced with equivalent state updates and break statements
|
|
759
|
+
* - Original identifiers are converted into member expressions
|
|
760
|
+
*/
|
|
761
|
+
|
|
762
|
+
// Remap 'GotoStatement' to actual state assignments and Break statements
|
|
763
|
+
} catch (err) {
|
|
764
|
+
_iterator.e(err);
|
|
765
|
+
} finally {
|
|
766
|
+
_iterator.f();
|
|
767
|
+
}
|
|
768
|
+
var _iterator2 = _createForOfIteratorHelper(basicBlocks.values()),
|
|
769
|
+
_step2;
|
|
770
|
+
try {
|
|
771
|
+
var _loop5 = function _loop5() {
|
|
772
|
+
var basicBlock = _step2.value;
|
|
773
|
+
var currentStateValues = basicBlock.stateValues;
|
|
774
|
+
// Wrap the statement in a Babel path to allow traversal
|
|
775
|
+
|
|
776
|
+
var outerFn = (0, _astUtils.getParentFunctionOrProgram)(basicBlock.parentPath);
|
|
777
|
+
function isWithinSameFunction(path) {
|
|
778
|
+
var fn = (0, _astUtils.getParentFunctionOrProgram)(path);
|
|
779
|
+
return fn.node === outerFn.node;
|
|
780
|
+
}
|
|
781
|
+
var visitor = {
|
|
782
|
+
BooleanLiteral: {
|
|
783
|
+
exit: function exit(boolPath) {
|
|
784
|
+
// Don't mangle booleans in debug mode
|
|
785
|
+
if (isDebug || !mangleBooleanLiterals || me.isSkipped(boolPath)) return;
|
|
786
|
+
if (!isWithinSameFunction(boolPath)) return;
|
|
787
|
+
if ((0, _randomUtils.chance)(50 + mangledLiteralsCreated)) return;
|
|
788
|
+
mangledLiteralsCreated++;
|
|
789
|
+
var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
|
|
790
|
+
var stateVar = stateVars[index];
|
|
791
|
+
var stateVarValue = currentStateValues[index];
|
|
792
|
+
var compareValue = (0, _randomUtils.choice)([(0, _randomUtils.getRandomInteger)(-250, 250), stateVarValue]);
|
|
793
|
+
var compareResult = stateVarValue === compareValue;
|
|
794
|
+
var newExpression = t.binaryExpression(boolPath.node.value === compareResult ? "==" : "!=", (0, _node.deepClone)(stateVar), (0, _node.numericLiteral)(compareValue));
|
|
795
|
+
(0, _astUtils.ensureComputedExpression)(boolPath);
|
|
796
|
+
boolPath.replaceWith(newExpression);
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
// Mangle numbers with the state values
|
|
800
|
+
NumericLiteral: {
|
|
801
|
+
exit: function exit(numPath) {
|
|
802
|
+
// Don't mangle numbers in debug mode
|
|
803
|
+
if (isDebug || !mangleNumericalLiterals || me.isSkipped(numPath)) return;
|
|
804
|
+
var num = numPath.node.value;
|
|
805
|
+
if (Math.floor(num) !== num || Math.abs(num) > 100000 || !Number.isFinite(num) || Number.isNaN(num)) return;
|
|
806
|
+
if (!isWithinSameFunction(numPath)) return;
|
|
807
|
+
if ((0, _randomUtils.chance)(50 + mangledLiteralsCreated)) return;
|
|
808
|
+
mangledLiteralsCreated++;
|
|
809
|
+
var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
|
|
810
|
+
var stateVar = stateVars[index];
|
|
811
|
+
|
|
812
|
+
// num = 50
|
|
813
|
+
// stateVar = 30
|
|
814
|
+
// stateVar + 30
|
|
815
|
+
|
|
816
|
+
var diff = t.binaryExpression("+", (0, _node.deepClone)(stateVar), me.skip((0, _node.numericLiteral)(num - currentStateValues[index])));
|
|
817
|
+
(0, _astUtils.ensureComputedExpression)(numPath);
|
|
818
|
+
numPath.replaceWith(diff);
|
|
819
|
+
numPath.skip();
|
|
820
|
+
}
|
|
821
|
+
},
|
|
822
|
+
Identifier: {
|
|
823
|
+
exit: function exit(path) {
|
|
824
|
+
if (!(0, _astUtils.isVariableIdentifier)(path)) return;
|
|
825
|
+
if (me.isSkipped(path)) return;
|
|
826
|
+
if (path.node[_constants.NO_RENAME] === cffIndex) return;
|
|
827
|
+
var identifierName = path.node.name;
|
|
828
|
+
if (identifierName === gotoFunctionName) return;
|
|
829
|
+
var binding = basicBlock.scope.getBinding(identifierName);
|
|
830
|
+
if (!binding) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
if (binding.kind === "var" || binding.kind === "let" || binding.kind === "const") {} else {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// console.log("No binding found for " + identifierName);
|
|
838
|
+
|
|
839
|
+
var scopeManager = scopeToScopeManager.get(binding.scope);
|
|
840
|
+
if (!scopeManager) return;
|
|
841
|
+
var newName = scopeManager.getNewName(identifierName, path.node);
|
|
842
|
+
var memberExpression = scopeManager.getMemberExpression(newName);
|
|
843
|
+
scopeManager.isNotUsed = false;
|
|
844
|
+
if ((0, _astUtils.isDefiningIdentifier)(path)) {
|
|
845
|
+
(0, _astUtils.replaceDefiningIdentifierToMemberExpression)(path, memberExpression);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
if (!path.container) return;
|
|
849
|
+
var isModified = (0, _astUtils.isModifiedIdentifier)(path);
|
|
850
|
+
if (basicBlock.withDiscriminant && basicBlock.withDiscriminant === scopeManager && basicBlock.withDiscriminant.hasOwnName(identifierName)) {
|
|
851
|
+
if (!isModified) {
|
|
852
|
+
memberExpression = basicBlock.identifier(newName, scopeManager);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
me.skip(memberExpression);
|
|
856
|
+
path.replaceWith(memberExpression);
|
|
857
|
+
path.skip();
|
|
858
|
+
}
|
|
859
|
+
},
|
|
860
|
+
// Top-level returns set additional flag to indicate that the function has returned
|
|
861
|
+
ReturnStatement: {
|
|
862
|
+
exit: function exit(path) {
|
|
863
|
+
var functionParent = path.getFunctionParent();
|
|
864
|
+
if (!functionParent || functionParent.get("body") !== blockPath) return;
|
|
865
|
+
var returnArgument = path.node.argument || t.identifier("undefined");
|
|
866
|
+
path.node.argument = new _template["default"]("\n ({didReturnVar} = true, {returnArgument})\n ").expression({
|
|
867
|
+
returnArgument: returnArgument,
|
|
868
|
+
didReturnVar: (0, _node.deepClone)(_didReturnVar)
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
// goto() calls are replaced with state updates and break statements
|
|
873
|
+
CallExpression: {
|
|
874
|
+
exit: function exit(path) {
|
|
875
|
+
if (t.isIdentifier(path.node.callee) && path.node.callee.name === gotoFunctionName) {
|
|
876
|
+
var _path$node$arguments = _slicedToArray(path.node.arguments, 1),
|
|
877
|
+
labelNode = _path$node$arguments[0];
|
|
878
|
+
(0, _assert.ok)(t.isStringLiteral(labelNode));
|
|
879
|
+
var _label = labelNode.value;
|
|
880
|
+
var jumpBlock = basicBlocks.get(_label);
|
|
881
|
+
(0, _assert.ok)(jumpBlock, "Label not found: " + _label);
|
|
882
|
+
var newStateValues = jumpBlock.stateValues,
|
|
883
|
+
newTotalState = jumpBlock.totalState;
|
|
884
|
+
var assignments = [];
|
|
885
|
+
var needsIndividualAssignments = true;
|
|
886
|
+
if (jumpBlock.withDiscriminant) {
|
|
887
|
+
assignments.push(t.assignmentExpression("=", (0, _node.deepClone)(withMemberExpression), jumpBlock.withDiscriminant.getScopeObject()));
|
|
888
|
+
} else if (basicBlock.withDiscriminant) {
|
|
889
|
+
assignments.push(t.callExpression((0, _node.deepClone)(resetWithMemberExpression), [t.arrayExpression(newStateValues.map(_node.numericLiteral))]));
|
|
890
|
+
needsIndividualAssignments = false;
|
|
891
|
+
}
|
|
892
|
+
if (needsIndividualAssignments) {
|
|
893
|
+
for (var _i7 = 0; _i7 < stateVars.length; _i7++) {
|
|
894
|
+
var oldValue = currentStateValues[_i7];
|
|
895
|
+
var newValue = newStateValues[_i7];
|
|
896
|
+
|
|
897
|
+
// console.log(oldValue, newValue);
|
|
898
|
+
if (oldValue === newValue) continue; // No diff needed if the value doesn't change
|
|
899
|
+
|
|
900
|
+
var leftValue = jumpBlock.withDiscriminant ? jumpBlock.withDiscriminant.getMemberExpression(stateVars[_i7].name) : (0, _node.deepClone)(stateVars[_i7]);
|
|
901
|
+
var assignment = t.assignmentExpression("=", leftValue, (0, _node.numericLiteral)(newValue));
|
|
902
|
+
if (!isDebug && addRelativeAssignments) {
|
|
903
|
+
// Use diffs to create confusing code
|
|
904
|
+
assignment = t.assignmentExpression("+=", (0, _node.deepClone)(stateVars[_i7]), (0, _node.numericLiteral)(newValue - oldValue));
|
|
905
|
+
}
|
|
906
|
+
assignments.push(assignment);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Add debug label
|
|
911
|
+
if (isDebug) {
|
|
912
|
+
assignments.unshift(t.stringLiteral("Goto " + newTotalState));
|
|
913
|
+
}
|
|
914
|
+
path.parentPath.replaceWith(t.expressionStatement(t.sequenceExpression(assignments)))[0].skip();
|
|
915
|
+
|
|
916
|
+
// Add break after updating state variables
|
|
917
|
+
path.insertAfter(breakStatement());
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
basicBlock.thisPath.traverse(visitor);
|
|
923
|
+
};
|
|
924
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
925
|
+
_loop5();
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Stage 3: Create a switch statement to handle the control flow
|
|
930
|
+
*
|
|
931
|
+
* - Add fake / impossible blocks
|
|
932
|
+
* - Add fake / predicates to the switch cases tests
|
|
933
|
+
*/
|
|
934
|
+
|
|
935
|
+
// Create global numbers for predicates
|
|
936
|
+
} catch (err) {
|
|
937
|
+
_iterator2.e(err);
|
|
938
|
+
} finally {
|
|
939
|
+
_iterator2.f();
|
|
940
|
+
}
|
|
941
|
+
var mainScope = basicBlocks.get(startLabel).scopeManager;
|
|
942
|
+
var predicateNumbers = new Map();
|
|
943
|
+
var predicateNumberCount = isDebug || !addPredicateTests ? 0 : (0, _randomUtils.getRandomInteger)(2, 5);
|
|
944
|
+
var _loop3 = function _loop3() {
|
|
945
|
+
var name = mainScope.getNewName(me.getPlaceholder("predicate_" + _i2));
|
|
946
|
+
var number = (0, _randomUtils.getRandomInteger)(-250, 250);
|
|
947
|
+
predicateNumbers.set(name, number);
|
|
948
|
+
var createAssignment = function createAssignment(value) {
|
|
949
|
+
return new _template["default"]("\n {memberExpression} = {number}\n ").single({
|
|
950
|
+
memberExpression: mainScope.getMemberExpression(name),
|
|
951
|
+
number: (0, _node.numericLiteral)(number)
|
|
952
|
+
});
|
|
953
|
+
};
|
|
954
|
+
basicBlocks.get(startLabel).body.unshift(createAssignment(number));
|
|
955
|
+
|
|
956
|
+
// Add random assignments to impossible blocks
|
|
957
|
+
fakeAssignmentCount = (0, _randomUtils.getRandomInteger)(0, 3);
|
|
958
|
+
for (var _i8 = 0; _i8 < fakeAssignmentCount; _i8++) {
|
|
959
|
+
impossibleBlock = (0, _randomUtils.choice)(getImpossibleBasicBlocks());
|
|
960
|
+
if (impossibleBlock) {
|
|
961
|
+
impossibleBlock.body.unshift(createAssignment((0, _randomUtils.getRandomInteger)(-250, 250)));
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
fakeAssignmentCount,
|
|
966
|
+
impossibleBlock;
|
|
967
|
+
for (var _i2 = 0; _i2 < predicateNumberCount; _i2++) {
|
|
968
|
+
_loop3();
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Add scope initializations: scope["_0"] = {identity: "_0"}
|
|
972
|
+
var _iterator3 = _createForOfIteratorHelper(scopeToScopeManager.values()),
|
|
973
|
+
_step3;
|
|
974
|
+
try {
|
|
975
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
976
|
+
var _scopeManager = _step3.value;
|
|
977
|
+
if (_scopeManager.isNotUsed) continue;
|
|
978
|
+
if (!_scopeManager.requiresInitializing) continue;
|
|
979
|
+
if (_scopeManager.initializingBasicBlock.label === startLabel) continue;
|
|
980
|
+
_scopeManager.initializingBasicBlock.body.unshift(_scopeManager.getInitializingStatement());
|
|
981
|
+
}
|
|
982
|
+
} catch (err) {
|
|
983
|
+
_iterator3.e(err);
|
|
984
|
+
} finally {
|
|
985
|
+
_iterator3.f();
|
|
986
|
+
}
|
|
987
|
+
var switchCases = [];
|
|
988
|
+
var blocks = Array.from(basicBlocks.values());
|
|
989
|
+
if (!isDebug && addFakeTests) {
|
|
990
|
+
(0, _randomUtils.shuffle)(blocks);
|
|
991
|
+
}
|
|
992
|
+
var _loop4 = function _loop4() {
|
|
993
|
+
var block = _blocks[_i3];
|
|
994
|
+
if (block.label === endLabel) {
|
|
995
|
+
// ok(block.body.length === 0);
|
|
996
|
+
return 1; // continue
|
|
997
|
+
}
|
|
998
|
+
var test = (0, _node.numericLiteral)(block.totalState);
|
|
999
|
+
|
|
1000
|
+
// Predicate tests cannot apply to the start label
|
|
1001
|
+
// As that's when the numbers are initialized
|
|
1002
|
+
if (!isDebug && addPredicateTests && block.label !== startLabel && (0, _randomUtils.chance)(50)) {
|
|
1003
|
+
var predicateName = (0, _randomUtils.choice)(Array.from(predicateNumbers.keys()));
|
|
1004
|
+
if (predicateName) {
|
|
1005
|
+
var number = predicateNumbers.get(predicateName);
|
|
1006
|
+
var diff = block.totalState - number;
|
|
1007
|
+
test = t.binaryExpression("+", mainScope.getMemberExpression(predicateName), (0, _node.numericLiteral)(diff));
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Add complex tests
|
|
1012
|
+
if (!isDebug && addComplexTests && (0, _randomUtils.chance)(50)) {
|
|
1013
|
+
// Create complex test expressions for each switch case
|
|
1014
|
+
|
|
1015
|
+
// case STATE+X:
|
|
1016
|
+
var stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
|
|
1017
|
+
var stateValues = block.stateValues;
|
|
1018
|
+
var difference = stateValues[stateVarIndex] - block.totalState;
|
|
1019
|
+
var conditionNodes = [];
|
|
1020
|
+
var alreadyConditionedItems = new Set();
|
|
1021
|
+
|
|
1022
|
+
// This code finds clash conditions and adds them to 'conditionNodes' array
|
|
1023
|
+
Array.from(basicBlocks.keys()).forEach(function (label) {
|
|
1024
|
+
if (label !== block.label) {
|
|
1025
|
+
var labelStates = basicBlocks.get(label).stateValues;
|
|
1026
|
+
var totalState = labelStates.reduce(function (a, b) {
|
|
1027
|
+
return a + b;
|
|
1028
|
+
}, 0);
|
|
1029
|
+
if (totalState === labelStates[stateVarIndex] - difference) {
|
|
1030
|
+
var differentIndex = labelStates.findIndex(function (v, i) {
|
|
1031
|
+
return v !== stateValues[i];
|
|
1032
|
+
});
|
|
1033
|
+
if (differentIndex !== -1) {
|
|
1034
|
+
var expressionAsString = stateVars[differentIndex].name + "!=" + labelStates[differentIndex];
|
|
1035
|
+
if (!alreadyConditionedItems.has(expressionAsString)) {
|
|
1036
|
+
alreadyConditionedItems.add(expressionAsString);
|
|
1037
|
+
conditionNodes.push(t.binaryExpression("!=", (0, _node.deepClone)(stateVars[differentIndex]), (0, _node.numericLiteral)(labelStates[differentIndex])));
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
conditionNodes.push(t.binaryExpression("!=", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(totalState)));
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// case STATE!=Y && STATE+X
|
|
1047
|
+
test = t.binaryExpression("-", (0, _node.deepClone)(stateVars[stateVarIndex]), (0, _node.numericLiteral)(difference));
|
|
1048
|
+
|
|
1049
|
+
// Use the 'conditionNodes' to not cause state clashing issues
|
|
1050
|
+
conditionNodes.forEach(function (conditionNode) {
|
|
1051
|
+
test = t.logicalExpression("&&", conditionNode, test);
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
var tests = [test];
|
|
1055
|
+
if (!isDebug && addFakeTests && (0, _randomUtils.chance)(50)) {
|
|
1056
|
+
// Add fake tests
|
|
1057
|
+
var fakeTestCount = (0, _randomUtils.getRandomInteger)(1, 3);
|
|
1058
|
+
for (var _i4 = 0; _i4 < fakeTestCount; _i4++) {
|
|
1059
|
+
tests.push((0, _node.numericLiteral)(stateIntGen.generate()));
|
|
1060
|
+
}
|
|
1061
|
+
(0, _randomUtils.shuffle)(tests);
|
|
1062
|
+
}
|
|
1063
|
+
var lastTest = tests.pop();
|
|
1064
|
+
for (var _i5 = 0, _tests = tests; _i5 < _tests.length; _i5++) {
|
|
1065
|
+
var _test = _tests[_i5];
|
|
1066
|
+
switchCases.push(t.switchCase(_test, []));
|
|
1067
|
+
}
|
|
1068
|
+
switchCases.push(t.switchCase(lastTest, block.thisPath.node.body));
|
|
1069
|
+
};
|
|
1070
|
+
for (var _i3 = 0, _blocks = blocks; _i3 < _blocks.length; _i3++) {
|
|
1071
|
+
if (_loop4()) continue;
|
|
1072
|
+
}
|
|
1073
|
+
if (!isDebug && addFakeTests) {
|
|
1074
|
+
// A random test can be 'default'
|
|
1075
|
+
(0, _randomUtils.choice)(switchCases).test = null;
|
|
1076
|
+
}
|
|
1077
|
+
var discriminant = new _template["default"]("\n ".concat(stateVars.map(function (x) {
|
|
1078
|
+
return x.name;
|
|
1079
|
+
}).join(" + "), "\n ")).expression();
|
|
1080
|
+
(0, _traverse["default"])(t.program([t.expressionStatement(discriminant)]), {
|
|
1081
|
+
Identifier: function Identifier(path) {
|
|
1082
|
+
path.node[_constants.NO_RENAME] = cffIndex;
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
// Create a new SwitchStatement
|
|
1087
|
+
var switchStatement = t.labeledStatement(t.identifier(switchLabel), t.switchStatement(discriminant, switchCases));
|
|
1088
|
+
var startStateValues = basicBlocks.get(startLabel).stateValues;
|
|
1089
|
+
var endTotalState = basicBlocks.get(endLabel).totalState;
|
|
1090
|
+
var whileStatement = t.whileStatement(t.binaryExpression("!==", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(endTotalState)), t.blockStatement([t.withStatement(new _template["default"]("{withDiscriminant} || Object[\"create\"](null)").expression({
|
|
1091
|
+
withDiscriminant: (0, _node.deepClone)(withMemberExpression)
|
|
1092
|
+
}), t.blockStatement([switchStatement]))]));
|
|
1093
|
+
var parameters = [].concat(_toConsumableArray(stateVars), [argVar, scopeVar]).map(function (id) {
|
|
1094
|
+
return (0, _node.deepClone)(id);
|
|
1095
|
+
});
|
|
1096
|
+
var parametersNames = parameters.map(function (id) {
|
|
1097
|
+
return id.name;
|
|
1098
|
+
});
|
|
1099
|
+
for (var _i6 = 0, _functionExpressions = functionExpressions; _i6 < _functionExpressions.length; _i6++) {
|
|
1100
|
+
var _functionExpressions$ = _slicedToArray(_functionExpressions[_i6], 4),
|
|
1101
|
+
originalFnName = _functionExpressions$[0],
|
|
1102
|
+
fnLabel = _functionExpressions$[1],
|
|
1103
|
+
basicBlock = _functionExpressions$[2],
|
|
1104
|
+
fn = _functionExpressions$[3];
|
|
1105
|
+
var _basicBlock = basicBlock,
|
|
1106
|
+
scopeManager = _basicBlock.scopeManager;
|
|
1107
|
+
var _basicBlocks$get = basicBlocks.get(fnLabel),
|
|
1108
|
+
stateValues = _basicBlocks$get.stateValues;
|
|
1109
|
+
var argumentsRestName = me.getPlaceholder();
|
|
1110
|
+
var argumentsNodes = [];
|
|
1111
|
+
var _iterator4 = _createForOfIteratorHelper(parametersNames),
|
|
1112
|
+
_step4;
|
|
1113
|
+
try {
|
|
1114
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
1115
|
+
var parameterName = _step4.value;
|
|
1116
|
+
var stateIndex = stateVars.map(function (x) {
|
|
1117
|
+
return x.name;
|
|
1118
|
+
}).indexOf(parameterName);
|
|
1119
|
+
if (stateIndex !== -1) {
|
|
1120
|
+
argumentsNodes.push((0, _node.numericLiteral)(stateValues[stateIndex]));
|
|
1121
|
+
} else if (parameterName === argVar.name) {
|
|
1122
|
+
argumentsNodes.push(t.identifier(argumentsRestName));
|
|
1123
|
+
} else if (parameterName === scopeVar.name) {
|
|
1124
|
+
argumentsNodes.push(scopeManager.getObjectExpression(fnLabel));
|
|
1125
|
+
} else {
|
|
1126
|
+
(0, _assert.ok)(false);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
} catch (err) {
|
|
1130
|
+
_iterator4.e(err);
|
|
1131
|
+
} finally {
|
|
1132
|
+
_iterator4.f();
|
|
1133
|
+
}
|
|
1134
|
+
Object.assign(fn, new _template["default"]("\n (function (...".concat(argumentsRestName, "){\n ").concat(isDebug ? "\"Calling ".concat(originalFnName, ", Label: ").concat(fnLabel, "\";") : "", "\n return {callExpression}\n })\n \n ")).expression({
|
|
1135
|
+
callExpression: t.callExpression((0, _node.deepClone)(mainFnName), argumentsNodes)
|
|
1136
|
+
}));
|
|
1137
|
+
}
|
|
1138
|
+
var mainFnDeclaration = t.functionDeclaration((0, _node.deepClone)(mainFnName), parameters, t.blockStatement([whileStatement]));
|
|
1139
|
+
mainFnDeclaration[_constants.PREDICTABLE] = true;
|
|
1140
|
+
var startProgramExpression = t.callExpression((0, _node.deepClone)(mainFnName), [].concat(_toConsumableArray(startStateValues.map(function (stateValue) {
|
|
1141
|
+
return (0, _node.numericLiteral)(stateValue);
|
|
1142
|
+
})), [t.identifier("undefined"), basicBlocks.get(startLabel).scopeManager.getObjectExpression(startLabel)]));
|
|
1143
|
+
var _resultVar = withIdentifier("result");
|
|
1144
|
+
var allowReturns = blockPath.find(function (p) {
|
|
1145
|
+
return p.isFunction();
|
|
1146
|
+
});
|
|
1147
|
+
var startProgramStatements = new _template["default"]("\n ".concat(allowReturns ? "var {didReturnVar};" : "", "\n var {resultVar} = {startProgramExpression};\n ").concat(allowReturns ? "\n if({didReturnVar}){\n return {resultVar};\n }" : "", "\n ")).compile({
|
|
1148
|
+
startProgramExpression: startProgramExpression,
|
|
1149
|
+
didReturnVar: function didReturnVar() {
|
|
1150
|
+
return (0, _node.deepClone)(_didReturnVar);
|
|
1151
|
+
},
|
|
1152
|
+
resultVar: function resultVar() {
|
|
1153
|
+
return (0, _node.deepClone)(_resultVar);
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
blockPath.node.body = [].concat(prependNodes, [mainFnDeclaration], _toConsumableArray(startProgramStatements));
|
|
1157
|
+
functionsModified.add(programOrFunctionPath.node);
|
|
1158
|
+
|
|
1159
|
+
// Reset all bindings here
|
|
1160
|
+
blockPath.scope.bindings = Object.create(null);
|
|
1161
|
+
|
|
1162
|
+
// Bindings changed - breaking control objects
|
|
1163
|
+
delete blockPath.node[_constants.CONTROL_OBJECTS];
|
|
1164
|
+
|
|
1165
|
+
// Register new declarations
|
|
1166
|
+
var _iterator5 = _createForOfIteratorHelper(blockPath.get("body")),
|
|
1167
|
+
_step5;
|
|
1168
|
+
try {
|
|
1169
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
1170
|
+
var node = _step5.value;
|
|
1171
|
+
blockPath.scope.registerDeclaration(node);
|
|
1172
|
+
}
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
_iterator5.e(err);
|
|
1175
|
+
} finally {
|
|
1176
|
+
_iterator5.f();
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
};
|