基于babel的AST操作(js反混淆基础)

基于babel的AST操作(js反混淆基础)

文章目录

      • 一、babel介绍
            • babel工作流
      • 二、环境搭建
          • 1、npm项目创建
          • 2、添加babel依赖
      • 三、模块应用介绍
          • 1、**@babel/parser**
          • 2、**@babel/traverse**
          • 3、**@babel/generator**
          • 4、**@babel/types**
      • 四、babel实践(js混淆还原)
          • 1、javascript(创建jscode.js)
          • 2、AST抽象语法树
          • 3、还原(index.js)
          • 5、结果(decode.js)

一、babel介绍

Babel 是一个通用的多功能的 JavaScript 编译器。更确切地说是源码到源码的编译器,通常也叫做“转换编译器(transpiler)”。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。此外它还拥有众多模块可用于不同形式的静态分析。

babel工作流

Babel 的工作流可以用下面一张图来表示,代码首先经由 babylon 解析成抽象语法树(AST),后经一些遍历和分析转换(主要过程),最后根据转换后的 AST 生成新的常规代码。

二、环境搭建

1、npm项目创建
~ npm init

package name: (ast) ast_zhangxiaofan
version: (1.0.0)
description: test
entry point: (test.js) index.js
test command:
git repository:
keywords: abc
author: zhangxiaofan
license: (ISC) Apache-2.0
About to write to /Users/**/**/**/ast/package.json:

{
  "name": "ast_zhangxiaofan",
  "version": "1.0.0",
  "description": "test",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "abc"
  ],
  "author": "zhangxiaofan",
  "license": "Apache-2.0"
}


Is this OK? (yes) yes
2、添加babel依赖
# babel遍历器
npm install @babel/traverse --save

# code解析为ast
npm install @babel/parser --save

# ast操作相关工具类
npm install @babel/types --save

# ast还原为代码
npm install @babel/generator --save

三、模块应用介绍

1、@babel/parser

Babel parser(以前是Babylon)是Babel中使用的JavaScript解析器。通过该模块来解析我们的代码生成AST抽象语法树

// ***** API *******
babelParser.parse(code, [options])
babelParser.parseExpression(code, [options])
//parse()将提供的内容解析code为整个ECMAScript程序,同时 parseExpression()尝试在考虑性能的情况下解析单个Expression。如有疑问,请使用.parse()

案例:

const code = `function square(n) {
  return n * n;
}`;

let ast = require("@babel/parser").parse(code)

console.log(ast)
2、@babel/traverse

可以用来遍历更新@babel/parser生成的AST

案例:替换code中n 为 x

const {parse} = require("@babel/parser");

const traverse = require("@babel/traverse").default;

const code = `function square(n) {
  return n * n;
}`;

const ast = parse(code);

traverse(ast, {
    enter(path) {
        if (path.isIdentifier({ name: "n" })) {
            path.node.name = "x";
        }
    }
});
3、@babel/generator

生成代码

案例:

const fs = require('fs');

const {parse} = require("@babel/parser");

const traverse = require("@babel/traverse").default;
//引入模块
const generator = require("@babel/generator").default;

const code = `function square(n) {
  return n * n;
}`;

const ast = parse(code);

traverse(ast, {
    enter(path) {
        if (path.isIdentifier({ name: "n" })) {
            path.node.name = "x";
        }
    }
});
// AST --> 代码
const output = generator(ast)["code"]

console.log(output)
4、@babel/types

工具类,主要用途是在创建AST的过程中判断各种语法的类型

// ******* API *******
太多了不一一介绍了 详情见 
https://babel.docschina.org/docs/en/babel-types

案例:

const {parse} = require("@babel/parser");

const traverse = require("@babel/traverse").default;

const t = require("@babel/types");

const generator = require("@babel/generator").default;

const code = `function square(n) {
  return n * n;
}`;

const ast = parse(code);

traverse(ast, {
    enter(path) {
        if (t.isIdentifier(path.node, { name: "n" })) {
            path.node.name = "x";
        }
    }
});

四、babel实践(js混淆还原)

1、javascript(创建jscode.js)
var a = "this " + decodeURIComponent("encoded%20data");

console.log(a);

// this encoded data
2、AST抽象语法树
{
  "type": "Program",
  "start": 0,
  "end": 481,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 179,
      "end": 394,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 183,
          "end": 393,
          "id": {
            "type": "Identifier",
            "start": 183,
            "end": 187,
            "name": "tips"
          },
          "init": {
            "type": "ArrayExpression",
            "start": 190,
            "end": 393,
            "elements": [
              {
                "type": "Literal",
                "start": 194,
                "end": 241,
                "value": "Click on any AST node with a '+' to expand it",
                "raw": "\"Click on any AST node with a '+' to expand it\""
              },
              {
                "type": "Literal",
                "start": 246,
                "end": 330,
                "value": "Hovering over a node highlights the    corresponding location in the source code",
                "raw": "\"Hovering over a node highlights the \\\n   corresponding location in the source code\""
              },
              {
                "type": "Literal",
                "start": 335,
                "end": 391,
                "value": "Shift click on an AST node to expand the whole subtree",
                "raw": "\"Shift click on an AST node to expand the whole subtree\""
              }
            ]
          }
        }
      ],
      "kind": "let"
    },
    {
      "type": "FunctionDeclaration",
      "start": 396,
      "end": 480,
      "id": {
        "type": "Identifier",
        "start": 405,
        "end": 414,
        "name": "printTips"
      },
      "expression": false,
      "generator": false,
      "async": false,
      "params": [],
      "body": {
        "type": "BlockStatement",
        "start": 417,
        "end": 480,
        "body": [
          {
            "type": "ExpressionStatement",
            "start": 421,
            "end": 478,
            "expression": {
              "type": "CallExpression",
              "start": 421,
              "end": 477,
              "callee": {
                "type": "MemberExpression",
                "start": 421,
                "end": 433,
                "object": {
                  "type": "Identifier",
                  "start": 421,
                  "end": 425,
                  "name": "tips"
                },
                "property": {
                  "type": "Identifier",
                  "start": 426,
                  "end": 433,
                  "name": "forEach"
                },
                "computed": false,
                "optional": false
              },
              "arguments": [
                {
                  "type": "ArrowFunctionExpression",
                  "start": 434,
                  "end": 476,
                  "id": null,
                  "expression": true,
                  "generator": false,
                  "async": false,
                  "params": [
                    {
                      "type": "Identifier",
                      "start": 435,
                      "end": 438,
                      "name": "tip"
                    },
                    {
                      "type": "Identifier",
                      "start": 440,
                      "end": 441,
                      "name": "i"
                    }
                  ],
                  "body": {
                    "type": "CallExpression",
                    "start": 446,
                    "end": 476,
                    "callee": {
                      "type": "MemberExpression",
                      "start": 446,
                      "end": 457,
                      "object": {
                        "type": "Identifier",
                        "start": 446,
                        "end": 453,
                        "name": "console"
                      },
                      "property": {
                        "type": "Identifier",
                        "start": 454,
                        "end": 457,
                        "name": "log"
                      },
                      "computed": false,
                      "optional": false
                    },
                    "arguments": [
                      {
                        "type": "BinaryExpression",
                        "start": 458,
                        "end": 475,
                        "left": {
                          "type": "TemplateLiteral",
                          "start": 458,
                          "end": 469,
                          "expressions": [
                            {
                              "type": "Identifier",
                              "start": 465,
                              "end": 466,
                              "name": "i"
                            }
                          ],
                          "quasis": [
                            {
                              "type": "TemplateElement",
                              "start": 459,
                              "end": 463,
                              "value": {
                                "raw": "Tip ",
                                "cooked": "Tip "
                              },
                              "tail": false
                            },
                            {
                              "type": "TemplateElement",
                              "start": 467,
                              "end": 468,
                              "value": {
                                "raw": ":",
                                "cooked": ":"
                              },
                              "tail": true
                            }
                          ]
                        },
                        "operator": "+",
                        "right": {
                          "type": "Identifier",
                          "start": 472,
                          "end": 475,
                          "name": "tip"
                        }
                      }
                    ],
                    "optional": false
                  }
                }
              ],
              "optional": false
            }
          }
        ]
      }
    }
  ],
  "sourceType": "module"
}
3、还原(index.js)
// 引入fs nodejs模块——fs模块:fs模块用于对系统文件及目录进行读写操作。
const fs = require('fs');
// 引入babel parser模块
const {parse} = require("@babel/parser");
// 引入babel travderse模块
const traverse = require("@babel/traverse").default;
// 引入babel types模块
const t = require("@babel/types");
// 引入babel generator模块
const generator = require("@babel/generator").default;
// 读取jscode.js文件内容
let jscode = fs.readFileSync("./jscode.js", {
    encoding: "utf-8"
});
// 将jscode内容转换成AST抽象语法树
let ast = parse(jscode);

const visitor =
    {

        CallExpression(path) {
            if (!t.isIdentifier(path.node.callee)
                || path.node.callee.name !== 'decodeURIComponent'
            ) {
                // 需要是调用 decodeURIComponent函数
                return;
            }
            if (path.node.arguments.length !== 1) {
                return;
            }
            let decodeArg = path.node.arguments[0];
            if (!t.isLiteral(decodeArg)) {
                //参数需要是常量
                return;
            }
            let replace = eval(path.toString());
            path.replaceWith(t.stringLiteral(replace));

            //触发父节点的树重新遍历
            path.parentPath.visit();
        },
        BinaryExpression(path) {
            if (!t.isLiteral(path.node.left)
                || !t.isLiteral(path.node.right)
            ) {
                return;
            }
            let replace = eval(path.toString());
            path.replaceWith(t.stringLiteral(replace));
        }

    };

traverse(ast, visitor);

// AST --> 代码
let {code} = generator(ast);

fs.writeFile('decode.js', code, (err) => {
});

5、结果(decode.js)
var a = "this encoded data";
console.log(a);







坐得住板凳,耐得住寂寞,守得住初心!

你可能感兴趣的:(js逆向,javascript,js,nodejs)