基础练一练,不喜勿喷。
jscode对应visitor,jscode1对应visitor1,以此类推。
/*
* 安装 npm install @babel/core
* */
// 将JS源码转换成语法树
const parser = require("@babel/parser");
// 为parser提供模板引擎
const template = require("@babel/template").default;
// 遍历AST
const traverse = require("@babel/traverse").default;
// 操作节点,比如判断节点类型,生成新的节点等
const t = require("@babel/types");
// 将语法树转换为源代码
const generator = require("@babel/generator").default;
// 操作文件
const fs = require("fs");
//https://astexplorer.net/
/*
type: 表示当前路径的类型;
start:表示当前节点的起始位置;
end: 表示当前节点的结束位置;
loc: 表示当前节点所在源代码中的起始和结束的行号和列号。
declarations :声明
kind:表示定义的类型
*/
const visitor = {
VariableDeclarator(path) {
// 当前路径所对应的源代码,使用toString方法
console.log(path.toString());// a = 123 b = 'ccc'
//获取path的上一级路径,使用parentPath
let parent = path.parentPath;
console.log(parent.node.kind) //var let
//获取path的子路径,使用 get方法,得到id节点
let id = path.get('id');
console.log(id.node.name); //a b
//获取当前路径的节点
const node = path.node;
console.log(node.init.value);//123 ccc
//得到init节点
let init = path.get('init');
//判断node下的init节点类型
if (t.isNumericLiteral(node.init)) {
id.replaceWith(t.Identifier('A'));
console.log(id.node.name); //A
init.replaceWith(t.NumericLiteral(4));
console.log(node.init.value);//4
}
//直接拿init节点来判断类型
if (init.isStringLiteral()) {
id.replaceWith(t.Identifier('B'));
console.log(id.node.name); //B
init.replaceWith({type: "StringLiteral", value: 'qqq'});
console.log(init.node.value);//qqq
// 删除init节点
// init.remove()
// delete path.node.init;
}
}
};
//替换path,单路径可以使用replaceWith方法,多路径则使用replaceWithMultiple方法
//init.replaceWith 和 path.replaceWith区别:前者只是替换当前遍历节点里的init节点,后者是把当前遍历节点整个替换
const visitor1 = {
CallExpression(path) {
console.log(path.toString()); //aaa(1, 2)
const node = path.node;
// 获取CallExpression节点结构
// console.log(JSON.stringify(node,null,'\t'));
console.log(node.callee.name); //aaa
console.log(node.arguments[0].value); //1
console.log(node.arguments[1].value); //2
let callee = path.get('callee');
// console.log(callee.toString()); //aaa
console.log(callee.node.name) //aaa
}
};
const visitor2 = {
'StringLiteral|NumericLiteral'(path) {
// let value = path.node.value
// //此行效果等于上行
// // let {value} = path.node
// console.log(value)
//之所以会以十六进制显示,完全是 raw这个节点造成的,那就删除
delete path.node.extra
}
};
const visitor3 = {
MemberExpression(path) {
// const node = path.node;
// let property = path.get('property')
// if (t.isStringLiteral(node.property)) {
// let value = node.property.value;
// console.log(value)
// //原为true,改后的效果把[]变为.
// node.computed = false
// //如果写成path.replaceWith是将整个MemberExpression节点换为value,节点类型也变为Identifier,例:window.btoa变为btoa
// //我们仅需要替换property节点
// property.replaceWith(t.Identifier(value))
// }
//效果同上
let property = path.get('property')
if (property.isStringLiteral()) {
let value = property.node.value;
console.log(value)
path.node.computed = false
property.replaceWith(t.Identifier(value))
}
}
};
const visitor4 = {
VariableDeclaration(path) {
const operator = '='; //赋值语句的运算符是 =
const left = t.Identifier('b'); //新建赋值语句左边的节点;
const right = t.NumericLiteral(456);//新建赋值语句右边的节点;
const new_assign = t.AssignmentExpression(operator, left, right) //构造一个赋值语句节点
const new_express = t.ExpressionStatement(new_assign); //再构造成表达式语句
path.insertBefore(new_express);//在变量声明语句前面插入
const new_vdtor = t.VariableDeclarator(left, right)
const new_vdtion = t.VariableDeclaration('let', [new_vdtor])
path.insertAfter(new_vdtion); //在变量声明语句后面插入
var str = path.toString()
const new_memberexp = t.MemberExpression(t.Identifier('console'), t.Identifier('log'),)
const new_callexp = t.CallExpression(new_memberexp, [t.StringLiteral(str)])
path.insertAfter(new_callexp); //在变量声明语句后面插入
path.stop() //不停止会无限循环下去
}
}
const visitor5 = {
SwitchCase(path) {
const node = path.node;
let consequent = path.get('consequent');//数组类型
//长度
// console.log(consequent.length)
for (var i = 0; i < consequent.length; i++) {
if (t.isContinueStatement(consequent[i])) continue
const conqt = consequent[i].toString();
const new_memberexp = t.MemberExpression(t.Identifier('console'), t.Identifier('log'),);
const new_callexp = t.CallExpression(new_memberexp, [t.StringLiteral(conqt)]);
consequent[i].insertBefore(new_callexp); //在变量声明语句后面插入
// 可删除原来的节点
// t.isReturnStatement(consequent[i]) ? consequent[i].node.argument = null : consequent[i].remove()
consequent[i].stop(); //不停止会无限循环下去
}
//运行得到的代码得到混淆前的代码
}
}
const visitor6 = {
NumericLiteral(path) {
// const node = path.node;
// const value = node.value
//等效上2行
const value = path.toString()
const first = 0 - Math.floor(Math.random() * 10000000 + 10000000);
const second = value ^ first;
const binexp = t.BinaryExpression('^', t.NumericLiteral(first), t.NumericLiteral(second))
path.replaceWith(binexp)
path.stop()
}
}
const visitor7 = {
//默认是enter,先遍历顶层,修改为exit,则是先遍历底层,看visitor7_1
'BinaryExpression|UnaryExpression|ConditionalExpression'(path) {
// const value = path.evaluate().value;
const {value} = path.evaluate();
// console.log(path.toString(),' ',value)
switch (typeof value) {
case 'boolean':
path.replaceWith(t.BooleanLiteral(value))
break;
case 'string':
path.replaceWith(t.StringLiteral(value))
break;
case 'number':
path.replaceWith(t.NumericLiteral(value))
break;
default:
break;
}
}
}
// visitor7另一种写法
const visitor7_1 = function (path) {
// const value = path.evaluate().value;
const {value} = path.evaluate();
console.log(path.toString(), ' ', value)
// switch (typeof value) {
// case 'boolean':
// path.replaceWith(t.BooleanLiteral(value))
// break;
// case 'string':
// path.replaceWith(t.StringLiteral(value))
// break;
// case 'number':
// path.replaceWith(t.NumericLiteral(value))
// break;
// default:
// break;
// }
}
const visitor8 = {
VariableDeclarator(path) {
//此法为替换原init,
console.log(path.toString())
const init = path.get('init') //a = 123 | 456
const left = init.node.left.value //123
const right = init.node.right.value //456
//重新构造一个函数
const p_left = t.Identifier('s'), p_right = t.Identifier('h') //设定2个形参
const params = [p_left, p_right] //数组类型
const retsta = [t.ReturnStatement(t.BinaryExpression('|', p_left, p_right))]//数组类型
const body = t.BlockStatement(retsta,) //第二参数默认[]
const callee = t.functionExpression(null, params, body,)
const arguments = [t.NumericLiteral(left), t.NumericLiteral(right)] //数组类型
const new_callexp = t.callExpression(callee, arguments)
init.replaceWith(new_callexp)
// 还可以直接在原init上修改
// const init = path.get('init') //a = 123 | 456
// const left = init.node.left.value //123
// const right = init.node.right.value //456
//
// const p_left = t.Identifier('s'), p_right = t.Identifier('h') //设定2个形参
// const params = [p_left, p_right] //数组类型
// const retsta = [t.ReturnStatement(t.BinaryExpression('|', p_left, p_right))]//数组类型
// const body = t.BlockStatement(retsta,)
// init.node.type = "CallExpression";
// init.node.arguments = [t.NumericLiteral(left), t.NumericLiteral(right)];
// init.node.callee = t.functionExpression(null, params, body,)
}
}
const visitor9 = {
StringLiteral(path) {
delete path.node.extra;
// 发现英文的Unicode形式被还原了,但是中文的没有被还原
// 需要给generator(ast,)添加参数{jsescOption:{"minimal":true}}
}
}
const visitor10 = {
VariableDeclarator(path) {
const init = path.get('init');
if (init.isFunctionExpression()) {
const {init, id} = path.node;
const name = id.name;
const params = init.params;
if (params.length !== 2) return;
let first_arg = params[0].name;
let second_arg = params[1].name;
const body = init.body;
if (!body.body || body.body.length !== 1) return;
let return_body = body.body[0];
let argument = return_body.argument;
if (!t.isReturnStatement(return_body) ||
!t.isBinaryExpression(argument)) return;
let {left, right, operator} = argument;
if (!t.isIdentifier(left, {name: first_arg}) ||
!t.isIdentifier(right, {name: second_arg})) return;
let scope = path.scope;
console.log(scope)
traverse(scope.block, {
CallExpression: function(_path){
let _node = _path.node;
let args = _node.arguments;
if (args.length === 2 && t.isIdentifier(_node.callee, {name: name}))
_path.replaceWith(t.BinaryExpression(operator, args[0], args[1]))
},
});
}
}
};
const visitor11 = {}
const visitor12 = {}
var jscode = `var a = 123;let b='ccc';`
var jscode1 = `function aaa(a,b){}aaa(1,2);`
var jscode2 = `h[$b('\x30\x78\x37\x31', '\x31\x21\x58\x47') + '\x4a\x61'] = function (Q, R) {return Q + R;}
var num = 0x30+0x31;
var $a = ['\x77\x71\x78\x70\x55\x41\x3d\x3d']
`
var jscode3 = `
if (!j) {
g['setCookie'](['*'], 'counter', 0x1);
} else if (j) {
i = g['getCookie'](null, 'counter');
} else {
g['removeCookie']();
}
(function (_0x1a61bd) {})()
['constructor']("while (true) {}")["apply"]("counter");
`
var jscode4 = `var a = 123;`
var jscode5 = `function test() {
var _0x238e3e = {
'puzUT': '4|3|2|1|0', 'Rgtsl': function (_0x3a9ad8, _0x312b1a) {
return _0x3a9ad8 < _0x312b1a;
}, 'HFHYB': function (_0x13ea88, _0x2533df) {
return _0x13ea88 + _0x2533df;
}
};
var _0x388255 = _0x238e3e['puzUT']['split']('|');
var _0xfbda52 = 0x0;
while (!![]) {
switch (_0x388255[_0xfbda52++]) {
case'0':
return _0x2ce383;
case'1':
for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {
_0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));
}
continue;
case'2':
var _0x2ce383 = [];
continue;
case'3':
_0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);
continue;
case'4':
var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
continue;
}
break;
}
}
var s = test();`
var jscode6 = `var value = 12345678;`
var jscode7 = `var a=!![],
b='hello '+'world '+'!',
c=2+3*50,
d=Math.abs(-200)%19,
e=true ? 123:456;`
var jscode8 = `var a = 123 | 456;`
var jscode9 = `var test ='\u4f60\u597d\u4e16\u754c\u0054\u0068\u0069\u0073\u0020\u0069\u0073\u0020\u0061\u0020\u0074\u0065\u0073\u0074\u0021'`
var jscode10 = `var Xor = function (p,q){
return p ^ q;
}
let a = Xor(111,222);`
var jscode11 = ``
var jscode12 = ``
let ast = parser.parse(jscode10); //修改此处
// console.log(JSON.stringify(ast,null,'\t'))
// traverse(ast,visitor) //var a = 123;let b='ccc';
// traverse(ast,visitor1) //aaa(1,2);
// traverse(ast, visitor2) //将\x30\x78\x37\x31转变为可识别字符串,十六进制数值还原成十进制
// traverse(ast, visitor3) //将a["length"]转变为a.length
// traverse(ast, visitor4) //插入节点
// traverse(ast, visitor5) //SwitchCase插入节点,平坦化还原
// traverse(ast, visitor6) //把12345678拆成两个数的异或操作
// traverse(ast, visitor7) //合并Literal类型的计算表达式
// traverse(ast, { //visitor7 的另一种写法
// 'BinaryExpression|UnaryExpression|ConditionalExpression': {
// enter: [visitor7_1]
// //exit: [visitor7_1]
// }
// })
// traverse(ast, visitor8) //将 BinaryExpression 类型转换为 CallExpression 类型
// traverse(ast, visitor9) //将unicode码转为可识别的字符
traverse(ast, visitor10) //CallExpression -> BinaryExpression
// traverse(ast, visitor11)
// traverse(ast, visitor12)
let {code} = generator(ast,); //第9需要添加参数{jsescOption:{"minimal":true}}
console.log(code)
// fs.writeFile('xxxx.js', code, (err)=>{});