js-confuser 1.4.2 → 1.4.3
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/CHANGELOG.md +18 -0
- package/README.md +4 -0
- package/dist/compiler.js +2 -1
- package/dist/obfuscator.js +3 -0
- package/dist/options.js +1 -1
- package/dist/order.js +1 -0
- package/dist/presets.js +3 -0
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +14 -2
- package/dist/transforms/deadCode.js +1 -1
- package/dist/transforms/hexadecimalNumbers.js +38 -0
- package/dist/transforms/minify.js +8 -3
- package/package.json +1 -1
- package/src/compiler.ts +5 -1
- package/src/obfuscator.ts +2 -0
- package/src/options.ts +8 -0
- package/src/order.ts +2 -0
- package/src/presets.ts +3 -0
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +33 -2
- package/src/transforms/deadCode.ts +102 -0
- package/src/transforms/hexadecimalNumbers.ts +31 -0
- package/src/transforms/minify.ts +9 -3
- package/test/transforms/hexadecimalNumbers.test.ts +62 -0
- package/test/transforms/minify.test.ts +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
<!-- # `1.5.0`
|
|
2
|
+
Hexadecimal Numbers
|
|
3
|
+
|
|
4
|
+
- The hexadecimal representation can now be used. [#29](https://github.com/MichaelXF/js-confuser/issues/29)
|
|
5
|
+
- New option `hexadecimalNumbers` to control this behavior.
|
|
6
|
+
|
|
7
|
+
### `hexadecimalNumbers`
|
|
8
|
+
|
|
9
|
+
Uses the hexadecimal representation (`50` -> `0x32`) for numbers. (`true/false`) -->
|
|
10
|
+
|
|
11
|
+
# `1.4.3`
|
|
12
|
+
Minify Fix
|
|
13
|
+
|
|
14
|
+
- Fixed [#34](https://github.com/MichaelXF/js-confuser/issues/34)
|
|
15
|
+
- - Minify was removing `return` statements too aggressively, fixed in this version.
|
|
16
|
+
|
|
17
|
+
- Slight improvement to Control Flow Flattening
|
|
18
|
+
|
|
1
19
|
# `1.4.2`
|
|
2
20
|
Eval Fix
|
|
3
21
|
|
package/README.md
CHANGED
|
@@ -115,6 +115,10 @@ JsConfuser.obfuscate(`<source code>`, {
|
|
|
115
115
|
|
|
116
116
|
Remove's whitespace from the final output. Enabled by default. (`true/false`)
|
|
117
117
|
|
|
118
|
+
<!-- ### `hexadecimalNumbers`
|
|
119
|
+
|
|
120
|
+
Uses the hexadecimal representation (`50` -> `0x32`) for numbers. (`true/false`) -->
|
|
121
|
+
|
|
118
122
|
### `minify`
|
|
119
123
|
|
|
120
124
|
Minifies redundant code. (`true/false`)
|
package/dist/compiler.js
CHANGED
package/dist/obfuscator.js
CHANGED
|
@@ -67,6 +67,8 @@ var _antiTooling = _interopRequireDefault(require("./transforms/antiTooling"));
|
|
|
67
67
|
|
|
68
68
|
var _hideInitializingCode = _interopRequireDefault(require("./transforms/hideInitializingCode"));
|
|
69
69
|
|
|
70
|
+
var _hexadecimalNumbers = _interopRequireDefault(require("./transforms/hexadecimalNumbers"));
|
|
71
|
+
|
|
70
72
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
71
73
|
|
|
72
74
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -128,6 +130,7 @@ class Obfuscator extends _events.EventEmitter {
|
|
|
128
130
|
test(options.stack, _stack.default);
|
|
129
131
|
test(true, _antiTooling.default);
|
|
130
132
|
test(options.hideInitializingCode, _hideInitializingCode.default);
|
|
133
|
+
test(options.hexadecimalNumbers, _hexadecimalNumbers.default);
|
|
131
134
|
|
|
132
135
|
if (options.lock && Object.keys(options.lock).filter(x => x == "domainLock" ? options.lock.domainLock && options.lock.domainLock.length : options.lock[x]).length) {
|
|
133
136
|
test(true, _lock.default);
|
package/dist/options.js
CHANGED
|
@@ -12,7 +12,7 @@ var _presets = _interopRequireDefault(require("./presets"));
|
|
|
12
12
|
|
|
13
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
14
|
|
|
15
|
-
const validProperties = new Set(["preset", "target", "indent", "compact", "minify", "es5", "renameVariables", "renameGlobals", "identifierGenerator", "nameRecycling", "controlFlowFlattening", "hideInitializingCode", "globalConcealing", "stringCompression", "stringConcealing", "stringEncoding", "stringSplitting", "duplicateLiteralsRemoval", "dispatcher", "eval", "rgf", "objectExtraction", "flatten", "deadCode", "calculator", "lock", "movedDeclarations", "opaquePredicates", "shuffle", "stack", "verbose", "globalVariables", "debugComments"]);
|
|
15
|
+
const validProperties = new Set(["preset", "target", "indent", "compact", "hexadecimalNumbers", "minify", "es5", "renameVariables", "renameGlobals", "identifierGenerator", "nameRecycling", "controlFlowFlattening", "hideInitializingCode", "globalConcealing", "stringCompression", "stringConcealing", "stringEncoding", "stringSplitting", "duplicateLiteralsRemoval", "dispatcher", "eval", "rgf", "objectExtraction", "flatten", "deadCode", "calculator", "lock", "movedDeclarations", "opaquePredicates", "shuffle", "stack", "verbose", "globalVariables", "debugComments"]);
|
|
16
16
|
const validOses = new Set(["windows", "linux", "osx", "ios", "android"]);
|
|
17
17
|
const validBrowsers = new Set(["firefox", "chrome", "iexplorer", "edge", "safari", "opera"]);
|
|
18
18
|
|
package/dist/order.js
CHANGED
|
@@ -39,4 +39,5 @@ exports.ObfuscateOrder = ObfuscateOrder;
|
|
|
39
39
|
ObfuscateOrder[ObfuscateOrder["ES5"] = 31] = "ES5";
|
|
40
40
|
ObfuscateOrder[ObfuscateOrder["StringEncoding"] = 32] = "StringEncoding";
|
|
41
41
|
ObfuscateOrder[ObfuscateOrder["AntiTooling"] = 34] = "AntiTooling";
|
|
42
|
+
ObfuscateOrder[ObfuscateOrder["HexadecimalNumbers"] = 35] = "HexadecimalNumbers";
|
|
42
43
|
})(ObfuscateOrder || (exports.ObfuscateOrder = ObfuscateOrder = {}));
|
package/dist/presets.js
CHANGED
|
@@ -33,6 +33,7 @@ const highPreset = {
|
|
|
33
33
|
preset: "high",
|
|
34
34
|
calculator: true,
|
|
35
35
|
compact: true,
|
|
36
|
+
hexadecimalNumbers: false,
|
|
36
37
|
controlFlowFlattening: 0.75,
|
|
37
38
|
deadCode: 0.2,
|
|
38
39
|
dispatcher: true,
|
|
@@ -69,6 +70,7 @@ const mediumPreset = {
|
|
|
69
70
|
preset: "medium",
|
|
70
71
|
calculator: true,
|
|
71
72
|
compact: true,
|
|
73
|
+
hexadecimalNumbers: false,
|
|
72
74
|
controlFlowFlattening: 0.5,
|
|
73
75
|
deadCode: 0.025,
|
|
74
76
|
dispatcher: 0.75,
|
|
@@ -96,6 +98,7 @@ const lowPreset = {
|
|
|
96
98
|
preset: "low",
|
|
97
99
|
calculator: true,
|
|
98
100
|
compact: true,
|
|
101
|
+
hexadecimalNumbers: false,
|
|
99
102
|
controlFlowFlattening: 0.25,
|
|
100
103
|
deadCode: 0.01,
|
|
101
104
|
dispatcher: 0.5,
|
|
@@ -193,6 +193,8 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
193
193
|
|
|
194
194
|
var resultVar = this.getPlaceholder();
|
|
195
195
|
var argVar = this.getPlaceholder();
|
|
196
|
+
var testVar = this.getPlaceholder();
|
|
197
|
+
var needsTestVar = false;
|
|
196
198
|
var needsResultAndArgVar = false;
|
|
197
199
|
var fnToLabel = Object.create(null);
|
|
198
200
|
fnNames.forEach(fnName => {
|
|
@@ -364,7 +366,10 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
364
366
|
|
|
365
367
|
|
|
366
368
|
finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
|
|
367
|
-
currentBody.push((0, _gen.
|
|
369
|
+
currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(testVar), control.test || (0, _gen.Literal)(true))));
|
|
370
|
+
needsTestVar = true;
|
|
371
|
+
finishCurrentChunk();
|
|
372
|
+
currentBody.push((0, _gen.IfStatement)((0, _gen.Identifier)(testVar), [{
|
|
368
373
|
type: "GotoStatement",
|
|
369
374
|
label: bodyPath
|
|
370
375
|
}])); // create new label called `bodyPath` and have test body point to afterPath (goto afterPath)
|
|
@@ -388,13 +393,16 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
388
393
|
}
|
|
389
394
|
|
|
390
395
|
if (stmt.type == "IfStatement" && stmt.consequent.type == "BlockStatement" && (!stmt.alternate || stmt.alternate.type == "BlockStatement")) {
|
|
396
|
+
finishCurrentChunk();
|
|
397
|
+
currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(testVar), stmt.test)));
|
|
398
|
+
needsTestVar = true;
|
|
391
399
|
finishCurrentChunk();
|
|
392
400
|
var hasAlternate = !!stmt.alternate;
|
|
393
401
|
(0, _assert.ok)(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
|
|
394
402
|
var yesPath = this.getPlaceholder();
|
|
395
403
|
var noPath = this.getPlaceholder();
|
|
396
404
|
var afterPath = this.getPlaceholder();
|
|
397
|
-
currentBody.push((0, _gen.IfStatement)(
|
|
405
|
+
currentBody.push((0, _gen.IfStatement)((0, _gen.Identifier)(testVar), [{
|
|
398
406
|
type: "GotoStatement",
|
|
399
407
|
label: yesPath
|
|
400
408
|
}]));
|
|
@@ -658,6 +666,10 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
658
666
|
}));
|
|
659
667
|
var declarations = [];
|
|
660
668
|
|
|
669
|
+
if (needsTestVar) {
|
|
670
|
+
declarations.push((0, _gen.VariableDeclarator)(testVar));
|
|
671
|
+
}
|
|
672
|
+
|
|
661
673
|
if (needsResultAndArgVar) {
|
|
662
674
|
declarations.push((0, _gen.VariableDeclarator)(resultVar));
|
|
663
675
|
declarations.push((0, _gen.VariableDeclarator)(argVar));
|
|
@@ -25,7 +25,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
25
25
|
|
|
26
26
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
27
27
|
|
|
28
|
-
const templates = [(0, _template.default)("\n function curCSS( elem, name, computed ) {\n var ret;\n \n computed = computed || getStyles( elem );\n \n if ( computed ) {\n ret = computed.getPropertyValue( name ) || computed[ name ];\n \n if ( ret === \"\" && !isAttached( elem ) ) {\n ret = redacted.style( elem, name );\n }\n }\n \n return ret !== undefined ?\n \n // Support: IE <=9 - 11+\n // IE returns zIndex value as an integer.\n ret + \"\" :\n ret;\n }"), (0, _template.default)("\n function Example() {\n var state = redacted.useState(false);\n return x(\n ErrorBoundary,\n null,\n x(\n DisplayName,\n null,\n )\n );\n }"), (0, _template.default)("\n const path = require('path');\nconst { version } = require('../../package');\nconst { version: dashboardPluginVersion } = require('@redacted/enterprise-plugin/package');\nconst { version: componentsVersion } = require('@redacted/components/package');\nconst { sdkVersion } = require('@redacted/enterprise-plugin');\nconst isStandaloneExecutable = require('../utils/isStandaloneExecutable');\nconst resolveLocalRedactedPath = require('./resolve-local-redacted-path');\n\nconst redactedPath = path.resolve(__dirname, '../redacted.js');"), (0, _template.default)("\nmodule.exports = async (resolveLocalRedactedPath = ()=>{throw new Error(\"No redacted path provided\")}) => {\n const cliParams = new Set(process.argv.slice(2));\n if (!cliParams.has('--version')) {\n if (cliParams.size !== 1) return false;\n if (!cliParams.has('-v')) return false;\n }\n\n const installationModePostfix = await (async (isStandaloneExecutable, redactedPath) => {\n if (isStandaloneExecutable) return ' (standalone)';\n if (redactedPath === (await resolveLocalRedactedPath())) return ' (local)';\n return '';\n })();\n\n return true;\n};"), (0, _template.default)("\nfunction setCookie(cname, cvalue, exdays) {\n var d = new Date();\n d.setTime(d.getTime() + (exdays*24*60*60*1000));\n var expires = \"expires=\"+ d.toUTCString();\n document.cookie = cname + \"=\" + cvalue + \";\" + expires + \";path=/\";\n}"), (0, _template.default)("function getCookie(cname) {\n var name = cname + \"=\";\n var decodedCookie = decodeURIComponent(document.cookie);\n var ca = decodedCookie.split(';');\n for(var i = 0; i <ca.length; i++) {\n var c = ca[i];\n while (c.charAt(0) == ' ') {\n c = c.substring(1);\n }\n if (c.indexOf(name) == 0) {\n return c.substring(name.length, c.length);\n }\n }\n return \"\";\n}"), (0, _template.default)("function getLocalStorageValue(key, cb){\n if ( typeof key !== \"string\" ) {\n throw new Error(\"Invalid data key provided (not type string)\")\n }\n if ( !key ) {\n throw new Error(\"Invalid data key provided (empty string)\")\n }\n var value = window.localStorage.getItem(key)\n try {\n value = JSON.parse(value)\n } catch ( e ) {\n cb(new Error(\"Serialization error for data '\" + key + \"': \" + e.message))\n }\n\n cb(null, value)\n }"), (0, _template.default)("\n \n var __ = \"(c=ak(<~F$VU'9f)~><&85dBPL-module/from\";\n var s = \"q:function(){var ad=ad=>b(ad-29);if(!T.r[(typeof ab==ad(123)?\";\n var g = \"return U[c[c[d(-199)]-b(205)]]||V[ae(b(166))];case T.o[c[c[c[d(-199)]+d(-174)]-(c[b(119)]-(c[d(-199)]-163))]+ae(b(146))](0)==b(167)?d(-130):-d(-144)\";\n\n __.match(s + g);\n ")];
|
|
28
|
+
const templates = [(0, _template.default)("\n function curCSS( elem, name, computed ) {\n var ret;\n \n computed = computed || getStyles( elem );\n \n if ( computed ) {\n ret = computed.getPropertyValue( name ) || computed[ name ];\n \n if ( ret === \"\" && !isAttached( elem ) ) {\n ret = redacted.style( elem, name );\n }\n }\n \n return ret !== undefined ?\n \n // Support: IE <=9 - 11+\n // IE returns zIndex value as an integer.\n ret + \"\" :\n ret;\n }"), (0, _template.default)("\n function Example() {\n var state = redacted.useState(false);\n return x(\n ErrorBoundary,\n null,\n x(\n DisplayName,\n null,\n )\n );\n }"), (0, _template.default)("\n const path = require('path');\nconst { version } = require('../../package');\nconst { version: dashboardPluginVersion } = require('@redacted/enterprise-plugin/package');\nconst { version: componentsVersion } = require('@redacted/components/package');\nconst { sdkVersion } = require('@redacted/enterprise-plugin');\nconst isStandaloneExecutable = require('../utils/isStandaloneExecutable');\nconst resolveLocalRedactedPath = require('./resolve-local-redacted-path');\n\nconst redactedPath = path.resolve(__dirname, '../redacted.js');"), (0, _template.default)("\nmodule.exports = async (resolveLocalRedactedPath = ()=>{throw new Error(\"No redacted path provided\")}) => {\n const cliParams = new Set(process.argv.slice(2));\n if (!cliParams.has('--version')) {\n if (cliParams.size !== 1) return false;\n if (!cliParams.has('-v')) return false;\n }\n\n const installationModePostfix = await (async (isStandaloneExecutable, redactedPath) => {\n if (isStandaloneExecutable) return ' (standalone)';\n if (redactedPath === (await resolveLocalRedactedPath())) return ' (local)';\n return '';\n })();\n\n return true;\n};"), (0, _template.default)("\nfunction setCookie(cname, cvalue, exdays) {\n var d = new Date();\n d.setTime(d.getTime() + (exdays*24*60*60*1000));\n var expires = \"expires=\"+ d.toUTCString();\n document.cookie = cname + \"=\" + cvalue + \";\" + expires + \";path=/\";\n}"), (0, _template.default)("function getCookie(cname) {\n var name = cname + \"=\";\n var decodedCookie = decodeURIComponent(document.cookie);\n var ca = decodedCookie.split(';');\n for(var i = 0; i <ca.length; i++) {\n var c = ca[i];\n while (c.charAt(0) == ' ') {\n c = c.substring(1);\n }\n if (c.indexOf(name) == 0) {\n return c.substring(name.length, c.length);\n }\n }\n return \"\";\n}"), (0, _template.default)("function getLocalStorageValue(key, cb){\n if ( typeof key !== \"string\" ) {\n throw new Error(\"Invalid data key provided (not type string)\")\n }\n if ( !key ) {\n throw new Error(\"Invalid data key provided (empty string)\")\n }\n var value = window.localStorage.getItem(key)\n try {\n value = JSON.parse(value)\n } catch ( e ) {\n cb(new Error(\"Serialization error for data '\" + key + \"': \" + e.message))\n }\n\n cb(null, value)\n }"), (0, _template.default)("\n \n var __ = \"(c=ak(<~F$VU'9f)~><&85dBPL-module/from\";\n var s = \"q:function(){var ad=ad=>b(ad-29);if(!T.r[(typeof ab==ad(123)?\";\n var g = \"return U[c[c[d(-199)]-b(205)]]||V[ae(b(166))];case T.o[c[c[c[d(-199)]+d(-174)]-(c[b(119)]-(c[d(-199)]-163))]+ae(b(146))](0)==b(167)?d(-130):-d(-144)\";\n\n __.match(s + g);\n "), (0, _template.default)("\n function vec_pack(vec) {\n return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]);\n }\n \n function vec_unpack(number) {\n switch (((number & 33554432) !== 0) * 1 + (number < 0) * 2) {\n case 0:\n return [number % 33554432, Math.trunc(number / 67108864)];\n case 1:\n return [\n (number % 33554432) - 33554432,\n Math.trunc(number / 67108864) + 1,\n ];\n case 2:\n return [\n (((number + 33554432) % 33554432) + 33554432) % 33554432,\n Math.round(number / 67108864),\n ];\n case 3:\n return [number % 33554432, Math.trunc(number / 67108864)];\n }\n }\n \n let a = vec_pack([2, 4]);\n let b = vec_pack([1, 2]);\n \n let c = a + b; // Vector addition\n let d = c - b; // Vector subtraction\n let e = d * 2; // Scalar multiplication\n let f = e / 2; // Scalar division\n \n console.log(vec_unpack(c)); // [3, 6]\n console.log(vec_unpack(d)); // [2, 4]\n console.log(vec_unpack(e)); // [4, 8]\n console.log(vec_unpack(f)); // [2, 4]\n "), (0, _template.default)("\n function buildCharacterMap(str) {\n const characterMap = {};\n \n for (let char of str.replace(/[^w]/g, \"\").toLowerCase())\n characterMap[char] = characterMap[char] + 1 || 1;\n \n return characterMap;\n }\n \n function isAnagrams(stringA, stringB) {\n const stringAMap = buildCharMap(stringA);\n const stringBMap = buildCharMap(stringB);\n \n for (let char in stringAMap) {\n if (stringAMap[char] !== stringBMap[char]) {\n return false;\n }\n }\n \n if (Object.keys(stringAMap).length !== Object.keys(stringBMap).length) {\n return false;\n }\n \n return true;\n }\n \n /**\n * @param {TreeNode} root\n * @return {boolean}\n */\n function isBalanced(root) {\n const height = getHeightBalanced(root);\n return height !== Infinity;\n }\n \n function getHeightBalanced(node) {\n if (!node) {\n return -1;\n }\n \n const leftTreeHeight = getHeightBalanced(node.left);\n const rightTreeHeight = getHeightBalanced(node.right);\n \n const heightDiff = Math.abs(leftTreeHeight - rightTreeHeight);\n \n if (\n leftTreeHeight === Infinity ||\n rightTreeHeight === Infinity ||\n heightDiff > 1\n ) {\n return Infinity;\n }\n \n const currentHeight = Math.max(leftTreeHeight, rightTreeHeight) + 1;\n return currentHeight;\n }\n \n window[\"__GLOBAL__HELPERS__\"] = {\n buildCharacterMap,\n isAnagrams,\n isBalanced,\n getHeightBalanced,\n };\n ")];
|
|
29
29
|
/**
|
|
30
30
|
* Adds dead code to blocks.
|
|
31
31
|
*
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _transform = _interopRequireDefault(require("./transform"));
|
|
9
|
+
|
|
10
|
+
var _order = require("../order");
|
|
11
|
+
|
|
12
|
+
var _gen = require("../util/gen");
|
|
13
|
+
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
|
|
16
|
+
class HexadecimalNumbers extends _transform.default {
|
|
17
|
+
constructor(o) {
|
|
18
|
+
super(o, _order.ObfuscateOrder.HexadecimalNumbers);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
match(object, parents) {
|
|
22
|
+
return object.type === "Literal" && typeof object.value === "number" && Math.floor(object.value) === object.value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
transform(object, parents) {
|
|
26
|
+
return () => {
|
|
27
|
+
// Technically, a Literal will never be negative because it's supposed to be inside a UnaryExpression with a "-" operator.
|
|
28
|
+
// This code handles it regardless
|
|
29
|
+
var isNegative = object.value < 0;
|
|
30
|
+
var hex = Math.abs(object.value).toString(16);
|
|
31
|
+
var newStr = (isNegative ? "-" : "") + "0x" + hex;
|
|
32
|
+
this.replace(object, (0, _gen.Identifier)(newStr));
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
exports.default = HexadecimalNumbers;
|
|
@@ -111,10 +111,15 @@ class Minify extends _transform.default {
|
|
|
111
111
|
// Unnecessary return
|
|
112
112
|
if (body.length && body[body.length - 1]) {
|
|
113
113
|
var last = body[body.length - 1];
|
|
114
|
-
var isUndefined = last.argument == null;
|
|
115
114
|
|
|
116
|
-
if (last.type == "ReturnStatement"
|
|
117
|
-
|
|
115
|
+
if (last.type == "ReturnStatement") {
|
|
116
|
+
var isUndefined = last.argument == null;
|
|
117
|
+
|
|
118
|
+
if (isUndefined) {
|
|
119
|
+
if ((0, _insert.getFunction)(object, parents).body == object) {
|
|
120
|
+
body.pop();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
118
123
|
}
|
|
119
124
|
} // Variable declaration grouping
|
|
120
125
|
// var a = 1;
|
package/package.json
CHANGED
package/src/compiler.ts
CHANGED
|
@@ -10,7 +10,11 @@ export default async function compileJs(tree: any, options: ObfuscateOptions) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function compileJsSync(tree: any, options: ObfuscateOptions): string {
|
|
13
|
-
var api: any = {
|
|
13
|
+
var api: any = {
|
|
14
|
+
format: {
|
|
15
|
+
...escodegen.FORMAT_MINIFY,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
14
18
|
|
|
15
19
|
if (!options.compact) {
|
|
16
20
|
api = {};
|
package/src/obfuscator.ts
CHANGED
|
@@ -34,6 +34,7 @@ import StringCompression from "./transforms/string/stringCompression";
|
|
|
34
34
|
import NameRecycling from "./transforms/identifier/nameRecycling";
|
|
35
35
|
import AntiTooling from "./transforms/antiTooling";
|
|
36
36
|
import HideInitializingCode from "./transforms/hideInitializingCode";
|
|
37
|
+
import HexadecimalNumbers from "./transforms/hexadecimalNumbers";
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* The parent transformation holding the `state`.
|
|
@@ -96,6 +97,7 @@ export default class Obfuscator extends EventEmitter {
|
|
|
96
97
|
test(options.stack, Stack);
|
|
97
98
|
test(true, AntiTooling);
|
|
98
99
|
test(options.hideInitializingCode, HideInitializingCode);
|
|
100
|
+
test(options.hexadecimalNumbers, HexadecimalNumbers);
|
|
99
101
|
|
|
100
102
|
if (
|
|
101
103
|
options.lock &&
|
package/src/options.ts
CHANGED
|
@@ -50,6 +50,13 @@ export interface ObfuscateOptions {
|
|
|
50
50
|
*/
|
|
51
51
|
compact?: boolean;
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* ### `hexadecimalNumbers`
|
|
55
|
+
*
|
|
56
|
+
* Uses the hexadecimal representation (`50` -> `0x32`) for numbers. (`true/false`)
|
|
57
|
+
*/
|
|
58
|
+
hexadecimalNumbers?: boolean;
|
|
59
|
+
|
|
53
60
|
/**
|
|
54
61
|
* ### `minify`
|
|
55
62
|
*
|
|
@@ -676,6 +683,7 @@ const validProperties = new Set([
|
|
|
676
683
|
"target",
|
|
677
684
|
"indent",
|
|
678
685
|
"compact",
|
|
686
|
+
"hexadecimalNumbers",
|
|
679
687
|
"minify",
|
|
680
688
|
"es5",
|
|
681
689
|
"renameVariables",
|
package/src/order.ts
CHANGED
package/src/presets.ts
CHANGED
|
@@ -29,6 +29,7 @@ const highPreset: ObfuscateOptions = {
|
|
|
29
29
|
|
|
30
30
|
calculator: true,
|
|
31
31
|
compact: true,
|
|
32
|
+
hexadecimalNumbers: false,
|
|
32
33
|
controlFlowFlattening: 0.75,
|
|
33
34
|
deadCode: 0.2,
|
|
34
35
|
dispatcher: true,
|
|
@@ -64,6 +65,7 @@ const mediumPreset: ObfuscateOptions = {
|
|
|
64
65
|
|
|
65
66
|
calculator: true,
|
|
66
67
|
compact: true,
|
|
68
|
+
hexadecimalNumbers: false,
|
|
67
69
|
controlFlowFlattening: 0.5,
|
|
68
70
|
deadCode: 0.025,
|
|
69
71
|
dispatcher: 0.75,
|
|
@@ -92,6 +94,7 @@ const lowPreset: ObfuscateOptions = {
|
|
|
92
94
|
|
|
93
95
|
calculator: true,
|
|
94
96
|
compact: true,
|
|
97
|
+
hexadecimalNumbers: false,
|
|
95
98
|
controlFlowFlattening: 0.25,
|
|
96
99
|
deadCode: 0.01,
|
|
97
100
|
dispatcher: 0.5,
|
|
@@ -234,6 +234,9 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
234
234
|
|
|
235
235
|
var resultVar = this.getPlaceholder();
|
|
236
236
|
var argVar = this.getPlaceholder();
|
|
237
|
+
var testVar = this.getPlaceholder();
|
|
238
|
+
|
|
239
|
+
var needsTestVar = false;
|
|
237
240
|
var needsResultAndArgVar = false;
|
|
238
241
|
var fnToLabel: { [fnName: string]: string } = Object.create(null);
|
|
239
242
|
|
|
@@ -478,7 +481,21 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
478
481
|
finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
|
|
479
482
|
|
|
480
483
|
currentBody.push(
|
|
481
|
-
|
|
484
|
+
ExpressionStatement(
|
|
485
|
+
AssignmentExpression(
|
|
486
|
+
"=",
|
|
487
|
+
Identifier(testVar),
|
|
488
|
+
control.test || Literal(true)
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
needsTestVar = true;
|
|
494
|
+
|
|
495
|
+
finishCurrentChunk();
|
|
496
|
+
|
|
497
|
+
currentBody.push(
|
|
498
|
+
IfStatement(Identifier(testVar), [
|
|
482
499
|
{
|
|
483
500
|
type: "GotoStatement",
|
|
484
501
|
label: bodyPath,
|
|
@@ -522,6 +539,16 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
522
539
|
) {
|
|
523
540
|
finishCurrentChunk();
|
|
524
541
|
|
|
542
|
+
currentBody.push(
|
|
543
|
+
ExpressionStatement(
|
|
544
|
+
AssignmentExpression("=", Identifier(testVar), stmt.test)
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
needsTestVar = true;
|
|
549
|
+
|
|
550
|
+
finishCurrentChunk();
|
|
551
|
+
|
|
525
552
|
var hasAlternate = !!stmt.alternate;
|
|
526
553
|
ok(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
|
|
527
554
|
|
|
@@ -530,7 +557,7 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
530
557
|
var afterPath = this.getPlaceholder();
|
|
531
558
|
|
|
532
559
|
currentBody.push(
|
|
533
|
-
IfStatement(
|
|
560
|
+
IfStatement(Identifier(testVar), [
|
|
534
561
|
{
|
|
535
562
|
type: "GotoStatement",
|
|
536
563
|
label: yesPath,
|
|
@@ -975,6 +1002,10 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
975
1002
|
|
|
976
1003
|
var declarations = [];
|
|
977
1004
|
|
|
1005
|
+
if (needsTestVar) {
|
|
1006
|
+
declarations.push(VariableDeclarator(testVar));
|
|
1007
|
+
}
|
|
1008
|
+
|
|
978
1009
|
if (needsResultAndArgVar) {
|
|
979
1010
|
declarations.push(VariableDeclarator(resultVar));
|
|
980
1011
|
declarations.push(VariableDeclarator(argVar));
|
|
@@ -124,6 +124,108 @@ function setCookie(cname, cvalue, exdays) {
|
|
|
124
124
|
|
|
125
125
|
__.match(s + g);
|
|
126
126
|
`),
|
|
127
|
+
Template(`
|
|
128
|
+
function vec_pack(vec) {
|
|
129
|
+
return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function vec_unpack(number) {
|
|
133
|
+
switch (((number & 33554432) !== 0) * 1 + (number < 0) * 2) {
|
|
134
|
+
case 0:
|
|
135
|
+
return [number % 33554432, Math.trunc(number / 67108864)];
|
|
136
|
+
case 1:
|
|
137
|
+
return [
|
|
138
|
+
(number % 33554432) - 33554432,
|
|
139
|
+
Math.trunc(number / 67108864) + 1,
|
|
140
|
+
];
|
|
141
|
+
case 2:
|
|
142
|
+
return [
|
|
143
|
+
(((number + 33554432) % 33554432) + 33554432) % 33554432,
|
|
144
|
+
Math.round(number / 67108864),
|
|
145
|
+
];
|
|
146
|
+
case 3:
|
|
147
|
+
return [number % 33554432, Math.trunc(number / 67108864)];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let a = vec_pack([2, 4]);
|
|
152
|
+
let b = vec_pack([1, 2]);
|
|
153
|
+
|
|
154
|
+
let c = a + b; // Vector addition
|
|
155
|
+
let d = c - b; // Vector subtraction
|
|
156
|
+
let e = d * 2; // Scalar multiplication
|
|
157
|
+
let f = e / 2; // Scalar division
|
|
158
|
+
|
|
159
|
+
console.log(vec_unpack(c)); // [3, 6]
|
|
160
|
+
console.log(vec_unpack(d)); // [2, 4]
|
|
161
|
+
console.log(vec_unpack(e)); // [4, 8]
|
|
162
|
+
console.log(vec_unpack(f)); // [2, 4]
|
|
163
|
+
`),
|
|
164
|
+
Template(`
|
|
165
|
+
function buildCharacterMap(str) {
|
|
166
|
+
const characterMap = {};
|
|
167
|
+
|
|
168
|
+
for (let char of str.replace(/[^\w]/g, "").toLowerCase())
|
|
169
|
+
characterMap[char] = characterMap[char] + 1 || 1;
|
|
170
|
+
|
|
171
|
+
return characterMap;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function isAnagrams(stringA, stringB) {
|
|
175
|
+
const stringAMap = buildCharMap(stringA);
|
|
176
|
+
const stringBMap = buildCharMap(stringB);
|
|
177
|
+
|
|
178
|
+
for (let char in stringAMap) {
|
|
179
|
+
if (stringAMap[char] !== stringBMap[char]) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (Object.keys(stringAMap).length !== Object.keys(stringBMap).length) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @param {TreeNode} root
|
|
193
|
+
* @return {boolean}
|
|
194
|
+
*/
|
|
195
|
+
function isBalanced(root) {
|
|
196
|
+
const height = getHeightBalanced(root);
|
|
197
|
+
return height !== Infinity;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getHeightBalanced(node) {
|
|
201
|
+
if (!node) {
|
|
202
|
+
return -1;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const leftTreeHeight = getHeightBalanced(node.left);
|
|
206
|
+
const rightTreeHeight = getHeightBalanced(node.right);
|
|
207
|
+
|
|
208
|
+
const heightDiff = Math.abs(leftTreeHeight - rightTreeHeight);
|
|
209
|
+
|
|
210
|
+
if (
|
|
211
|
+
leftTreeHeight === Infinity ||
|
|
212
|
+
rightTreeHeight === Infinity ||
|
|
213
|
+
heightDiff > 1
|
|
214
|
+
) {
|
|
215
|
+
return Infinity;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const currentHeight = Math.max(leftTreeHeight, rightTreeHeight) + 1;
|
|
219
|
+
return currentHeight;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
window["__GLOBAL__HELPERS__"] = {
|
|
223
|
+
buildCharacterMap,
|
|
224
|
+
isAnagrams,
|
|
225
|
+
isBalanced,
|
|
226
|
+
getHeightBalanced,
|
|
227
|
+
};
|
|
228
|
+
`),
|
|
127
229
|
];
|
|
128
230
|
|
|
129
231
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Transform from "./transform";
|
|
2
|
+
import { ObfuscateOrder } from "../order";
|
|
3
|
+
import { ExitCallback } from "../traverse";
|
|
4
|
+
import { Identifier, Node } from "../util/gen";
|
|
5
|
+
|
|
6
|
+
export default class HexadecimalNumbers extends Transform {
|
|
7
|
+
constructor(o) {
|
|
8
|
+
super(o, ObfuscateOrder.HexadecimalNumbers);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
match(object: Node, parents: Node[]): boolean {
|
|
12
|
+
return (
|
|
13
|
+
object.type === "Literal" &&
|
|
14
|
+
typeof object.value === "number" &&
|
|
15
|
+
Math.floor(object.value) === object.value
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
transform(object: Node, parents: Node[]): void | ExitCallback {
|
|
20
|
+
return () => {
|
|
21
|
+
// Technically, a Literal will never be negative because it's supposed to be inside a UnaryExpression with a "-" operator.
|
|
22
|
+
// This code handles it regardless
|
|
23
|
+
var isNegative = object.value < 0;
|
|
24
|
+
var hex = Math.abs(object.value).toString(16);
|
|
25
|
+
|
|
26
|
+
var newStr = (isNegative ? "-" : "") + "0x" + hex;
|
|
27
|
+
|
|
28
|
+
this.replace(object, Identifier(newStr));
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/transforms/minify.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
clone,
|
|
22
22
|
isForInitialize,
|
|
23
23
|
isLexContext,
|
|
24
|
+
getFunction,
|
|
24
25
|
} from "../util/insert";
|
|
25
26
|
import { isValidIdentifier, isEquivalent } from "../util/compare";
|
|
26
27
|
import { walk, isBlock } from "../traverse";
|
|
@@ -126,9 +127,14 @@ export default class Minify extends Transform {
|
|
|
126
127
|
// Unnecessary return
|
|
127
128
|
if (body.length && body[body.length - 1]) {
|
|
128
129
|
var last = body[body.length - 1];
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (last.type == "ReturnStatement") {
|
|
131
|
+
var isUndefined = last.argument == null;
|
|
132
|
+
|
|
133
|
+
if (isUndefined) {
|
|
134
|
+
if (getFunction(object, parents).body == object) {
|
|
135
|
+
body.pop();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import JsConfuser from "../../src/index";
|
|
2
|
+
|
|
3
|
+
test("Variant #1: Positive integer to hexadecimal", async () => {
|
|
4
|
+
var output = await JsConfuser.obfuscate(`TEST_VAR = 10;`, {
|
|
5
|
+
target: "node",
|
|
6
|
+
hexadecimalNumbers: true,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
expect(output).toContain("0xa");
|
|
10
|
+
expect(output).not.toContain("10");
|
|
11
|
+
|
|
12
|
+
var TEST_VAR;
|
|
13
|
+
|
|
14
|
+
eval(output);
|
|
15
|
+
|
|
16
|
+
expect(TEST_VAR).toStrictEqual(10);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("Variant #2: Negative integer to hexadecimal", async () => {
|
|
20
|
+
var output = await JsConfuser.obfuscate(`TEST_VAR = -10;`, {
|
|
21
|
+
target: "node",
|
|
22
|
+
hexadecimalNumbers: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(output).toContain("-0xa");
|
|
26
|
+
expect(output).not.toContain("-10");
|
|
27
|
+
|
|
28
|
+
var TEST_VAR;
|
|
29
|
+
|
|
30
|
+
eval(output);
|
|
31
|
+
|
|
32
|
+
expect(TEST_VAR).toStrictEqual(-10);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Variant #3: Don't change floats", async () => {
|
|
36
|
+
var output = await JsConfuser.obfuscate(`var TEST_VAR = [15.5, -75.9];`, {
|
|
37
|
+
target: "node",
|
|
38
|
+
hexadecimalNumbers: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(output).toContain("15.5");
|
|
42
|
+
expect(output).toContain("-75.9");
|
|
43
|
+
expect(output).not.toContain("0x");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("Variant #4: Work even on large numbers", async () => {
|
|
47
|
+
var output = await JsConfuser.obfuscate(
|
|
48
|
+
`TEST_VAR = 10000000000000000000000000000;`,
|
|
49
|
+
{
|
|
50
|
+
target: "node",
|
|
51
|
+
hexadecimalNumbers: true,
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(output).toContain("0x204fce5e3e25020000000000");
|
|
56
|
+
|
|
57
|
+
var TEST_VAR;
|
|
58
|
+
|
|
59
|
+
eval(output);
|
|
60
|
+
|
|
61
|
+
expect(TEST_VAR).toStrictEqual(10000000000000000000000000000);
|
|
62
|
+
});
|
|
@@ -261,3 +261,41 @@ test("Variant #14: Shorten 'var x = undefined' to 'var x'", async () => {
|
|
|
261
261
|
expect(output).toContain("var x");
|
|
262
262
|
expect(output).not.toContain("var x=");
|
|
263
263
|
});
|
|
264
|
+
|
|
265
|
+
test("Variant #15: Removing implied 'return'", async () => {
|
|
266
|
+
// Valid
|
|
267
|
+
var output = await JsConfuser(
|
|
268
|
+
`
|
|
269
|
+
function MyFunction(){
|
|
270
|
+
var output = "Hello World";
|
|
271
|
+
console.log(output);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
MyFunction();
|
|
276
|
+
`,
|
|
277
|
+
{ target: "node", minify: true }
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
expect(output).not.toContain("return");
|
|
281
|
+
|
|
282
|
+
// Invalid
|
|
283
|
+
// https://github.com/MichaelXF/js-confuser/issues/34
|
|
284
|
+
var output2 = await JsConfuser(
|
|
285
|
+
`
|
|
286
|
+
function greet(){
|
|
287
|
+
if(true){
|
|
288
|
+
console.log("return");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
var output = "should not show!"; console.log(output);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
greet();
|
|
296
|
+
`,
|
|
297
|
+
{ target: "browser", minify: true }
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
expect(output2).toContain("return");
|
|
301
|
+
});
|