AST 反混淆

本文搬运于GitHub:原文

依赖: 请自行安装node.js 同时安装 babel

控制流相关

while-if转变为while-switch

代码:


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode =
    `
function test() {
  var index = 0,
      arr = [3, 0, 2, 1],
      pindex;

  while (1) {
    pindex = arr[index++];

    if (pindex < 1) {
      console.log("this is index 0");
    } else if (pindex < 2) {
      console.log("this is index 1");
      return;
    } else if (pindex < 3) {
      console.log("this is index 2");
    } else {
      console.log("Hello world!");
    }
  }
}
`;

let ast = parse(jscode);

const visitor =
    {
        "WhileStatement"(path) {
            let {test, body} = path.node;

            //******************************************************特征判断开始
            if (!t.isNumericLiteral(test, {value: 1})) return;

            let block_body = body.body;

            if (block_body.length !== 2 || !t.isExpressionStatement(block_body[0]) || !t.isIfStatement(block_body[1])) {
                return;
            }
            //******************************************************特征判断结束

            let {left, right} = block_body[0].expression;
            let name = left.name;

            let if_arrs = [];
            path.traverse({
                "IfStatement"(_path) {

                    let {test, consequent, alternate} = _path.node;

                    let {left, operator, right} = test;

                    if (!t.isIdentifier(left, {name: name}) || operator !== '<' || !t.isNumericLiteral(right)) return;

                    if (consequent.body.length === 1) {
                        if_arrs[right.value - 1] = consequent.body[0];
                    } else {
                        if_arrs[right.value - 1] = consequent;
                    }

                    if (!t.isIfStatement(alternate)) {
                        if (consequent.body.length === 1) {
                            if_arrs[right.value] = alternate.body[0];
                        } else {
                            if_arrs[right.value] = alternate;
                        }
                    }
                },
            })

            if (if_arrs.length === 0) return;

            for (var i = 0; i < if_arrs.length - 1; i++) {
                consequent = [if_arrs[i], t.BreakStatement()];
                if_arrs[i] = t.SwitchCase(test = t.NumericLiteral(i), consequent = consequent);
            }

            consequent = [if_arrs[if_arrs.length - 1], t.BreakStatement()];
            if_arrs[i] = t.SwitchCase(test = null, consequent = consequent);

            let new_node = t.SwitchStatement(right, if_arrs);

            path.get('body.body.1').replaceInline(new_node);
            path.get('body.body.0').remove();
        },
    }


//some function code


traverse(ast, visitor);
let {code} = generator(ast);
console.log(code);

去控制流(while-switch)

代码:


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode =
`
var arr = "3|0|1|2|4".split("|");
var cnt = 0;

while (true) {
  switch (arr[cnt++]) {
     case "1":
      console.log("This is case-block 1");
      continue;
      
    case "0":
      console.log("This is case-block 0");
      continue;

    case "2":
      console.log("This is case-block 2");
      continue;

    case "4":
      console.log("This is case-block 4");
      continue;
      
    case "3":
      console.log("This is case-block 3");
      continue;
  }

  break;
}
`;

let ast = parse(jscode);

const visitor =
    {
        WhileStatement(path) {
            const { test, body } = path.node;

            //特征语句判断,视情况而定,也可以不判断
            if (!t.isBooleanLiteral(test) || test.value !== true) return;

            //特征语句判断,body.body[0] 必须是 SwitchStatement 节点,
            //注意一定要先判断长度,避免index出错
            if (body.body.length === 0 || !t.isSwitchStatement(body.body[0])) return;

            let switch_state = body.body[0];

            //获取discriminant及cases节点
            let { discriminant, cases } = switch_state;

            //特征语句判断,经过此判断后,基本可以确定是需要还原的while节点了。
            //如果出错了,可以继续增加判断,直到不出错即可
            if (!t.isMemberExpression(discriminant) || !t.isUpdateExpression(discriminant.property)) return;

            //获取数组名,用于查找该数组。
            let arr_name = discriminant.object.name;
            let arr = [];

            //在这里再加一个特征语句的判断:WhileStatement 节点前面有两个节点
            let all_pre_siblings = path.getAllPrevSiblings();
            if (all_pre_siblings.length !== 2) return;

            all_pre_siblings.forEach((pre_path) => {
                //虽然知道是第0个节点,但这里还是做下判断取arr
                const { declarations } = pre_path.node;
                let { id, init } = declarations[0];

                if (arr_name === id.name) {
                    //如果是定义arr的节点,拿到该arr的值
                    arr = init.callee.object.value.split("|");
                }

                //没啥用的语句可以直接删除
                pre_path.remove();
            });

            //新建一个数组变量,用于存放 case 节点
            let ret_body = [];

            arr.forEach((index) => {
                //遍历数组,去case节点
                let case_body = [];
                for (const tmp of cases){
                    if(index === tmp.test.value){
                        case_body = tmp.consequent;
                        break;
                    }
                }

                if (t.isContinueStatement(case_body[case_body.length - 1])) {
                    //删除 continue语句
                    case_body.pop();
                }

                //存放于数组变量中
                ret_body = ret_body.concat(case_body);
            });

            //替换
            path.replaceInline(ret_body);
        }
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);


/*
去控制流(while-switch), 代码不通用,存在优化空间,
比如将 case 做个键值对映射,这样直接取结果就比循环判断好一些
 */

去控制流(for-switch)

代码:

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

// let jscode = "if (true)\n" +
//     "{\n" +
//     "   //do something;\n" +
//     "  funcA();\n" +
//     "}\n" +
//     "else\n" +
//     "{\n" +
//     "   //do something;\n" +
//     "  funcB();\n" +
//     "}";

let jscode = "if ('123'==='123')\n" +
    "   a = 123;\n" +
    "else\n" +
    "  a = 456;";

let ast = parse(jscode);

const visitor1 =
    {
        BinaryExpression(path) {
            let {confident, value} = path.evaluate();
            // console.log(path.type, confident, value)
            if (confident) {
                // console.log(path.node);
                path.replaceInline(t.valueToNode(value))

            }
        }
    }

const visitor2 =
    {
        IfStatement(path) {
            let {test, consequent, alternate} = path.node;

            if (!t.isBlockStatement(consequent)) {//添加中括号
                path.node.consequent = t.BlockStatement([consequent]);
            }

            if (alternate !== null && !t.isBlockStatement(alternate)) {//添加中括号
                path.node.alternate = t.BlockStatement([alternate]);
            }

            //特征判断,if语句里面的test是否为字面量
            if (!t.isLiteral(test)) return;

            let value = test.value;
            consequent = path.node.consequent;
            alternate = path.node.alternate;

            if (value) {//替换
                path.replaceInline(consequent.body);
            } else {//替换
                alternate === null ? path.remove() : path.replaceInline(alternate.body);
            }
        },
    }

//some function code


traverse(ast, visitor1);
traverse(ast, visitor2);
let {code} = generator(ast);
console.log(code);

/*
if (true)
//do something;
else
//do something;

与
if (true)
{
    //do something;
}
else
{
    //do something;
}

解析出来的结构会有小小的差异,不利于处理,因此先将上面的代码转变为下面的代码,再进行处理即可

1.先将if语句块中没有 中括号的处理成 包含中括号的
2.判断if条件里面的值,获取应该执行的语句块
3.用语句块替换整个if表达式即可。

*/

条件表达式拆分为if语句

代码:

/*
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode =
`
r ? r > 1 ? e.apply(t, arguments) : e.call(t, n) : e.call(t);
`;

let ast = parse(jscode);

const visitor =
    {
        ConditionalExpression(path) {
            let { test, consequent, alternate } = path.node;
            const ParentPath = path.parentPath;
            if (t.isExpressionStatement(ParentPath)) {
                if (!t.isExpressionStatement(consequent)) {
                    consequent = t.BlockStatement([t.ExpressionStatement(consequent)]);
                }
                if (!t.isExpressionStatement(alternate)) {
                    alternate = t.BlockStatement([t.ExpressionStatement(alternate)]);
                }
                ParentPath.replaceInline(t.IfStatement(test, consequent, alternate));
            }
        }
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

/*

r ? r > 1 ? e.apply(t, arguments) : e.call(t, n) : e.call(t);


if (r) {
  if (r > 1) {
    e.apply(t, arguments);
  } else {
    e.call(t, n);
  }
} else {
  e.call(t);
}
 */

处理条件已知的if语句

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

// let jscode = "if (true)\n" +
//     "{\n" +
//     "   //do something;\n" +
//     "  funcA();\n" +
//     "}\n" +
//     "else\n" +
//     "{\n" +
//     "   //do something;\n" +
//     "  funcB();\n" +
//     "}";

let jscode = "if ('123'==='123')\n" +
    "   a = 123;\n" +
    "else\n" +
    "  a = 456;";

let ast = parse(jscode);

const visitor1 =
    {
        BinaryExpression(path) {
            let {confident, value} = path.evaluate();
            // console.log(path.type, confident, value)
            if (confident) {
                // console.log(path.node);
                path.replaceInline(t.valueToNode(value))

            }
        }
    }

const visitor2 =
    {
        IfStatement(path) {
            let {test, consequent, alternate} = path.node;

            if (!t.isBlockStatement(consequent)) {//添加中括号
                path.node.consequent = t.BlockStatement([consequent]);
            }

            if (alternate !== null && !t.isBlockStatement(alternate)) {//添加中括号
                path.node.alternate = t.BlockStatement([alternate]);
            }

            //特征判断,if语句里面的test是否为字面量
            if (!t.isLiteral(test)) return;

            let value = test.value;
            consequent = path.node.consequent;
            alternate = path.node.alternate;

            if (value) {//替换
                path.replaceInline(consequent.body);
            } else {//替换
                alternate === null ? path.remove() : path.replaceInline(alternate.body);
            }
        },
    }

//some function code


traverse(ast, visitor1);
traverse(ast, visitor2);
let {code} = generator(ast);
console.log(code);

/*
if (true)
//do something;
else
//do something;

与
if (true)
{
    //do something;
}
else
{
    //do something;
}

解析出来的结构会有小小的差异,不利于处理,因此先将上面的代码转变为下面的代码,再进行处理即可

1.先将if语句块中没有 中括号的处理成 包含中括号的
2.判断if条件里面的值,获取应该执行的语句块
3.用语句块替换整个if表达式即可。

*/

删除相关

删除多余的空行和空语句


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var a = 123;\n" +
    "\n" +
    ";\n" +
    "\n" +
    "var b = 456;";

let ast = parse(jscode);

const visitor =
    {
        EmptyStatement(path)
        {
            path.remove();
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

删除所有的代码注释



const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var a = 123; //this is single line comment\n" +
    "\n" +
    "/*\n" +
    "\n" +
    "This is a  multiline comments;\n" +
    "\n" +
    "test\n" +
    "\n" +
    "test\n" +
    "\n" +
    "test\n" +
    "\n" +
    "*/\n" +
    "\n" +
    "var b = 456;";

let ast = parse(jscode);

const visitor =
    {
        //TODO  write your code here!
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast, {comments:false});
console.log(code);

删除未被使用的变量


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var a = 123,b;\n" +
    "\n" +
    "let c = 4 + 5;\n" +
    "\n" +
    "d = a + 12;";

let ast = parse(jscode);

const visitor =
    {
        VariableDeclarator(path) {

            const {id} = path.node;
            const binding = path.scope.getBinding(id.name);

            //如果变量被修改过,则不能进行删除动作。
            if (!binding || binding.constantViolations.length > 0) {
                return;
            }

            //长度为0,说明变量没有被使用过。
            if (binding.referencePaths.length === 0) {
                path.remove();
            }
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

删除未被调用的函数

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "function i()\n" +
    "\n" +
    "{\n" +
    "\n" +
    "    var i = 123;\n" +
    "\n" +
    "    i += 2;\n" +
    "\n" +
    "    \n" +
    "\n" +
    "  return 123;\n" +
    "\n" +
    "}";

let ast = parse(jscode);

const visitor =
    {
        FunctionDeclaration(path) {
            // path.scope.dump();

            const {id} = path.node;
            const binding = path.scope.parent.getBinding(id.name);

            if (!binding || binding.constantViolations.length > 0) {
                return;
            }

            if (binding.referencePaths.length === 0) {
                path.remove();
            }
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

还原相关

还原Array对象

/*
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var _2$SS = function (_SSz, _1111) {\n" +
    "    var _l1L1 = [46222, '\x74\x61\x43\x61\x70\x74\x63\x68\x61\x42\x6c\x6f\x62', '\x74', '\x61', '\x73', '\x6c', '\x64', '\x69', .3834417654519915, '\x65\x6e\x63\x72\x79\x70\x74\x4a', '\x73\x6f', '\x6e', 49344];\n" +
    "\n" +
    "    var _2Szs = _l1L1[5] + _l1L1[7] + (_l1L1[4] + _l1L1[2]),\n" +
    "        _I1il1 = _l1L1[9] + (_l1L1[10] + _l1L1[11]);\n" +
    "\n" +
    "    var _0ooQoO = _l1L1[0];\n" +
    "    var _$Z22 = _l1L1[12],\n" +
    "        _2sS2 = _l1L1[8];\n" +
    "    return _l1L1[6] + _l1L1[3] + _l1L1[1];\n" +
    "};";

let ast = parse(jscode);

const visitor =
    {
        VariableDeclarator(path){
            // 还原数组对象
            const {id, init} = path.node;

            // 非Array或者没有元素, 返回
            if (!t.isArrayExpression(init) || init.elements.length===0) return;

            let elements = init.elements;

            // 获取binding实例
            const binding = path.scope.getBinding(id.name);

            for ( const ref_path of binding.referencePaths){
                // 获取 MemberExpression 父节点
                let member_path = ref_path.findParent(p=>p.isMemberExpression());
                let property = member_path.get('property');

                // 索引值不是 NumericLiteral 类型的不处理
                if(!property.isNumericLiteral()){
                    continue;
                }

                // 获取索引值
                let index = property.node.value;

                // 获取索引值对应的节点, 并替换
                let arr_ele = elements[index];
                member_path.replaceWith(arr_ele)
            }
        }
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

还原object对象

/*
date    : 2020/8/11
desc    : 
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var a = {\n" +
    "  \"YJJox\": \"object\",\n" +
    "  \"sbTga\": function (b, c) {\n" +
    "    return b | c;\n" +
    "  },\n" +
    "  \"iwvEK\": function (b, c) {\n" +
    "    return b << c;\n" +
    "  },\n" +
    "  \"HqkiD\": function (b, c) {\n" +
    "    return b(c);\n" +
    "  }\n" +
    "};\n" +
    "b = a[\"iwvEK\"](1, 3), c = a[\"sbTga\"](111, 222), d = a[\"YJJox\"], e = a[\"HqkiD\"](String.fromCharCode, 49);";

let ast = parse(jscode);

const visitor =
    {
        VariableDeclarator(path) {
            const {id, init} = path.node;

            //特征判断,对象为空则不处理
            if (!t.isObjectExpression(init) || init.properties.length === 0) return;

            let name = id.name;
            let scope = path.scope;

            for (const property of init.properties) {//遍历key、value
                let key = property.key.value;
                let value = property.value;

                //一般ob混淆,key长度都是5,也有是3的,自行调整即可。
                if (key.length !== 5) return;

                //如果是字面量
                if (t.isLiteral(value)) {
                    scope.traverse(scope.block, {
                        //遍历MemberExpression,找出与key相同的表达式
                        MemberExpression(_path) {
                            let _node = _path.node;
                            if (!t.isIdentifier(_node.object, {name: name})) return;
                            if (!t.isLiteral(_node.property, {value: key})) return;
                            _path.replaceWith(value);
                        },
                    })
                }
                //如果是函数表达式
                else if (t.isFunctionExpression(value)) {
                    let ret_state = value.body.body[0];

                    //特征判断,如果不是return表达式
                    if (!t.isReturnStatement(ret_state)) continue;

                    scope.traverse(scope.block, {
                        CallExpression: function (_path) {

                            //遍历CallExpression
                            let {callee, arguments} = _path.node;
                            if (!t.isMemberExpression(callee)) return;
                            if (!t.isIdentifier(callee.object, {name: name})) return;
                            if (!t.isLiteral(callee.property, {value: key})) return;
                            if (t.isCallExpression(ret_state.argument) && arguments.length > 0) {

                                //构造节点
                                _path.replaceWith(t.CallExpression(arguments[0], arguments.slice(1)));
                            } else if (t.isBinaryExpression(ret_state.argument) && arguments.length === 2) {

                                //构造节点
                                let replace_node = t.BinaryExpression(ret_state.argument.operator, arguments[0], arguments[1]);
                                _path.replaceWith(replace_node);
                            } else if (t.isLogicalExpression(ret_state.argument) && arguments.length === 2) {

                                //构造节点
                                let replace_node = t.LogicalExpression(ret_state.argument.operator, arguments[0], arguments[1]);
                                _path.replaceWith(replace_node);
                            }
                        }
                    })
                }
            }
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

/*
1.这里是 VariableDeclarator 节点,子节点 init 是 ObjectExpression 类型的表达式,因此我们可以遍历VariableDeclarator节点,便于获取 对象名 及整个对象
2.遍历 ObjectExpression 节点的 properties 属性,它是一个 数组,遍历这个数组,获取key和value
3.判断 value 的节点类型,如果是字面量,则可以直接进行替换;如果是函数表达式,则需要通过返回的表达式类型构造相应的表达式。然后在作用域块内遍历 MemberExpression (PS:a["sbTga"]) 节点,如果是对象名,并且当前的key值也相等,则进行节点替换。
4.遍历 properties 完毕后,可以试着删除整个  VariableDeclarator 节点,如果不报错就没事。
*/

还原定义的字面量

/*
date    : 2020/8/11
desc    : 
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var s = 92;\n" +
    "b = Z(1324801, 92);";

let ast = parse(jscode);

const visitor =
    {
        "Identifier"(path)
        {
            const {confident,value} = path.evaluate();
            confident && path.replaceInline(t.valueToNode(value));
        },

        // 替换完了就没用了,将其删除
        VariableDeclarator(path)
        {
            const {id,init} = path.node;

            if (!t.isLiteral(init)) return;//只处理字面量

            const binding = path.scope.getBinding(id.name);

            if (!binding || binding.constantViolations.length > 0)
            {//如果该变量的值被修改则不能处理
                return;
            }

            for (const refer_path of binding.referencePaths)
            {
                refer_path.replaceWith(init);
            }
            path.remove();
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

还原成中文字符

/*
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var s = \"\u4f60\u597d\uff0c\u4e16\u754c;\"\n" +
    "var a = \"\u0068\u0065\u006c\u006c\u006f\u002c\u0020\u0077\u006f\u0072\u0064\";";

let ast = parse(jscode);

const visitor =
    {
        StringLiteral(path)
        {
            path.get('extra').remove();
        },
    }

//some function code


traverse(ast,visitor);
let {code} = generator(ast, {jsescOption:{"minimal":true}});
console.log(code);

还原自执行函数的实参

/*
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "!function (a, b) {\n" +
    "  c = a | b;\n" +
    "}(111, 222);";

let ast = parse(jscode);

const visitor =
    {
        CallExpression(path){
            let callee = path.get('callee');
            let arguments = path.get('arguments');

            if(!t.isFunctionExpression(callee) || arguments.length ===0){
                // 实参的长度判断可以写死
                return;
            }

            // 获取形参
            let params = callee.get('params');
            let scope = callee.scope;

            for ( let i =0; i< arguments.length; i++){
                // 遍历实参, 因为形参可能比实参长
                let arg = params[i];
                let {name} = arg.node;

                const binding = scope.getBinding(name);

                if(!binding || binding.constantViolations.length > 0){
                    // 形参发生改变,不能被还原
                    continue;
                }

                for(refer_path of binding.referencePaths){
                    // 字面量可以直接替换
                    refer_path.replaceWith(arguments[i]);
                }

                arg.remove();
                arguments[i].remove();
            }
        }
    }

//some function code

traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

// 替换完之后可以通过无参的自执行插件再替换

合并定义在object对象外面的key、value


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode =
`
var h = {};
h["aaa"] = "hello wolrd";
h["bbb"] = function (a,b)
{
   return a | b;
}
`;

let ast = parse(jscode);

const visitor =
    {
        VariableDeclarator(path)
        {
            const {id,init} = path.node;
            if (!t.isObjectExpression(init)) return;
            let name = id.name;
            let properties = init.properties;
            let all_next_siblings = path.parentPath.getAllNextSiblings();

            for (let next_sibling of all_next_siblings)
            {
                if (!next_sibling.isExpressionStatement())  break;

                let expression = next_sibling.get('expression');
                if (!expression.isAssignmentExpression()) break;
                let {operator,left,right} = expression.node;

                if (operator !== '=' || !t.isMemberExpression(left) || !t.isIdentifier(left.object,{name:name}))
                {
                    break;
                }
                properties.push(t.ObjectProperty(left.property,right));
                next_sibling.remove();
            }
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);


/*

1.object对象使用var定义的,因此遍历 VariableDeclarator 节点即可
2.依次判断后续节点,是否为定义在外面的key和value
3.收集key和value,用于构造ObjectProperty 节点
4. properties  属性是Array对象,只用push方法来增加节点
5.处理完成后删除后续节点。

 */

表达式相关

逗号表达式

/*
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode =
`
b = (0,g.o)(a);

c = (a=1,b=2,c=3,d=4,e=5,f);

function get()
{
	return a=1,b=2,a +=2,a;
}
`;

let ast = parse(jscode);

const visitor =
    {
        SequenceExpression: {
		    exit(path){
                let expressions = path.get('expressions');
                let last_expression = expressions.pop();
                
                let statement = path.getStatementParent();
                
                if(statement){
                    for(let expression of expressions)
                    {
                        // 删除无用的干扰代码
                        if(expression.isLiteral() ||expression.isIdentifier())
                        {
                            expression.remove();
                            continue;
                        }
                        statement.insertBefore(types.ExpressionStatement(expression=expression.node));
                    }
				    path.replaceInline(last_expression);
                }
            }
        }
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);


/*******************************************
b = (0,g.o)(a);

c = (a=1,b=2,c=3,d=4,e=5,f);

function get()
{
	return a=1,b=2,a +=2,a;
}

===>

b = g.o(a);
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
c = f;

function get() {
  a = 1;
  b = 2;
  a += 2;
  return a;
}

******************************************/

三目运算符

/*
desc    : 把 a = m?11:22; 转成 m ? a = 11 : a = 22;
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "a = m ? 11 : 22;";

let ast = parse(jscode);

const visitor =
    {
        ConditionalExpression(path){
            let {test, consequent, alternate} = path.node;
            const ParentPath = path.parentPath;
            if(t.isAssignmentExpression(ParentPath)){
                let {operator, left} = ParentPath.node;
                if (operator === '='){
                    consequent = t.AssignmentExpression('=', left, consequent);
                    alternate = t.AssignmentExpression('=', left, alternate);
                    ParentPath.replaceWith(t.ConditionalExpression(test, consequent, alternate))
                }
            }
        }
    }

//some function code

traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

/*

1.节点类型变了,由 AssignmentExpression 类型变成了ConditionalExpression 类型。
2.ConditionalExpression 子节点的 consequent 和 alternate 都变成了 AssignmentExpression 类型。

*/

处理条件已知的三元表达式

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "a = 5 ? 11 : 22;\n" +
    "b = 0 ? 33 : 44;";

let ast = parse(jscode);

const visitor =
    {
        ConditionalExpression(path) {
            let { test, consequent, alternate } = path.node;

            if (!t.isLiteral(test)) return;

            if (test.value) {
                path.replaceInline(consequent);
            } else {
                path.replaceInline(alternate);
            }
        }
    }

//some function code

traverse(ast, visitor);
let {code} = generator(ast);
console.log(code);

// 和常见表达式的计算类似

节点

对同一节点使用多个方法


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "";

let ast = parse(jscode);

const visitor =
    {
        CallExpression:
            {// 注意顺序
                enter: [reduce_call_express, delete_empty_params]
            },
    }


//some function code

function reduce_call_express(path) {
}

function delete_empty_params(path) {
}

traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

/*

假设想对一个函数表达式的 CallExpression 节点进行进行实参还原,
如果还原后,没有了实参和形参,假如又可以将函数体里面的代码提取出来。
这个时候就需要两个方法来分别进行处理了

reduce_call_express     还原函数的实参
delete_empty_params     将参数为空的函数进行处理,将函数体里面的代码直接提取出来

*/

构造节点

/*
* 将 var a; 转换为 var a = 123 + 456;
*
* */

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var a";

let ast = parse(jscode);

const visitor =
    {
        // 方法一
        VariableDeclarator(path){
            const {init} = path.node;
            let node = {
                type: "BinaryExpression",
                operator: "+",
                left: {
                    type: "NumericLiteral",
                    value: 123,
                },
                right: {
                    type: "NumericLiteral",
                    value: 456,
                }
            }

            init || path.set("init", node)
        },

        // 方法二
/*        VariableDeclarator(path){
            const {init} = path.node;
            init || path.set("init", t.binaryExpression('+',t.valueToNode(123),t.valueToNode(456)))

        },*/
    }



//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

节点类型转换

/*
desc    : 将 BinaryExpression 类型转换为 CallExpression 类型
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "var a = 123 | 456;";

let ast = parse(jscode);

const visitor =
    {
        "VariableDeclarator"(path)
        {
            const init_path = path.get('init');
            if (!init_path.isBinaryExpression()) return

            init_path.node.type = "CallExpression";
            let {operator,left,right} = init_path.node;
            init_path.node.arguments = [left,right];

            let id = null;
            let frist_arg  = t.Identifier('s');
            let second_arg = t.Identifier('h');
            let params = [frist_arg,second_arg];

            let args = t.BinaryExpression(operator,frist_arg,second_arg);
            let return_state = t.ReturnStatement(args);

            let body = t.BlockStatement([return_state]);
            init_path.node.callee = t.FunctionExpression(id ,params,body);
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

函数调用处自动替换计算值

/*
*/

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "function add(a, b) {\n" +
    "  return a + b;\n" +
    "}\n" +
    "\n" +
    "let c = add(1, 2);";

let ast = parse(jscode);

const visitor =
    {
        FunctionDeclaration(path) {
            let {id} = path.node;
            let code = path.toString();
            if (code.indexOf("try") !== -1 || code.indexOf("random") !== -1 || code.indexOf("Date") !== -1) {
                // 不是纯函数,不处理
                return
            }

            eval(code);

            let scope = path.scope;
            const binding = path.scope.parent.getBinding(id.name);

            if (!binding || binding.constantViolations.length > 0) {
                return
            }

            for (const refer_path of binding.referencePaths) {
                // 查找父节点
                let call_express = refer_path.findParent(p => p.isCallExpression());

                let arguments = call_express.get("arguments");
                let args = [];

                // 判断参数是否为 Literal 类型
                arguments.forEach(arg => {
                    args.push(arg.isLiteral())
                })

                // 自行编写判断条件,example
                if (args.length === 0 || args.indexOf(false) !== -1) {
                    continue
                }

                try {
                    // 计算值
                    let value = eval(call_express.toString());
                    value && call_express.replaceWith(t.valueToNode(value));
                } catch (e) {

                }
            }

        }
    }


//some function code


traverse(ast, visitor);
let {code} = generator(ast);
console.log(code);


/*
1.函数需要满足在任意位置都能执行。就是一个函数声明的代码,随便拷贝到任意的地方都能直接运行,不会报错。说白了其实也就是函数体内的所有变量或者对象,其作用域只在函数体里面。
2.定义的函数,对于实参是固定的,其结果也是固定的。请大家自行百度 纯函数 的概念
3.实参必须是字面量,因为对于遍历的节点来说。只有是字面量,才能给你计算出具体的字,如果不是字面量,实参对它来说就是 undefined 的,是无法计算的。
4.将函数定义eval到本地环境,然后根据作用域找到函数调用的位置。再eval该表达式即可。注意需要使用try...catch语句,避免错误。
*/

处理eval函数


const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "eval('var a = 123;');";

let ast = parse(jscode);

const visitor =
    {
        CallExpression(path)
        {
            let {callee,arguments} = path.node;
            if (!t.isIdentifier(callee,{name:'eval'})) return;
            if (arguments.length !== 1 || !t.isStringLiteral(arguments[0])) return;
            let value = arguments[0].value;
            path.replaceWith(t.Identifier(value));
        },
    }


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);

处理没有实参的自执行函数

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;

let jscode = "!function () {\n" +
    "  a = 123;\n" +
    "}();";

let ast = parse(jscode);

const visitor = {

    UnaryExpression(path) {
        const {operator, argument} = path.node;

        if (operator !== "!" || !t.isCallExpression(argument)) return;

        let {callee, arguments} = argument;

        if (!t.isFunctionExpression(callee) || arguments.length !== 0) return;

        path.replaceInline(callee.body.body);

    },

}


//some function code


traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值