//在此之前,先了解下path和node
/*
path指的是路径 其常用的方法
当前路径所对应的源代码 : path.toString
判断path是什么type,使用path.isXXX 这个方法 : if(path.isStringLiteral()){}
获取path的上一级路径 : let parent = path.parentPath;
获取path的子路径 : path.get('test');
删除path : path.remove()
替换path(一个节点) : path.replaceWith({type:"NumericLiteral",value:3});
替换path(多个节点) : path.replaceWithMultiple([{type:"NumericLiteral",value:3}]);
插入path : path.insertAfter({type:"NumericLiteral",value:3});
node指的是节点 是path的一个属性 可以通过path.node来获取node 本质是一个json结果的数据
获取原码 :
const generator = require("@babel/generator").default;
let {code} = generator(node);
删除节点 : path.node.init = undefined;
访问子节点的值(init节点的value属性) : console.log(path.node.init.value);
节点的类型判断 : 先引入types if(types.isBlockStatement(path.node)){}
path和node关系: node是path的一个属性
举例: path.node.test === path.get("test").node 结果是True
*/
1.表达式还原
js文件内容:
const a = !![]
const b = "abc" == "dsa";
const c = (1 << 2) | 2;
const d = parseInt("5" + "0")
还原代码:
import { parse } from "@babel/parser";
import traverse from "@babel/traverse";
import generate from "@babel/generator";
import * as types from "@babel/types";
import fs from "fs";
const code = fs.readFileSync("code/表达式还原.js","utf-8");
let ast = parse(code);
traverse(ast,{
//下面对应的是一元表达式、二元表达式、条件表达式、调用表达式
"UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression" : (
path
) => {
//执行path对应的语句,并将结果中的value属性赋值给value变量,将confident属性赋值给confident变量
//confident 是可信度 value是计算结果
const { confident , value } = path.evaluate();
//如果表达式计算的结果是正负无穷大,则return不做任何处理
if (value == Infinity || value == -Infinity)return
if (confident){
//如果结果可信,将结果转换为节点替换当前节点
path.replaceWith(types.valueToNode(value));
}
}
});
const{ code: output } = generate(ast);
console.log(output)
2.字符串还原
const strings = ["\x68\x65\x6c\x6c\x6f","\x77\x6f\x72\x6c\x64"]
将这段js代码解混淆如下,可以看出如果我们将raw的值变为正常,那么还原ast的时候就能的得到想要的代码
还原过程:
import { parse } from "@babel/parser";
import traverse from "@babel/traverse";
import generate from "@babel/generator";
import * as types from "@babel/types";
import fs from "fs";
const code = fs.readFileSync("code/表达式还原.js","utf-8");
let ast = parse(code);
traverse(ast,{
//此处的{ node } 写法就等价于 { node } = path 等价于 node = path.node
StringLiteral({ node }){
// /正则内容/gi.test(要匹配的字符串) 这个写法是JS中正则的写法,返回值是布尔值 详情可见https://www.runoob.com/jsref/jsref-obj-regexp.html
if (node.extra && /\\[ux]/gi.test(node.extra.raw)){
node.extra.raw = node.extra.rawValue;
}
},
});
const{ code: output } = generate(ast);
console.log(output)
3.无用代码剔除(僵尸代码)
如下代码,第一个条件结果永远是true,第二个条件结果永远是false,所以可以通过判断IfStatement节点下的test节点的计算结果,来显示代码,其中if 下的条件对应的是consequent,else内的代码对应的是alternate
const _0x16c18d = function(){
if(!![[]]){
console.log("hello,world")
}else{
console.log("1111")
console.log("2222")
console.log("3333")
console.log("4444")
}
};
const _0x1f7292 = function (){
if ("dhsanhdu7dsa8".charAt(4) !== String.fromCharCode(110)){
console.log("this")
console.log("is")
console.log("dead")
console.log("code")
}else {
console.log("nice to meet you")
}
};
_0x16c18d();
_0x1f7292();
import { parse } from "@babel/parser";
import traverse from "@babel/traverse";
import generate from "@babel/generator";
import * as types from "@babel/types";
import fs from "fs";
const code = fs.readFileSync("code/表达式还原.js","utf-8");
let ast = parse(code);
traverse(ast,{
IfStatement(path){
let result = path.get("test").evaluateTruthy(); //计算test路径下代码的结果是否为真 返回布尔值
let consequent = path.get("consequent")
let alternate = path.get("alternate")
if (result === true){
if (consequent.isBlockStatement()){
path.replaceWith(consequent.node)
}
}else if(result === false){
if (alternate.isBlockStatement()){
path.replaceWith(alternate.node)
}
}else{
path.remove()
}
}
});
const{ code: output } = generate(ast);
console.log(output)
4.反控制流平坦化
原代码:
const s = "3|1|2".split("|");
let x = 0;
while (true){
switch (s[x++]){
case "1":
const a = 1;
continue;
case "2":
const b = 3;
continue;
case "3":
const c = 0;
continue;
}
break;
}
import { parse } from "@babel/parser";
import traverse from "@babel/traverse";
import generate from "@babel/generator";
import * as types from "@babel/types";
import fs from "fs";
const code = fs.readFileSync("code/表达式还原.js","utf-8");
let ast = parse(code);
traverse(ast,{
//反控制流平坦化
WhileStatement(path){
const { node,scope } = path;
const { test,body } = node;
let switchNode = body.body[0];
let { discriminant,cases } = switchNode;
let { object,property } = discriminant;
let arrName = object.name;
let binding = scope.getBinding(arrName);
let { init } = binding.path.node;
object = init.callee.object;
property = init.callee.property;
let argument = init.arguments[0].value;
let arrayFlow = object.value[property.name](argument);
// console.log(object.value[property.name])
// console.log(argument)
// console.log(arrayFlow)
let resultBody = [];
arrayFlow.forEach((index)=> {
let switchCase = cases.filter((c) => c.test.value == index)[0];
let caseBody = switchCase.consequent;
if (types.isContinueStatement(caseBody[caseBody.length - 1])) {
caseBody.pop();
}
resultBody = resultBody.concat(caseBody);
});
path.replaceWithMultiple(resultBody);
},
});
const{ code: output } = generate(ast);
console.log(output)