Skip to content

Instantly share code, notes, and snippets.

@yyuueexxiinngg
Created April 24, 2022 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yyuueexxiinngg/acf0a11405eaaf4e786aafb845849601 to your computer and use it in GitHub Desktop.
Save yyuueexxiinngg/acf0a11405eaaf4e786aafb845849601 to your computer and use it in GitHub Desktop.
JavaScript AST deobfuscate script
// code from https://jia666666.blog.csdn.net/article/details/120427994
const fs = require("fs");//文件读写
const parse = require("@babel/parser"); //解析为ast
const traverse = require('@babel/traverse').default;//遍历节点
const t = require('@babel/types');//类型
const generator = require('@babel/generator').default;//ast解析为代码
//读取js文件
const jscode = fs.readFileSync(
'./demo.js', {
encoding: 'utf-8'
}
);
let ast = parse.parse(jscode);//js转ast
try {
// //TODO 1 字符还原
ast = decry_str(ast)//16进制数字还原与字符还原
console.log('第一步:准备工作已完成')
//TODO 2 大数组解密
ast = parse.parse(generator(ast).code);//刷新ast
ast = decrypt_arr(ast)//大数组还原
console.log('第二步:大数组解密已完成')
//TODO 3 拆分对象合并
ast = parse.parse(generator(ast).code);//刷新ast
traverse(ast, { VariableDeclarator: { exit: [merge_obj] }, }); // 将拆分的对象重新合并-花指令还原准备工作
console.log('第三步:拆分对象合并已完成')
//TODO 4 对象表达式字符串合并
traverse(ast, { ObjectProperty: { exit: [AddObjPro] }, }); //
console.log('第四步:对象表达式字符串合并已完成')
//TODO 5 花指令函数处理
ast = parse.parse(generator(ast).code);//刷新ast
traverse(ast, { VariableDeclarator: { exit: [callToStr] }, }); // 对象替换
console.log('第五步:花指令处理已完成')
//
//TODO 6 禁用console删减
ast = parse.parse(generator(ast).code)//刷新ast
traverse(ast, { VariableDeclarator: { exit: [DelConsole_one] }, });
ast = parse.parse(generator(ast).code)//刷新ast
traverse(ast, { VariableDeclarator: { exit: [DelConsole_two] }, });
console.log('第六步:禁用console删减已完成')
//TODO 7 去除逗号表达式
traverse(ast, {ExpressionStatement: remove_comma,}); // 去除逗号表达式
console.log('第七步:去除逗号表达式已完成')
//TODO 8 删除定时器部分
traverse(ast, { CallExpression: { exit: [del_setInterval] }, }); // 删减定时器
console.log('第八步:删除定时器部分已完成')
// //TODO 9 删减debugger部分
traverse(ast, { VariableDeclarator: { exit: [DelDebuger_one] } }); //禁用debugger删减
ast = parse.parse(generator(ast).code);
traverse(ast, { FunctionDeclaration: { enter: [DelDebuger_two] } }); //禁用debugger删减
console.log('第九步:删减debugger部分已完成')
//TODO 10 替换空参数的自执行方法为顺序语句
traverse(ast, { ExpressionStatement: delConvParam, }) // 替换空参数的自执行方法为顺序语句
//TODO 11 常量计算
traverse(ast, { // 常量计算,慎用!
"UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": eval_constant,
});
} catch (e) {
console.log(e);
} finally {
//TODO Finally ast还原js
code = generator(ast, opts = { jsescOption: { "minimal": true } }).code// 处理中文Unicode
//文件保存
fs.writeFile('./demoNew.js', code, (err) => {
});
}
function add_Mem_str(path) {
let node = path.node;
if (node.computed && t.isBinaryExpression(node.property) && node.property.operator == '+') {
let BinNode = node.property;//属性节点
let tmpast = parse.parse(generator(BinNode).code);
let addstr = '';
traverse(tmpast, {
BinaryExpression: {
exit: function (_p) {
if (t.isStringLiteral(_p.node.right) && t.isStringLiteral(_p.node.left)) {//二进制表达式左右有一个类型为字符型
_p.replaceWith(t.StringLiteral(eval(generator(_p.node).code))) // 值替换节点
}
addstr = _p.toString();
}
}
})
node.property = t.Identifier(addstr);
}
}
function decrypt_arr(ast) {
//TODO 1 解密三部分的代码执行
let end = 3;//切片需要处理的代码块
let newAst = parse.parse('');//新建ast
let decrypt_code = ast.program.body.slice(0, end);//切片
newAst.program.body = decrypt_code// 将前3个节点替换进新建ast
let stringDecryptFunc = generator(newAst, { compact: true },).code;//转为js,由于存在格式化检测,需要指定选项,来压缩代码// 自动转义
eval(stringDecryptFunc);//执行三部分的代码
//TODO 2 准备工作及对解密三部分节点删除
let stringDecryptFuncAst = ast.program.body[end - 1];// 拿到解密函数所在的节点
let DecryptFuncName = stringDecryptFuncAst.declarations[0].id.name;//拿到解密函数的名字
var rest_code = ast.program.body.slice(end); // 剩下的节点
ast.program.body = rest_code;//剩下的节点替换
//TODO 3 加密数组还原
traverse(ast, {
CallExpression(path) {//回调表达式匹配--替换加密数组为对应的值
if (t.isIdentifier(path.node.callee, { name: DecryptFuncName })) { //当变量名与解密函数名相同时,就执行相应操作
path.replaceWith(t.valueToNode(eval(path.toString()))); // 值替换节点
}
},
});
traverse(ast, { MemberExpression: { exit: [add_Mem_str] }, }); // 成员表达式字符串合并
return ast;
}
function merge_obj(path) {
// 将拆分的对象重新合并
const { id, init } = path.node;//提取节点指定的值
if (!t.isObjectExpression(init))//如果指定属性不是对象表达式,退出
return;
let name = id.name;//获取id的名称
let properties = init.properties;//获取初始属性数组
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(name);//
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
paths.map(function (refer_path) {
let bindpath = refer_path.parentPath;//父路径
if (!t.isVariableDeclarator(bindpath.node)) return;//变量声明
let bindname = bindpath.node.id.name;//获取变量节点声明的值
bindpath.scope.rename(bindname, name, bindpath.scope.block);//变量名重命名,传作用域参数
bindpath.remove();//删除节点
});
scope.traverse(scope.block, {
AssignmentExpression: function (_path) {//赋值表达式
const left = _path.get("left");//节点路径左侧信息
const right = _path.get("right");//节点路径右侧信息
if (!left.isMemberExpression())//左侧是否为成员表达式
return;
const object = left.get("object");//获取左侧信息的对象
const property = left.get("property");//获取左侧信息的属性
//a={},a['b']=5;合并后a={'b':5}
if (object.isIdentifier({ name: name }) && property.isStringLiteral() && _path.scope == scope) {
properties.push(t.ObjectProperty(t.valueToNode(property.node.value), right.node));
_path.remove();
}
//a={},a.b=5;合并后a={'b':5}
if (object.isIdentifier({ name: name }) && property.isIdentifier() && _path.scope == scope) {
properties.push(t.ObjectProperty(t.valueToNode(property.node.name), right.node));
_path.remove();
}
}
})
}
function callToStr(path) {
// 将对象进行替换
var node = path.node;//获取路径节点
if (!t.isObjectExpression(node.init))//不是对象表达式则退出
return;
var objPropertiesList = node.init.properties; // 获取对象内所有属性
if (objPropertiesList.length == 0) // 对象内属性列表为0则退出
return;
var objName = node.id.name; // 对象名
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(objName);//
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
let paths_sums = 0;//路径计数
objPropertiesList.forEach(prop => {
var key = prop.key.value;//属性名
if (t.isFunctionExpression(prop.value))//属性值为函数表达式
{
var retStmt = prop.value.body.body[0];//定位到ReturnStatement
path.scope.traverse(path.scope.block, {
CallExpression: function (_path) {//调用表达式匹配
let _path_binding = _path.scope.getBinding(objName);//当前作用域获取绑定
if (_path_binding != binding) return;//两者绑定对比
if (!t.isMemberExpression(_path.node.callee))//成员表达式判定
return;
var _node = _path.node.callee;//回调函数节点
if (!t.isIdentifier(_node.object) || _node.object.name !== objName)//非标识符检测||节点对象名全等验证
return;
if (!(t.isStringLiteral(_node.property) || t.isIdentifier(_node.property)))//节点属性非可迭代字符验证||节点属性标识符验证
return;
if (!(_node.property.value == key || _node.property.name == key))//节点属性值与名称等于指定值验证
return;
if (!t.isStringLiteral(_node.property) || _node.property.value != key)//节点属性可迭代字符验证与节点属性值与指定值等于验证
return;
var args = _path.node.arguments;//获取节点的参数
// 二元运算
if (t.isBinaryExpression(retStmt.argument) && args.length === 2)//二进制表达式判定且参数为两个
{
_path.replaceWith(t.binaryExpression(retStmt.argument.operator, args[0], args[1]));//二进制表达式替换当前节点
}
// 逻辑运算
else if (t.isLogicalExpression(retStmt.argument) && args.length == 2)//与二元运算一样
{
_path.replaceWith(t.logicalExpression(retStmt.argument.operator, args[0], args[1]));
}
// 函数调用
else if (t.isCallExpression(retStmt.argument) && t.isIdentifier(retStmt.argument.callee))//回调函数表达式判定及回调参数部分判定
{
_path.replaceWith(t.callExpression(args[0], args.slice(1)))
}
paths_sums += 1;//删除计数标志
}
})
} else if (t.isStringLiteral(prop.value)) {//属性值为可迭代字符类型
var retStmt = prop.value.value;//属性值的值即A:B中的B部分
path.scope.traverse(path.scope.block, {
MemberExpression: function (_path) {//成员表达式
let _path_binding = _path.scope.getBinding(objName);//当前作用域获取绑定
if (_path_binding != binding) return;//两者绑定对比
var _node = _path.node;
if (!t.isIdentifier(_node.object) || _node.object.name !== objName)//节点对象标识符验证|节点对象名验证
return;
if (!(t.isStringLiteral(_node.property) || t.isIdentifier(_node.property)))//节点属性可迭代字符验证|标识符验证
return;
if (!(_node.property.value == key || _node.property.name == key))//节点属性值与名称等于指定值验证
return;
if (!t.isStringLiteral(_node.property) || _node.property.value != key)//节点属性可迭代字符判定|节点属性值等于指定值验证
return;
_path.replaceWith(t.stringLiteral(retStmt))//节点替换
paths_sums += 1;//删除计数标志
}
})
}
});
if (paths_sums == paths.length) {//若绑定的每个路径都已处理 ,则移除当前路径
path.remove();//删除路径
}
}
function DelConsole_one(path) {
// 删除console
let node = path.node;//获取路径节点
if (!t.isCallExpression(node.init)) return;//不是回调表达式,退出
if (node.init.arguments.length !== 2) return;//形参不等于2个
if (!t.isThisExpression(node.init.arguments[0])) return;//this表达式
let thisname = node.id.name;//节点名称
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(thisname);//获取绑定
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
paths.map(function (refer_path) {
let bindpath = refer_path.parentPath;//父路径
if (!t.isCallExpression(bindpath)) return;//回调表达式判断
if (!t.isIdentifier(bindpath.node.callee)) return;//标识符判定
bindpath.remove();//删除路径
});
path.remove();//删除路径
}
function DelConsole_two(path) {
// 删除console遗留下列未使用的定义变量
let node = path.node;//获取路径节点
if (!t.isCallExpression(node.init)) return;//不是回调表达式,退出
if (node.init.arguments.length !== 0) return;//形参不等于0个
if (!t.isFunctionExpression(node.init.callee)) return;//this表达式
let thisname = node.id.name;//节点名称
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(thisname);//获取绑定
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
if (paths.length !== 0) return;
path.remove();//删除路径
}
function remove_comma(path) {
// 去除逗号表达式
let { expression } = path.node
if (!t.isSequenceExpression(expression))
return;
let body = []
expression.expressions.forEach(
express => {
body.push(t.expressionStatement(express))
}
)
path.replaceInline(body)
}
function del_setInterval(path) {
// 将对象进行替换
var node = path.node;//获取路径节点
if (!t.isIdentifier(node.callee))//不是标识符则退出
return;
if (node.callee.name != 'setInterval') return;//不是定时器退出
if (node.arguments.length !== 2) return;
if (!t.isFunctionExpression(node.arguments[0]) || node.arguments[0].params.length !== 0) return;
let InterNode = node.arguments[0].body.body[0];
if (!t.isExpressionStatement(InterNode)) return;
if (!t.isCallExpression(InterNode.expression)) return;
if (!t.isIdentifier(InterNode.expression.callee)) return;
let InterName = InterNode.expression.callee.name;
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(InterName);//
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
// if(paths.length==0)return;//引用路径必须等于1
let paths_sums = 0;//路径计数
paths.map(function (refer_path) {
let bindpath = refer_path.parentPath;//父路径
let break_sign = true;//while循环控制
while (break_sign) {
try {
bindpath.remove();//路径删除
paths_sums += 1;//处理数+1
break_sign = false;//while循环终止
} catch (e) {
bindpath = bindpath.parentPath;
}
}
});
if (paths_sums == paths.length) {//若绑定的每个路径都已处理 ,则移除当前路径
path.remove();//删除路径
}
}
function DelDebuger_one(path) {
// 将对象进行替换
var node = path.node;//获取路径节点
if (!t.isCallExpression(node.init)) return;//回调表达式过滤
if (node.init.arguments.length !== 0) return;//实参个数为0
if (!t.isFunctionExpression(node.init.callee)) return;//函数表达式过滤
if (node.init.callee.params.length !== 0) return;//形参个数过滤
let varName = node.id.name;//定义的变量名称
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(varName);//
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
// if(paths.length===0)return;//引用路径必须等于1
let paths_sums = 0;//路径计数
paths.map(function (refer_path) {
let bindpath = refer_path.parentPath;//父路径
let BinNode = bindpath.node;//获取路径节点
if (!t.isCallExpression(BinNode)) return;//不是回调表达式,退出
if (BinNode.arguments.length !== 2) return;//形参不等于2个
if (!t.isThisExpression(BinNode.arguments[0])) return;//this表达式
let thisname = BinNode.callee.name;//节点名称
if (thisname !== varName) return;//二次确认,名称不等退出
let break_sign = true;//while循环控制
while (break_sign) {
try {
bindpath.remove();//路径删除
paths_sums += 1;//处理数+1
break_sign = false;//while循环终止
} catch (e) {
bindpath = bindpath.parentPath;
}
}
});
if (paths_sums == paths.length) {//若绑定的每个路径都已处理 ,则移除当前路径
path.remove();//删除路径
}
}
function DelDebuger_two(path) {
//删减deugger未引用的函数
var node = path.node;//获取路径节点
let varName = node.id.name;//定义的变量名称
let scope = path.scope;//获取路径的作用域
let binding = scope.getBinding(varName);//
if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
return;
}
let paths = binding.referencePaths;//绑定引用的路径
if (paths.length !== 0) return;//引用路径必须等于1
path.remove();//删除路径
}
function AddObjPro(path) {
if (t.isBinaryExpression(path.node.value)) {
let BinNode = path.node.value;//属性节点
if (!t.isBinaryExpression(BinNode)) return;//二相式表达式验证
try {
path.node.value = t.StringLiteral(eval(generator(BinNode).code)); // 值替换节点
} catch (e) {
}
}
}
function delConvParam(path) {
// 替换空参数的自执行方法为顺序语句
let node = path.node;//路径节点
let node_exp = node.expression;//节点表达式
//回调表达式|一元表达式
if (!t.isCallExpression(node_exp) && !t.isUnaryExpression(node_exp))
return;
//实参列表为空且长度不大于0
if (node.expression.arguments !== undefined && node.expression.arguments.length > 0)
return;
if (t.isUnaryExpression(node_exp) && node_exp.operator == '!') {//第二种自执行修改为第一种类型
node_exp = node_exp.argument;
}
if (t.isCallExpression(node_exp)) {//第一种自执行
if (!t.isFunctionExpression(node_exp.callee))//函数表达式判断
return;
let paramsList = node_exp.callee.params//形参列表
if (paramsList.length > 0) {
paramsList.map(function (letname) {
if (t.isIdentifier(letname)) {
//定义一个变量,并添加到结构体中
let varDec = t.VariableDeclarator(t.identifier(letname.name))//
let localAST = t.VariableDeclaration('var', [varDec]);//
node_exp.callee.body.body.unshift(localAST);//添加
}
})
}
// 替换节点
path.replaceInline(node_exp.callee.body.body);
}
}
function eval_constant(path) {
// 常量计算
if (path.type == "UnaryExpression") {
const { operator, argument } = path.node;
if (operator == "-" && t.isLiteral(argument)) {
return;
}
}
const { confident, value } = path.evaluate();
// 无限计算则退出,如1/0与-(1/0)
if (value == Infinity || value == -Infinity)
return;
confident && path.replaceWith(t.valueToNode(value));
}
function decry_str(ast) {
//数字与字符还原
traverse(ast, {
'StringLiteral|NumericLiteral|DirectiveLiteral'(path) {//迭代字符串|迭代数组匹配--16进制文本还原
delete path.node.extra; //删除节点的额外部分-触发原始值处理
},
});
return ast;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment