distinctiomagnam
Version:
JavaScript Obfuscation Tool.
171 lines (142 loc) • 5.64 kB
text/typescript
import { ok } from "assert";
import { EventEmitter } from "events";
import { Node } from "./util/gen";
import traverse from "./traverse";
import { ObfuscateOptions } from "./options";
import { ProbabilityMap, isProbabilityMapProbable } from "./probability";
import Transform from "./transforms/transform";
import Preparation from "./transforms/preparation/preparation";
import ObjectExtraction from "./transforms/extraction/objectExtraction";
import Lock from "./transforms/lock/lock";
import Dispatcher from "./transforms/dispatcher";
import DeadCode from "./transforms/deadCode";
import OpaquePredicates from "./transforms/opaquePredicates";
import Calculator from "./transforms/calculator";
import ControlFlowFlattening from "./transforms/controlFlowFlattening/controlFlowFlattening";
import Eval from "./transforms/eval";
import GlobalConcealing from "./transforms/identifier/globalConcealing";
import StringConcealing from "./transforms/string/stringConcealing";
import StringSplitting from "./transforms/string/stringSplitting";
import DuplicateLiteralsRemoval from "./transforms/extraction/duplicateLiteralsRemoval";
import Shuffle from "./transforms/shuffle";
import MovedDeclarations from "./transforms/identifier/movedDeclarations";
import RenameVariables from "./transforms/identifier/renameVariables";
import RenameLabels from "./transforms/renameLabels";
import Minify from "./transforms/minify";
import ES5 from "./transforms/es5/es5";
import StringEncoding from "./transforms/string/stringEncoding";
import RGF from "./transforms/rgf";
import Flatten from "./transforms/flatten";
import Stack from "./transforms/stack";
import StringCompression from "./transforms/string/stringCompression";
import NameRecycling from "./transforms/identifier/nameRecycling";
import AntiTooling from "./transforms/antiTooling";
import HideInitializingCode from "./transforms/hideInitializingCode";
/**
* The parent transformation holding the `state`.
*/
export default class Obfuscator extends EventEmitter {
varCount: number;
transforms: { [name: string]: Transform };
array: Transform[];
state: "transform" | "eval" = "transform";
generated: Set<string>;
constructor(public options: ObfuscateOptions) {
super();
this.varCount = 0;
this.transforms = Object.create(null);
this.generated = new Set();
this.push(new Preparation(this));
this.push(new RenameLabels(this));
const test = <T>(map: ProbabilityMap<T>, ...transformers: any[]) => {
if (isProbabilityMapProbable(map)) {
// options.verbose && console.log("+ Added " + transformer.name);
transformers.forEach((Transformer) => this.push(new Transformer(this)));
} else {
// options.verbose && console.log("- Skipped adding " + transformer.name);
}
};
// Optimization: Only add needed transformers. If a probability always return false, no need in running that extra code.
test(options.objectExtraction, ObjectExtraction);
test(options.deadCode, DeadCode);
test(options.dispatcher, Dispatcher);
test(options.controlFlowFlattening, ControlFlowFlattening);
test(options.globalConcealing, GlobalConcealing);
test(options.stringCompression, StringCompression);
test(options.stringConcealing, StringConcealing);
test(options.stringEncoding, StringEncoding);
test(options.stringSplitting, StringSplitting);
test(options.renameVariables, RenameVariables);
test(options.nameRecycling, NameRecycling);
test(options.eval, Eval);
test(options.opaquePredicates, OpaquePredicates);
test(options.duplicateLiteralsRemoval, DuplicateLiteralsRemoval);
test(options.minify, Minify);
test(options.calculator, Calculator);
test(options.movedDeclarations, MovedDeclarations);
test(options.es5, ES5);
test(options.shuffle, Shuffle);
test(options.flatten, Flatten);
test(options.rgf, RGF);
test(options.stack, Stack);
test(true, AntiTooling);
test(options.hideInitializingCode, HideInitializingCode);
if (
options.lock &&
Object.keys(options.lock).filter((x) =>
x == "domainLock"
? options.lock.domainLock && options.lock.domainLock.length
: options.lock[x]
).length
) {
test(true, Lock);
}
// Make array
this.array = Object.values(this.transforms);
// Sort transformations based on their priority
this.array.sort((a, b) => a.priority - b.priority);
}
push(transform: Transform) {
if (transform.className) {
ok(
!this.transforms[transform.className],
"Already have " + transform.className
);
}
this.transforms[transform.className] = transform;
}
resetState() {
this.varCount = 0;
this.generated = new Set();
this.state = "transform";
}
async apply(tree: Node, debugMode = false) {
ok(tree.type == "Program", "The root node must be type 'Program'");
ok(Array.isArray(tree.body), "The root's body property must be an array");
ok(Array.isArray(this.array));
this.resetState();
var completed = 0;
for (var transform of this.array) {
await transform.apply(tree);
completed++;
if (debugMode) {
this.emit("debug", transform.className, tree, completed);
}
}
if (this.options.verbose) {
console.log("-> Check for Eval Callbacks");
}
this.state = "eval";
// Find eval callbacks
traverse(tree, (o, p) => {
if (o.$eval) {
return () => {
o.$eval(o, p);
};
}
});
if (this.options.verbose) {
console.log("<- Done");
}
}
}