mirror of
https://github.com/thegeneralist01/twitter-tid-deobf-fork
synced 2026-01-09 14:50:26 +01:00
737 lines
21 KiB
JavaScript
737 lines
21 KiB
JavaScript
const fs = require("fs");
|
|
const t = require("@babel/types");
|
|
const parser = require("@babel/parser");
|
|
const traverse = require("@babel/traverse").default;
|
|
const generate = require("@babel/generator").default;
|
|
const vm = require("vm");
|
|
const { readFileSync, writeFile, writeFileSync } = require("fs");
|
|
const { exit } = require("process");
|
|
var output = "";
|
|
|
|
let beautify_opts = {
|
|
comments: true,
|
|
minified: false,
|
|
concise: false,
|
|
};
|
|
const [_, __, inputPath, outputPath] = process.argv;
|
|
const script = readFileSync(inputPath, "utf-8");
|
|
|
|
const AST = parser.parse(script, {});
|
|
|
|
var decryptFuncCtx = vm.createContext();
|
|
var decryptCode = "";
|
|
var decryptFuncName = "";
|
|
|
|
const constantReplacer = {
|
|
VariableDeclarator(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.id ||
|
|
!node.init ||
|
|
(node.init.type != "StringLiteral" &&
|
|
node.init.type != "NumericLiteral") ||
|
|
node.id.type != "Identifier"
|
|
) {
|
|
return;
|
|
}
|
|
if (
|
|
(node.init.type == "NumericLiteral" && node.init.value == 0) ||
|
|
(node.init.type == "StringLiteral" && node.init.value == "")
|
|
) {
|
|
return;
|
|
}
|
|
let binding = path.scope.getBinding(node.id.name);
|
|
if (!binding) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < binding.referencePaths.length; i++) {
|
|
binding.referencePaths[i].replaceWith(node.init);
|
|
}
|
|
path.remove();
|
|
},
|
|
};
|
|
|
|
const replaceObjSimple = {
|
|
VariableDeclarator(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.id ||
|
|
!node.init ||
|
|
node.init.type != "ObjectExpression" ||
|
|
node.id.type != "Identifier" ||
|
|
node.init.properties.length < 1
|
|
) {
|
|
return;
|
|
}
|
|
var valid = true;
|
|
var map = {};
|
|
for (var i = 0; i < node.init.properties.length; i++) {
|
|
var prop = node.init.properties[i];
|
|
if (
|
|
!prop.key ||
|
|
!prop.value ||
|
|
prop.key.type != "Identifier" ||
|
|
(prop.value.type != "NumericLiteral" &&
|
|
prop.value.type != "StringLiteral")
|
|
) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
map[prop.key.name] = prop.value;
|
|
}
|
|
if (!valid) {
|
|
return;
|
|
}
|
|
path.scope.crawl();
|
|
let binding = path.scope.getBinding(node.id.name);
|
|
if (!binding) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < binding.referencePaths.length; i++) {
|
|
let refPath = binding.referencePaths[i].parentPath;
|
|
if (refPath.node.type != "MemberExpression" || !refPath.node.property) {
|
|
continue;
|
|
}
|
|
let key;
|
|
if (refPath.node.property.type == "Identifier") {
|
|
key = refPath.node.property.name;
|
|
} else {
|
|
key = refPath.node.property.value;
|
|
}
|
|
refPath.replaceWith(map[key]);
|
|
}
|
|
path.remove();
|
|
},
|
|
};
|
|
|
|
const replaceExprStmts = {
|
|
MemberExpression(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.property ||
|
|
node.property.type != "SequenceExpression" ||
|
|
!node.property.expressions ||
|
|
node.property.expressions.length < 3
|
|
) {
|
|
return;
|
|
}
|
|
var callExprIndex = node.property.expressions.length - 1;
|
|
if (node.property.expressions[callExprIndex].type != "CallExpression") {
|
|
return;
|
|
}
|
|
var values = [];
|
|
var order = [];
|
|
for (var i = 0; i < node.property.expressions.length; i++) {
|
|
var expr = node.property.expressions[i];
|
|
if (expr.type != "AssignmentExpression" || !expr.right || !expr.left) {
|
|
continue;
|
|
}
|
|
values.push(generate(expr.right).code);
|
|
order.push(expr.left.name);
|
|
}
|
|
let newArgs = [];
|
|
for (
|
|
var i = 0;
|
|
i < node.property.expressions[callExprIndex].arguments.length;
|
|
i++
|
|
) {
|
|
let arg = node.property.expressions[callExprIndex].arguments[i];
|
|
let str = generate(arg).code;
|
|
if (str.match(/[A-z]/g) == null) {
|
|
newArgs.push(arg);
|
|
continue;
|
|
}
|
|
let key = str.match(/[A-z]/g)[0];
|
|
let index = order.indexOf(key);
|
|
str = str.replace(key, values[index]);
|
|
if (
|
|
str.match(/[0-9]/g) != null &&
|
|
str.match(/[0-9]/g).length > 1 &&
|
|
!str.match(/[A-z"]/g)
|
|
) {
|
|
newArgs.push(t.numericLiteral(eval(str)));
|
|
continue;
|
|
}
|
|
str = str.slice(1);
|
|
str = str.slice(0, -1);
|
|
newArgs.push(t.stringLiteral(str));
|
|
}
|
|
path.replaceWith(
|
|
t.memberExpression(
|
|
node.object,
|
|
t.callExpression(
|
|
node.property.expressions[callExprIndex].callee,
|
|
newArgs
|
|
),
|
|
true
|
|
)
|
|
);
|
|
},
|
|
// ! same thing except ExpressionStatement, SequenceExpression
|
|
// ! example: (a = "7d]D", k = -497, m = -404, C = -368, uo(k - -1644, a - 298, a, m - 199, C - 208))
|
|
SequenceExpression(path) {
|
|
const { node } = path;
|
|
if (!node.expressions || node.expressions.length < 3) {
|
|
return;
|
|
}
|
|
var callExprIndex = node.expressions.length - 1;
|
|
if (node.expressions[callExprIndex].type != "CallExpression") {
|
|
return;
|
|
}
|
|
var values = [];
|
|
var order = [];
|
|
for (var i = 0; i < node.expressions.length; i++) {
|
|
var expr = node.expressions[i];
|
|
if (expr.type != "AssignmentExpression" || !expr.right || !expr.left) {
|
|
continue;
|
|
}
|
|
values.push(generate(expr.right).code);
|
|
order.push(expr.left.name);
|
|
}
|
|
let newArgs = [];
|
|
for (var i = 0; i < node.expressions[callExprIndex].arguments.length; i++) {
|
|
let arg = node.expressions[callExprIndex].arguments[i];
|
|
let str = generate(arg).code;
|
|
if (str.match(/[A-z]/g) == null) {
|
|
newArgs.push(arg);
|
|
continue;
|
|
}
|
|
let key = str.match(/[A-z]/g)[0];
|
|
let index = order.indexOf(key);
|
|
str = str.replace(key, values[index]);
|
|
if (
|
|
str.match(/[0-9]/g) != null &&
|
|
str.match(/[0-9]/g).length > 1 &&
|
|
!str.match(/[A-z"]/g)
|
|
) {
|
|
newArgs.push(t.numericLiteral(eval(str)));
|
|
continue;
|
|
}
|
|
str = str.slice(1);
|
|
str = str.slice(0, -1);
|
|
newArgs.push(t.stringLiteral(str));
|
|
}
|
|
path.replaceWith(
|
|
t.callExpression(node.expressions[callExprIndex].callee, newArgs)
|
|
);
|
|
},
|
|
};
|
|
|
|
const replaceWeirdProxyCall = {
|
|
MemberExpression(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.object ||
|
|
node.object.type != "Identifier" ||
|
|
!node.property ||
|
|
node.property.type != "CallExpression"
|
|
) {
|
|
return;
|
|
}
|
|
if (
|
|
!node.property.callee ||
|
|
node.property.callee.type != "FunctionExpression"
|
|
) {
|
|
return;
|
|
}
|
|
let values = [
|
|
generate(node.property.arguments[0]).code,
|
|
generate(node.property.arguments[1]).code,
|
|
generate(node.property.arguments[2]).code,
|
|
generate(node.property.arguments[3]).code,
|
|
generate(node.property.arguments[4]).code,
|
|
];
|
|
let order = [
|
|
node.property.callee.params[0].name,
|
|
node.property.callee.params[1].name,
|
|
node.property.callee.params[2].name,
|
|
node.property.callee.params[3].name,
|
|
node.property.callee.params[4].name,
|
|
];
|
|
let newArgs = [];
|
|
for (
|
|
var i = 0;
|
|
i < node.property.callee.body.body[0].argument.arguments.length;
|
|
i++
|
|
) {
|
|
let arg = node.property.callee.body.body[0].argument.arguments[i];
|
|
let str = generate(arg).code;
|
|
if (str.match(/[A-z]/g) == null) {
|
|
newArgs.push(arg);
|
|
continue;
|
|
}
|
|
let key = str.match(/[A-z]/g)[0];
|
|
let index = order.indexOf(key);
|
|
str = str.replace(key, values[index]);
|
|
if (
|
|
str.match(/[0-9]/g) != null &&
|
|
str.match(/[0-9]/g).length > 1 &&
|
|
!str.match(/[A-z"]/g)
|
|
) {
|
|
newArgs.push(t.numericLiteral(eval(str)));
|
|
continue;
|
|
}
|
|
str = str.slice(1);
|
|
str = str.slice(0, -1);
|
|
newArgs.push(t.stringLiteral(str));
|
|
}
|
|
path.replaceWith(
|
|
t.memberExpression(
|
|
node.object,
|
|
t.callExpression(
|
|
node.property.callee.body.body[0].argument.callee,
|
|
newArgs
|
|
),
|
|
true
|
|
)
|
|
);
|
|
},
|
|
};
|
|
|
|
const getStringDeobfFuncs = {
|
|
ExpressionStatement(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.expression ||
|
|
node.expression.operator != "!" ||
|
|
!node.expression.prefix ||
|
|
!node.expression.argument ||
|
|
node.expression.argument.type != "CallExpression"
|
|
) {
|
|
return;
|
|
}
|
|
// ! get array func
|
|
let binding = path.scope.getBinding(
|
|
node.expression.argument.arguments[0].name
|
|
);
|
|
if (!binding) {
|
|
return;
|
|
}
|
|
decryptCode += generate(binding.path.node).code + "\n";
|
|
// ! get decrypt func
|
|
var bodyIndex = 0;
|
|
for (var i = 0; i < node.expression.argument.callee.body.body.length; i++) {
|
|
if (
|
|
node.expression.argument.callee.body.body[i].type ==
|
|
"FunctionDeclaration"
|
|
) {
|
|
bodyIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
decryptFuncName =
|
|
node.expression.argument.callee.body.body[bodyIndex].body.body[0].argument
|
|
.callee.name;
|
|
path.scope.crawl();
|
|
let binding1 = path.scope.getBinding(decryptFuncName);
|
|
if (!binding1) {
|
|
return;
|
|
}
|
|
decryptCode += generate(binding1.path.node).code + "\n";
|
|
decryptCode += generate(node).code + "\n";
|
|
binding1.path.remove();
|
|
binding.path.remove();
|
|
path.remove();
|
|
path.stop();
|
|
},
|
|
};
|
|
|
|
function makeid(length) {
|
|
let result = "";
|
|
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
const charactersLength = characters.length;
|
|
let counter = 0;
|
|
while (counter < length) {
|
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
counter += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const replaceInterceptingFuncNames = {
|
|
FunctionDeclaration(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.id ||
|
|
node.id.type != "Identifier" ||
|
|
node.id.name != decryptFuncName ||
|
|
!node.body ||
|
|
!node.body.body ||
|
|
node.body.body.length != 1
|
|
) {
|
|
return;
|
|
}
|
|
path.scope.crawl();
|
|
let binding = path.parentPath.scope.getBinding(node.id.name);
|
|
if (!binding) {
|
|
return;
|
|
}
|
|
var ID = t.identifier(makeid(10));
|
|
for (var i = 0; i < binding.referencePaths.length; i++) {
|
|
binding.referencePaths[i].replaceWith(ID);
|
|
}
|
|
node.id = ID;
|
|
},
|
|
};
|
|
|
|
const deobfStrings = {
|
|
CallExpression(path) {
|
|
const { node } = path;
|
|
|
|
if (
|
|
!node.callee ||
|
|
node.callee.type != "Identifier" ||
|
|
!node.arguments ||
|
|
node.arguments.length < 2
|
|
) {
|
|
return;
|
|
}
|
|
var valid = true;
|
|
for (var i = 0; i < node.arguments.length; i++) {
|
|
var arg = node.arguments[i];
|
|
let str = generate(arg).code;
|
|
if (arg.type == "StringLiteral" || str == "NaN") {
|
|
continue;
|
|
}
|
|
if (
|
|
arg.type != "UnaryExpression" &&
|
|
arg.type != "BinaryExpression" &&
|
|
arg.type != "NumericLiteral"
|
|
) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
|
|
if (str.match(/[A-z]/g) != null) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!valid) {
|
|
return;
|
|
}
|
|
// ! the logic here is we want to get the function this is calling
|
|
// ! then we want to keep getting the nested function calls until we get to the final function, aka the decryptFuncName
|
|
let code = "";
|
|
path.scope.crawl();
|
|
let binding = path.scope.getBinding(node.callee.name);
|
|
if (!binding) {
|
|
// ! hopefully no binding will always mean that the function in question is `r`???
|
|
path.replaceWith(
|
|
t.valueToNode(vm.runInContext(generate(node).code, decryptFuncCtx))
|
|
);
|
|
return;
|
|
}
|
|
// ! loop until we get to a place where we can't get a binding (aka hopefully the root function)
|
|
while (true) {
|
|
if (!binding) {
|
|
let a = generate(node).code;
|
|
if (a[0] == decryptFuncName) {
|
|
a[0] = "asd";
|
|
}
|
|
code += a;
|
|
break;
|
|
}
|
|
code += generate(binding.path.node).code + "\n";
|
|
path.scope.crawl();
|
|
binding = binding.path.scope.getBinding(
|
|
binding.path.node.body.body[0].argument.callee.name
|
|
);
|
|
}
|
|
// ! now we should have all the code we need
|
|
path.replaceWith(t.valueToNode(vm.runInContext(code, decryptFuncCtx)));
|
|
},
|
|
};
|
|
|
|
const deobfuscateStringConcatVisitor = {
|
|
BinaryExpression(path) {
|
|
let { confident, value } = path.evaluate();
|
|
if (!confident) return;
|
|
if (typeof value == "string") {
|
|
path.replaceWith(t.stringLiteral(value));
|
|
}
|
|
},
|
|
};
|
|
|
|
const getObfioObjs = {
|
|
VariableDeclarator(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.id ||
|
|
node.id.type != "Identifier" ||
|
|
!node.init ||
|
|
node.init.type != "ObjectExpression" ||
|
|
!node.init.properties ||
|
|
node.init.properties.length < 1
|
|
) {
|
|
return;
|
|
}
|
|
// ! further validation, just incase
|
|
let map = {};
|
|
let valid = true;
|
|
for (var i = 0; i < node.init.properties.length; i++) {
|
|
var prop = node.init.properties[i];
|
|
if (!prop.key || !prop.value || prop.key.type != "Identifier") {
|
|
valid = false;
|
|
break;
|
|
}
|
|
if (
|
|
prop.value.type != "FunctionExpression" &&
|
|
prop.value.type != "StringLiteral" &&
|
|
prop.value.type != "MemberExpression"
|
|
) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
if (prop.key.name.length != 5) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
if (
|
|
prop.value.type == "FunctionExpression" &&
|
|
prop.value.body.body[0].type != "ReturnStatement"
|
|
) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
map[prop.key.name] = prop.value;
|
|
}
|
|
if (!valid) {
|
|
return;
|
|
}
|
|
path.scope.crawl();
|
|
let binding = path.scope.getBinding(node.id.name);
|
|
if (!binding) {
|
|
return;
|
|
}
|
|
var ID = t.identifier(makeid(20));
|
|
for (var i = 0; i < binding.referencePaths.length; i++) {
|
|
binding.referencePaths[i].replaceWith(ID);
|
|
}
|
|
obfioObjMap[ID.name] = map;
|
|
path.remove();
|
|
},
|
|
};
|
|
|
|
function getArgs(arguments, cutFirst) {
|
|
var out = [];
|
|
for (var i = cutFirst ? 1 : 0; i < arguments.length; i++) {
|
|
out.push(arguments[i]);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const objDeobfMemberExpr = {
|
|
MemberExpression(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.object ||
|
|
!node.property ||
|
|
node.object.type != "Identifier" ||
|
|
!obfioObjMap[node.object.name]
|
|
) {
|
|
return;
|
|
}
|
|
let map = obfioObjMap[node.object.name];
|
|
let key;
|
|
if (node.property.type == "Identifier") {
|
|
key = node.property.name;
|
|
} else {
|
|
key = node.property.value;
|
|
}
|
|
let value = map[key];
|
|
if (value.type == "StringLiteral") {
|
|
path.replaceWith(value);
|
|
return;
|
|
}
|
|
if (value.type == "MemberExpression") {
|
|
map = obfioObjMap[value.object.name];
|
|
if (value.property.type == "Identifier") {
|
|
key = value.property.name;
|
|
} else {
|
|
key = value.property.value;
|
|
}
|
|
value = map[key];
|
|
path.replaceWith(value);
|
|
return;
|
|
}
|
|
output += `FAILED (1): ${generate(node).code}\n\n`;
|
|
},
|
|
CallExpression(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.callee ||
|
|
node.callee.type != "MemberExpression" ||
|
|
!node.callee.object ||
|
|
!node.callee.property ||
|
|
node.callee.object.type != "Identifier" ||
|
|
!obfioObjMap[node.callee.object.name]
|
|
) {
|
|
return;
|
|
}
|
|
let map = obfioObjMap[node.callee.object.name];
|
|
let key;
|
|
if (node.callee.property.type == "Identifier") {
|
|
key = node.callee.property.name;
|
|
} else {
|
|
key = node.callee.property.value;
|
|
}
|
|
let value = map[key];
|
|
// ! replace functions
|
|
let retNode = value.body.body[0].argument;
|
|
// ! call expression
|
|
if (retNode.type == "CallExpression") {
|
|
var callExprID;
|
|
// ! check if it's a reference to another object
|
|
if (retNode.callee.type == "MemberExpression") {
|
|
callExprID = retNode.callee;
|
|
} else {
|
|
callExprID = node.arguments[0];
|
|
}
|
|
var args = [];
|
|
if (
|
|
node.arguments.length > 1 ||
|
|
retNode.callee.type == "MemberExpression"
|
|
) {
|
|
args = getArgs(
|
|
node.arguments,
|
|
retNode.callee.type != "MemberExpression"
|
|
);
|
|
}
|
|
path.replaceWith(t.callExpression(callExprID, args));
|
|
return;
|
|
}
|
|
// ! BinaryExpression
|
|
if (retNode.type == "BinaryExpression") {
|
|
path.replaceWith(
|
|
t.binaryExpression(
|
|
retNode.operator,
|
|
node.arguments[0],
|
|
node.arguments[1]
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
output += `FAILED (2): ${generate(node).code}\n\n`;
|
|
},
|
|
};
|
|
|
|
function evalValue(left, right, op) {
|
|
switch (op) {
|
|
case "===":
|
|
return left == right;
|
|
case "!==":
|
|
return left != right;
|
|
}
|
|
}
|
|
|
|
const cleanupDeadCode = {
|
|
FunctionDeclaration(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.id ||
|
|
node.id.type != "Identifier" ||
|
|
!node.body ||
|
|
!node.body.body ||
|
|
!node.params ||
|
|
node.params.length < 2 ||
|
|
node.body.body.length != 1 ||
|
|
node.body.body[0].type != "ReturnStatement"
|
|
) {
|
|
return;
|
|
}
|
|
path.remove();
|
|
},
|
|
"IfStatement|ConditionalExpression"(path) {
|
|
const { node } = path;
|
|
if (
|
|
!node.test ||
|
|
!node.consequent ||
|
|
node.test.type != "BinaryExpression" ||
|
|
!node.test.left ||
|
|
!node.test.right ||
|
|
node.test.left.type != "StringLiteral" ||
|
|
node.test.right.type != "StringLiteral"
|
|
) {
|
|
// ! handle if(!("x" !== "x")) { } else { } here
|
|
if (
|
|
!node.test ||
|
|
!node.consequent ||
|
|
node.test.type != "UnaryExpression" ||
|
|
!node.test.argument ||
|
|
node.test.argument.type != "BinaryExpression" ||
|
|
!node.test.argument.left ||
|
|
!node.test.argument.right ||
|
|
node.test.argument.left.type != "StringLiteral" ||
|
|
node.test.argument.right.type != "StringLiteral"
|
|
) {
|
|
return;
|
|
}
|
|
if (
|
|
!evalValue(
|
|
node.test.argument.left.value,
|
|
node.test.argument.right.value,
|
|
node.test.argument.operator
|
|
)
|
|
) {
|
|
path.replaceWithMultiple(node.consequent);
|
|
return;
|
|
}
|
|
if (!node.alternate) {
|
|
path.remove();
|
|
return;
|
|
}
|
|
path.replaceWithMultiple(node.alternate);
|
|
return;
|
|
}
|
|
if (
|
|
evalValue(node.test.left.value, node.test.right.value, node.test.operator)
|
|
) {
|
|
path.replaceWithMultiple(node.consequent);
|
|
return;
|
|
}
|
|
path.replaceWithMultiple(node.alternate);
|
|
},
|
|
};
|
|
|
|
// ! replace the `x = 123` and `y = "asd"` things
|
|
traverse(AST, constantReplacer);
|
|
// ! replace `const n = {T: 702}`
|
|
traverse(AST, replaceObjSimple);
|
|
// ! replace mr[(t = "7d]D", o = 896, r(o - 493, t)), mr[(t = "hyP7", r = 661, o = 735, $(t - 252, o - 1061, t, r - 204, o - 6))]
|
|
traverse(AST, replaceExprStmts);
|
|
/*
|
|
replace the stuff that looks like this:
|
|
iu[function (n, t, W, r, u) {
|
|
return On(n - 247, W, r - 606, r - 38, u - 235);
|
|
}(1095, 0, "e9so", 1006, 1053)]
|
|
*/
|
|
// ! if this code breaks, I'd assume it's likely because of the static way I'm doing the variable replacement
|
|
traverse(AST, replaceWeirdProxyCall);
|
|
// ! get the string deobf code and func name, the entry point is the array shifter exprstmt
|
|
traverse(AST, getStringDeobfFuncs);
|
|
// ! this is a really hacky way to fix this "problem" since I'm doing the string deobf in a bad way
|
|
// ! some func names are the same as the main decrypt func name so it'll error when you try to deobf the strings
|
|
traverse(AST, replaceInterceptingFuncNames);
|
|
|
|
vm.runInContext(decryptCode, decryptFuncCtx);
|
|
// ! finally we can decrypt/deobf our strings
|
|
traverse(AST, deobfStrings);
|
|
// ! now we need to concat strings so we can properly deobf the object obfuscation
|
|
// (stolen from pianoman)
|
|
traverse(AST, deobfuscateStringConcatVisitor);
|
|
let obfioObjMap = {};
|
|
// ! first we need to rename all objects and all uses of those objects
|
|
// ! some objects have conflicting names which can mess up this solution, easiest fix is just renaming them
|
|
// ! then we will populate the obfioObjMap
|
|
traverse(AST, getObfioObjs);
|
|
// ! now we can deobf the object obfuscation
|
|
traverse(AST, objDeobfMemberExpr);
|
|
// ! clean dead code, like the proxy functions we never removed
|
|
traverse(AST, cleanupDeadCode);
|
|
|
|
writeFileSync("output.log", output, "utf-8");
|
|
|
|
const final_code = generate(AST, beautify_opts).code;
|
|
|
|
fs.writeFileSync(outputPath, final_code);
|