node-automator 1.1.0 → 1.2.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 (31) hide show
  1. package/commands/deobfuscate.js +15 -0
  2. package/commands/mgr.js +2 -0
  3. package/package.json +7 -2
  4. package/utils/deobfuscator/deobfuscator.js +97 -0
  5. package/utils/deobfuscator/helpers/declaration.js +57 -0
  6. package/utils/deobfuscator/helpers/expression.js +36 -0
  7. package/utils/deobfuscator/helpers/misc.js +38 -0
  8. package/utils/deobfuscator/helpers/strings/decoders/base64StringDecoder.js +50 -0
  9. package/utils/deobfuscator/helpers/strings/decoders/basicStringDecoder.js +28 -0
  10. package/utils/deobfuscator/helpers/strings/decoders/rc4StringDecoder.js +77 -0
  11. package/utils/deobfuscator/helpers/strings/decoders/stringDecoder.js +22 -0
  12. package/utils/deobfuscator/helpers/strings/rotation/rotation.js +216 -0
  13. package/utils/deobfuscator/helpers/strings/util/util.js +30 -0
  14. package/utils/deobfuscator/helpers/variable.js +140 -0
  15. package/utils/deobfuscator/index.js +25 -0
  16. package/utils/deobfuscator/transformations/config.js +43 -0
  17. package/utils/deobfuscator/transformations/controlFlow/controlFlowRecoverer.js +160 -0
  18. package/utils/deobfuscator/transformations/controlFlow/deadBranchRemover.js +120 -0
  19. package/utils/deobfuscator/transformations/controlFlow/sequenceSplitter.js +168 -0
  20. package/utils/deobfuscator/transformations/expressions/expressionSimplifier.js +288 -0
  21. package/utils/deobfuscator/transformations/objects/objectPacker.js +125 -0
  22. package/utils/deobfuscator/transformations/objects/objectSimplifier.js +71 -0
  23. package/utils/deobfuscator/transformations/objects/proxyObject.js +144 -0
  24. package/utils/deobfuscator/transformations/properties/propertySimplifier.js +70 -0
  25. package/utils/deobfuscator/transformations/proxyFunctions/proxyFunction.js +151 -0
  26. package/utils/deobfuscator/transformations/proxyFunctions/proxyFunctionInliner.js +46 -0
  27. package/utils/deobfuscator/transformations/strings/stringRevealer.js +381 -0
  28. package/utils/deobfuscator/transformations/transformation.js +28 -0
  29. package/utils/deobfuscator/transformations/variables/constantPropagator.js +73 -0
  30. package/utils/deobfuscator/transformations/variables/reassignmentRemover.js +78 -0
  31. package/utils/deobfuscator/transformations/variables/unusedVariableRemover.js +93 -0
@@ -0,0 +1,15 @@
1
+ const { deobfuscate } = require('../utils/deobfuscator');
2
+ const { BaseCommand } = require("./base");
3
+
4
+ class DeobfuscateCommand extends BaseCommand {
5
+ async execute() {
6
+ return deobfuscate(this.content, this.selfData.sourceType, this.selfData.config);
7
+ }
8
+ getRequireContent() {
9
+ return true;
10
+ }
11
+ }
12
+
13
+ module.exports = {
14
+ DeobfuscateCommand,
15
+ };
package/commands/mgr.js CHANGED
@@ -114,6 +114,7 @@ const { SvnAddCommand } = require('./svn_add');
114
114
  const { ImageCropCommand } = require('./image_crop');
115
115
  const { CursorUpCommand } = require('./cursor_up');
116
116
  const { RegisterSearchInstallLocationCommand } = require('./register_search_install_location');
117
+ const { DeobfuscateCommand } = require('./deobfuscate');
117
118
 
118
119
  const globalData = {
119
120
  "executed_cfg": [], // 执行过的配置文件
@@ -469,6 +470,7 @@ function init() {
469
470
  register("image_crop", ImageCropCommand, false);
470
471
  register("cursor_up", CursorUpCommand, false);
471
472
  register("register_search_install_location", RegisterSearchInstallLocationCommand, false);
473
+ register("deobfuscate", DeobfuscateCommand, false);
472
474
  }
473
475
 
474
476
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-automator",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Execute automation with yaml configuration(compatible with json)",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -59,6 +59,11 @@
59
59
  "xmldom": "^0.4.0",
60
60
  "yaml": "^1.10.0",
61
61
  "yamljs": "^0.3.0",
62
- "yargs": "^16.2.0"
62
+ "yargs": "^16.2.0",
63
+ "@babel/generator": "^7.22.3",
64
+ "@babel/parser": "^7.22.4",
65
+ "@babel/traverse": "^7.22.4",
66
+ "@babel/types": "^7.22.4",
67
+ "@types/node": "^20.2.5"
63
68
  }
64
69
  }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Deobfuscator = void 0;
7
+ const generator_1 = __importDefault(require("@babel/generator"));
8
+ const traverse_1 = __importDefault(require("@babel/traverse"));
9
+ const config_1 = require("./transformations/config");
10
+ const objectSimplifier_1 = require("./transformations/objects/objectSimplifier");
11
+ const proxyFunctionInliner_1 = require("./transformations/proxyFunctions/proxyFunctionInliner");
12
+ const unusedVariableRemover_1 = require("./transformations/variables/unusedVariableRemover");
13
+ const constantPropagator_1 = require("./transformations/variables/constantPropagator");
14
+ const reassignmentRemover_1 = require("./transformations/variables/reassignmentRemover");
15
+ const stringRevealer_1 = require("./transformations/strings/stringRevealer");
16
+ const deadBranchRemover_1 = require("./transformations/controlFlow/deadBranchRemover");
17
+ const sequenceSplitter_1 = require("./transformations/controlFlow/sequenceSplitter");
18
+ const propertySimplifier_1 = require("./transformations/properties/propertySimplifier");
19
+ const expressionSimplifier_1 = require("./transformations/expressions/expressionSimplifier");
20
+ const controlFlowRecoverer_1 = require("./transformations/controlFlow/controlFlowRecoverer");
21
+ const objectPacker_1 = require("./transformations/objects/objectPacker");
22
+ class Deobfuscator {
23
+ /**
24
+ * Creates a new deobfuscator.
25
+ * @param ast The AST.
26
+ * @param config The config (optional).
27
+ */
28
+ constructor(ast, config = config_1.defaultConfig) {
29
+ this.transformationTypes = [
30
+ unusedVariableRemover_1.UnusedVariableRemover,
31
+ constantPropagator_1.ConstantPropgator,
32
+ reassignmentRemover_1.ReassignmentRemover,
33
+ deadBranchRemover_1.DeadBranchRemover,
34
+ objectPacker_1.ObjectPacker,
35
+ proxyFunctionInliner_1.ProxyFunctionInliner,
36
+ expressionSimplifier_1.ExpressionSimplifier,
37
+ sequenceSplitter_1.SequenceSplitter,
38
+ controlFlowRecoverer_1.ControlFlowRecoverer,
39
+ propertySimplifier_1.PropertySimplifier,
40
+ objectSimplifier_1.ObjectSimplifier,
41
+ stringRevealer_1.StringRevealer
42
+ ];
43
+ this.ast = ast;
44
+ this.config = config;
45
+ }
46
+ /**
47
+ * Executes the deobfuscator.
48
+ * @returns The simplified code.
49
+ */
50
+ execute() {
51
+ let types = this.transformationTypes.filter(t => this.config[t.properties.key].isEnabled);
52
+ let i = 0;
53
+ while (i < Deobfuscator.MAX_ITERATIONS) {
54
+ let isModified = false;
55
+ if (!this.config.silent) {
56
+ console.log(`\n[${new Date().toISOString()}]: Starting pass ${i + 1}`);
57
+ }
58
+ for (const type of types) {
59
+ const transformationConfig = this.config[type.properties.key];
60
+ const transformation = new type(this.ast, transformationConfig);
61
+ if (!this.config.silent) {
62
+ console.log(`[${new Date().toISOString()}]: Executing ${transformation.constructor.name}`);
63
+ }
64
+ let modified = false;
65
+ try {
66
+ modified = transformation.execute(console.log.bind(console, `[${transformation.constructor.name}]:`));
67
+ }
68
+ catch (err) {
69
+ console.log(`Error: ${err}`);
70
+ }
71
+ if (modified) {
72
+ isModified = true;
73
+ }
74
+ if (!this.config.silent) {
75
+ console.log(`[${new Date().toISOString()}]: Executed ${transformation.constructor.name}, modified ${modified}`);
76
+ }
77
+ if (type.properties.rebuildScopeTree) {
78
+ this.clearCache();
79
+ }
80
+ }
81
+ i++;
82
+ if (!isModified) {
83
+ break;
84
+ }
85
+ }
86
+ return (0, generator_1.default)(this.ast, { jsescOption: { minimal: true } }).code;
87
+ }
88
+ /**
89
+ * Clears the traversal cache to force the scoping to be handled
90
+ * again on the next traverse.
91
+ */
92
+ clearCache() {
93
+ traverse_1.default.cache.clear();
94
+ }
95
+ }
96
+ exports.Deobfuscator = Deobfuscator;
97
+ Deobfuscator.MAX_ITERATIONS = 50;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.isDeclarationOrAssignmentExpression = exports.isDeclarationOrAssignmentStatement = void 0;
27
+ const t = __importStar(require("@babel/types"));
28
+ /**
29
+ * Checks whether a node is a variable declaration or assignment expression
30
+ * within an expression statement that is initialising a variable that
31
+ * satisfies the provided constraints.
32
+ * @param node The AST node.
33
+ * @param isId The function that determines whether the variable being declared matches.
34
+ * @param isValue The function that determines whether the value the variable is initialised to matches.
35
+ * @returns Whether.
36
+ */
37
+ function isDeclarationOrAssignmentStatement(node, isId, isValue) {
38
+ return ((t.isVariableDeclaration(node) &&
39
+ node.declarations.length == 1 &&
40
+ isId(node.declarations[0].id) &&
41
+ node.declarations[0].init &&
42
+ isValue(node.declarations[0].init)) ||
43
+ (t.isExpressionStatement(node) &&
44
+ t.isAssignmentExpression(node.expression) &&
45
+ isId(node.expression.left) &&
46
+ isValue(node.expression.right)));
47
+ }
48
+ exports.isDeclarationOrAssignmentStatement = isDeclarationOrAssignmentStatement;
49
+ function isDeclarationOrAssignmentExpression(node, isId, isValue) {
50
+ return ((t.isVariableDeclaration(node) &&
51
+ node.declarations.length == 1 &&
52
+ isId(node.declarations[0].id) &&
53
+ node.declarations[0].init &&
54
+ isValue(node.declarations[0].init)) ||
55
+ (t.isAssignmentExpression(node) && isId(node.left) && isValue(node.right)));
56
+ }
57
+ exports.isDeclarationOrAssignmentExpression = isDeclarationOrAssignmentExpression;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.isNegativeNumericLiteral = void 0;
27
+ const t = __importStar(require("@babel/types"));
28
+ /**
29
+ * Returns whether a node is a unary expression that represents a negative number.
30
+ * @param node The AST node.
31
+ * @returns Whether.
32
+ */
33
+ function isNegativeNumericLiteral(node) {
34
+ return t.isUnaryExpression(node) && node.operator == '-' && t.isNumericLiteral(node.argument);
35
+ }
36
+ exports.isNegativeNumericLiteral = isNegativeNumericLiteral;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getProperty = exports.setProperty = exports.copyExpression = void 0;
7
+ const generator_1 = __importDefault(require("@babel/generator"));
8
+ /**
9
+ * Copies an expression.
10
+ * @param expression The expression.
11
+ * @returns The copy.
12
+ */
13
+ const copyExpression = (expression) => {
14
+ const parseExpression = globalThis.parser
15
+ .parseExpression;
16
+ return parseExpression((0, generator_1.default)(expression).code);
17
+ };
18
+ exports.copyExpression = copyExpression;
19
+ /**
20
+ * Sets a property on an object.
21
+ * @param obj The object.
22
+ * @param property The property key.
23
+ * @param value The value.
24
+ */
25
+ const setProperty = (obj, property, value) => {
26
+ obj.property = value;
27
+ };
28
+ exports.setProperty = setProperty;
29
+ /**
30
+ * Gets the value of a property on an object.
31
+ * @param obj The object.
32
+ * @param property The property key.
33
+ * @returns
34
+ */
35
+ const getProperty = (obj, property) => {
36
+ return obj.property;
37
+ };
38
+ exports.getProperty = getProperty;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Base64StringDecoder = void 0;
4
+ const util_1 = require("../util/util");
5
+ const stringDecoder_1 = require("./stringDecoder");
6
+ class Base64StringDecoder extends stringDecoder_1.StringDecoder {
7
+ /**
8
+ * Creates a new base 64 string decoder.
9
+ * @param stringArray The string array.
10
+ * @param indexOffset The offset used when accessing elements by index.
11
+ */
12
+ constructor(stringArray, indexOffset) {
13
+ super(stringArray, indexOffset);
14
+ this.stringCache = new Map();
15
+ }
16
+ /**
17
+ * Returns the type of the decoder.
18
+ */
19
+ get type() {
20
+ return stringDecoder_1.DecoderType.BASE_64;
21
+ }
22
+ /**
23
+ * Decodes a string.
24
+ * @param index The index.
25
+ * @returns The string.
26
+ */
27
+ getString(index) {
28
+ const cacheKey = index + this.stringArray[0];
29
+ if (this.stringCache.has(cacheKey)) {
30
+ return this.stringCache.get(cacheKey);
31
+ }
32
+ const encoded = this.stringArray[index + this.indexOffset];
33
+ const str = (0, util_1.base64Transform)(encoded);
34
+ this.stringCache.set(cacheKey, str);
35
+ return str;
36
+ }
37
+ /**
38
+ * Decodes a string for the rotate string call.
39
+ * @param index The index.
40
+ * @returns THe string.
41
+ */
42
+ getStringForRotation(index) {
43
+ if (this.isFirstCall) {
44
+ this.isFirstCall = false;
45
+ throw new Error();
46
+ }
47
+ return this.getString(index);
48
+ }
49
+ }
50
+ exports.Base64StringDecoder = Base64StringDecoder;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BasicStringDecoder = void 0;
4
+ const stringDecoder_1 = require("./stringDecoder");
5
+ class BasicStringDecoder extends stringDecoder_1.StringDecoder {
6
+ /**
7
+ * Returns the type of the decoder.
8
+ */
9
+ get type() {
10
+ return stringDecoder_1.DecoderType.BASIC;
11
+ }
12
+ /**
13
+ * Decodes a string.
14
+ * @param index The index.
15
+ */
16
+ getString(index) {
17
+ return this.stringArray[index + this.indexOffset];
18
+ }
19
+ /**
20
+ * Decodes a string for the rotate string call.
21
+ * @param index The index.
22
+ * @returns THe string.
23
+ */
24
+ getStringForRotation(index) {
25
+ return this.getString(index);
26
+ }
27
+ }
28
+ exports.BasicStringDecoder = BasicStringDecoder;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Rc4StringDecoder = void 0;
4
+ const util_1 = require("../util/util");
5
+ const stringDecoder_1 = require("./stringDecoder");
6
+ class Rc4StringDecoder extends stringDecoder_1.StringDecoder {
7
+ /**
8
+ * Creates a new RC4 string decoder.
9
+ * @param stringArray The string array.
10
+ * @param indexOffset The offset used when accessing elements by index.
11
+ */
12
+ constructor(stringArray, indexOffset) {
13
+ super(stringArray, indexOffset);
14
+ this.stringCache = new Map();
15
+ }
16
+ /**
17
+ * Returns the type of the decoder.
18
+ */
19
+ get type() {
20
+ return stringDecoder_1.DecoderType.RC4;
21
+ }
22
+ /**
23
+ * Decodes a string.
24
+ * @param index The index.
25
+ */
26
+ getString(index, key) {
27
+ const cacheKey = index + this.stringArray[0];
28
+ if (this.stringCache.has(cacheKey)) {
29
+ return this.stringCache.get(cacheKey);
30
+ }
31
+ const encoded = this.stringArray[index + this.indexOffset];
32
+ const str = this.rc4Decode(encoded, key);
33
+ this.stringCache.set(cacheKey, str);
34
+ return str;
35
+ }
36
+ /**
37
+ * Decodes a string for the rotate string call.
38
+ * @param index The index.
39
+ * @returns THe string.
40
+ */
41
+ getStringForRotation(index, key) {
42
+ if (this.isFirstCall) {
43
+ this.isFirstCall = false;
44
+ throw new Error();
45
+ }
46
+ return this.getString(index, key);
47
+ }
48
+ /**
49
+ * Decodes a string encoded with RC4.
50
+ * @param str The RC4 encoded string.
51
+ * @param key The key.
52
+ * @returns The decoded string.
53
+ */
54
+ rc4Decode(str, key) {
55
+ const s = [];
56
+ let j = 0;
57
+ let decoded = '';
58
+ str = (0, util_1.base64Transform)(str);
59
+ for (var i = 0; i < 256; i++) {
60
+ s[i] = i;
61
+ }
62
+ for (var i = 0; i < 256; i++) {
63
+ j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
64
+ [s[i], s[j]] = [s[j], s[i]];
65
+ }
66
+ i = 0;
67
+ j = 0;
68
+ for (let y = 0; y < str.length; y++) {
69
+ i = (i + 1) % 256;
70
+ j = (j + s[i]) % 256;
71
+ [s[i], s[j]] = [s[j], s[i]];
72
+ decoded += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]);
73
+ }
74
+ return decoded;
75
+ }
76
+ }
77
+ exports.Rc4StringDecoder = Rc4StringDecoder;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DecoderType = exports.StringDecoder = void 0;
4
+ class StringDecoder {
5
+ /**
6
+ * Creates a new string decoder.
7
+ * @param stringArray The string array.
8
+ * @param indexOffset The offset used when accessing elements by index.
9
+ */
10
+ constructor(stringArray, indexOffset) {
11
+ this.stringArray = stringArray;
12
+ this.indexOffset = indexOffset;
13
+ this.isFirstCall = true;
14
+ }
15
+ }
16
+ exports.StringDecoder = StringDecoder;
17
+ var DecoderType;
18
+ (function (DecoderType) {
19
+ DecoderType["BASIC"] = "BASIC";
20
+ DecoderType["BASE_64"] = "BASE_64";
21
+ DecoderType["RC4"] = "RC4";
22
+ })(DecoderType || (exports.DecoderType = DecoderType = {}));
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.rotateStringArray = void 0;
27
+ const t = __importStar(require("@babel/types"));
28
+ const expression_1 = require("../../expression");
29
+ const binaryOperatorSet = new Set(['+', '-', '*', '/', '%']);
30
+ const unaryOperatorSet = new Set(['-']);
31
+ const operationSet = new Set([
32
+ 'CallExpression',
33
+ 'UnaryExpression',
34
+ 'BinaryExpression',
35
+ 'NumericLiteral'
36
+ ]);
37
+ /**
38
+ * Parses an operation.
39
+ * @param expression The expression.
40
+ * @param decoderMap The string decoder map.
41
+ * @returns The operation.
42
+ */
43
+ function parseOperation(expression, decoderMap) {
44
+ switch (expression.type) {
45
+ case 'CallExpression':
46
+ return parseCallOperation(expression, decoderMap);
47
+ case 'UnaryExpression':
48
+ return parseUnaryOperation(expression, decoderMap);
49
+ case 'BinaryExpression':
50
+ return parseBinaryOperation(expression, decoderMap);
51
+ case 'NumericLiteral':
52
+ return expression;
53
+ }
54
+ }
55
+ /**
56
+ * Parses a call operation.
57
+ * @param expression The call expression.
58
+ * @param decoderMap The string decoder map.
59
+ * @returns The call operation.
60
+ */
61
+ function parseCallOperation(expression, decoderMap) {
62
+ if (!t.isIdentifier(expression.callee) ||
63
+ expression.callee.name != 'parseInt' ||
64
+ expression.arguments.length != 1 ||
65
+ !t.isCallExpression(expression.arguments[0])) {
66
+ throw new Error('Unsupported string call operation');
67
+ }
68
+ const stringCall = expression.arguments[0];
69
+ if (!t.isIdentifier(stringCall.callee) ||
70
+ !stringCall.arguments.every(e => t.isNumericLiteral(e) || (0, expression_1.isNegativeNumericLiteral)(e) || t.isStringLiteral(e))) {
71
+ throw new Error('Unsupported string call operation');
72
+ }
73
+ const args = stringCall.arguments.map(e => t.isNumericLiteral(e) || t.isStringLiteral(e) ? e.value : -e.argument.value);
74
+ const name = stringCall.callee.name;
75
+ if (!decoderMap.has(name)) {
76
+ throw new Error(`Unknown string decoder ${name}`);
77
+ }
78
+ const decoder = decoderMap.get(name);
79
+ return {
80
+ type: 'CallOperation',
81
+ decoder,
82
+ args
83
+ };
84
+ }
85
+ /**
86
+ * Parses a unary operation.
87
+ * @param expression The unary expression.
88
+ * @param decoderMap The string decoder map.
89
+ * @returns The unary operation.
90
+ */
91
+ function parseUnaryOperation(expression, decoderMap) {
92
+ if (!unaryOperatorSet.has(expression.operator)) {
93
+ throw new Error(`Unsupported unary operator ${expression.operator}`);
94
+ }
95
+ else if (!operationSet.has(expression.argument.type)) {
96
+ throw new Error(`Unsupported string rotation operation type ${expression.argument.type}`);
97
+ }
98
+ const argument = parseOperation(expression.argument, decoderMap);
99
+ return {
100
+ type: 'UnaryOperation',
101
+ operator: expression.operator,
102
+ argument
103
+ };
104
+ }
105
+ /**
106
+ * Parses a binary operation.
107
+ * @param expression The binary expression.
108
+ * @param decoderMap The string decoder map.
109
+ * @returns The binary operation.
110
+ */
111
+ function parseBinaryOperation(expression, decoderMap) {
112
+ if (!binaryOperatorSet.has(expression.operator)) {
113
+ throw new Error(`Unsupported binary operator ${expression.operator}`);
114
+ }
115
+ else if (!operationSet.has(expression.left.type)) {
116
+ throw new Error(`Unsupported string rotation operation type ${expression.left.type}`);
117
+ }
118
+ else if (!operationSet.has(expression.right.type)) {
119
+ throw new Error(`Unsupported string rotation operation type ${expression.right.type}`);
120
+ }
121
+ const left = parseOperation(expression.left, decoderMap);
122
+ const right = parseOperation(expression.right, decoderMap);
123
+ return {
124
+ type: 'BinaryOperation',
125
+ operator: expression.operator,
126
+ left,
127
+ right
128
+ };
129
+ }
130
+ /**
131
+ * Applies an operation.
132
+ * @param operation The operation.
133
+ * @returns The result.
134
+ */
135
+ function applyOperation(operation) {
136
+ switch (operation.type) {
137
+ case 'CallOperation':
138
+ return applyCall(operation);
139
+ case 'UnaryOperation':
140
+ return applyUnaryOperation(operation);
141
+ case 'BinaryOperation':
142
+ return applyBinaryOperation(operation);
143
+ case 'NumericLiteral':
144
+ return operation.value;
145
+ }
146
+ }
147
+ /**
148
+ * Applies a call of a string decoder.
149
+ * @param call The call.
150
+ * @returns The result.
151
+ */
152
+ function applyCall(call) {
153
+ return parseInt(call.decoder.getStringForRotation(...call.args));
154
+ }
155
+ /**
156
+ * Applies a unary operation.
157
+ * @param operation The unary operation.
158
+ * @returns The result.
159
+ */
160
+ function applyUnaryOperation(operation) {
161
+ const argument = applyOperation(operation.argument);
162
+ switch (operation.operator) {
163
+ case '-':
164
+ return -argument;
165
+ }
166
+ }
167
+ /**
168
+ * Applies a binary operation.
169
+ * @param operation The binary operation.
170
+ * @returns The result.
171
+ */
172
+ function applyBinaryOperation(operation) {
173
+ const left = applyOperation(operation.left);
174
+ const right = applyOperation(operation.right);
175
+ switch (operation.operator) {
176
+ case '+':
177
+ return left + right;
178
+ case '-':
179
+ return left - right;
180
+ case '*':
181
+ return left * right;
182
+ case '/':
183
+ return left / right;
184
+ case '%':
185
+ return left % right;
186
+ }
187
+ }
188
+ /**
189
+ * Rotates the string array.
190
+ * @param expression The expression containing the string array calls.
191
+ * @param decoderMap The string decoder map.
192
+ * @param stopValue The value to stop at.
193
+ */
194
+ function rotateStringArray(array, expression, decoderMap, stopValue) {
195
+ const operation = parseOperation(expression, decoderMap);
196
+ let i = 0;
197
+ while (true) {
198
+ try {
199
+ const value = applyOperation(operation);
200
+ if (value == stopValue) {
201
+ break;
202
+ }
203
+ else {
204
+ array.push(array.shift());
205
+ }
206
+ }
207
+ catch (err) {
208
+ array.push(array.shift());
209
+ }
210
+ // avoid entering infinite loops
211
+ if (i++ > 1e5) {
212
+ throw new Error('Max number of string rotation iterations reached');
213
+ }
214
+ }
215
+ }
216
+ exports.rotateStringArray = rotateStringArray;