-
Notifications
You must be signed in to change notification settings - Fork 0
/
obfuscate.js
114 lines (100 loc) · 4.7 KB
/
obfuscate.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"use strict";
import fs from "node:fs";
import path from "node:path";
import JavaScriptObfuscator from "javascript-obfuscator";
import { modifyBasename } from "./utils.js";
// from https://jabde.com/2022/01/09/top-100-variable-names-2021/
const variablesDictionary = [
"a", "b", "c", "f", "i", "j", "k", "n", "p", "r", "s", "t", "v", "x", "y", "z", "id", "ii", "im", "jj",
"x2", "y2", "all", "any", "arg", "bar", "ctr", "dat", "err", "foo", "idx", "inc", "len", "loc", "map",
"mat", "msg", "num", "pos", "ptr", "pts", "pub", "ret", "rng", "rst", "sec", "str", "sub", "sum", "tst",
"vec", "x_t", "yes", "copy", "data", "func", "info", "iter", "left", "list", "next", "none", "parm",
"path", "prob", "rcvr", "temp", "text", "time", "unit", "used", "vals", "word", "angle", "array",
"check", "found", "fpath", "input", "items", "names", "right", "sigma", "state", "theta", "failed",
"output", "scores", "sender", "string", "var_in", "current", "var_out", "filename", "temp_vec"
];
// Each input file is obfuscated with every permutation of these options
const obfuscationSwitches = {
compact: [false],
controlFlowFlattening: [true, false],
deadCodeInjection: [false],
debugProtection: [false],
disableConsoleOutput: [false],
identifierNamesGenerator: ["dictionary", "hexadecimal"],
ignoreImports: [false],
numbersToExpressions: [true, false],
renameGlobals: [true],
renameProperties: [true],
selfDefending: [false],
simplify: [true],
splitStrings: [true, false],
stringArray: [true, false],
stringArrayCallsTransform: [true],
transformObjectKeys: [true, false],
unicodeEscapeSequence: [false],
}
// These options aren't iterated over
const obfuscationOptions = {
deadCodeInjectionThreshold: 0.33,
identifiersDictionary: variablesDictionary,
renamePropertiesMode: "safe",
stringArrayEncoding: ["none", "base64"],
stringArrayCallsTransformThreshold: 0.5,
target: "node",
};
// Generates many combinations of obfuscation options
// For option meanings, see
// https://github.com/javascript-obfuscator/javascript-obfuscator#javascript-obfuscator-options
export function generateOptionCombinations() {
// Map of option key to allowable values
// Total number of combinations is approximately equal to product of lengths of all value arrays.
// Some options are restricted to one value so that there is some baseline level of obfuscation.
const combinations = createCombinations(obfuscationSwitches, obfuscationOptions);
console.log(`Generated ${combinations.length} option combinations`)
return combinations;
}
// Creates a list of all combinations of option values
// based on the list of option names passed in the first argument
// and valid values passed in the second argument.
// NOTE: this function allocates a struct for each of the combinations,
// Time and space complexity is linear in the product of the lengths of all option values
function createCombinationsRecursively(optionNames, optionValues, currentIndex, currentSettings, allCombinations) {
if (currentIndex === optionNames.length) {
// finished settings all options so add this combination to the list of all
allCombinations.push(currentSettings);
return;
}
const currentOption = optionNames[currentIndex];
const values = optionValues[currentOption];
for (let v of values) {
// add the current option and current value to the existing settings and recurse
const newSettings = {}
Object.assign(newSettings, currentSettings);
newSettings[currentOption] = v
createCombinationsRecursively(optionNames, optionValues, currentIndex + 1, newSettings, allCombinations)
}
}
// Creates a list of all combinations of option values
// based on the list of option names passed in the first argument
// and valid values passed in the second argument.
function createCombinations(variableOptions, fixedOptions) {
const optionNames = Object.keys(variableOptions);
const allCombinations = [];
createCombinationsRecursively(optionNames, variableOptions, 0, fixedOptions, allCombinations)
return allCombinations;
}
export function generateObfuscatedVersions(filename, logPrefix, optionCombinations, outputDir) {
const sourceCode = fs.readFileSync(filename, {encoding: "utf8"});
let combinationId = 0;
for (let obfuscationOptions of optionCombinations) {
const result = JavaScriptObfuscator.obfuscate(sourceCode, obfuscationOptions);
const obfuscatedCode = result.getObfuscatedCode();
// write to file
const outputPath = path.join(outputDir, modifyBasename(filename, "-obfs"+combinationId));
fs.writeFileSync(outputPath, obfuscatedCode)
if ((combinationId + 1) % 64 === 0) {
console.log(`${logPrefix} progress ${combinationId+1}/${optionCombinations.length}`)
}
combinationId++;
}
}