AST代码生成

AST代码生成

基础概念

一段代码被执行,首先会被分为一段一段的词法单元。然后会进行语法分析,最后在生成对应的真正可执行的指令。

const name = 'Germey'

语法分析为

const
name
=
Germey

语法分析生成为

{
  "type": "Program",
  "start": 0,
  "end": 21,
  "loc": {
    "start": {
      "line": 1,
      "column": 0
    },
    "end": {
      "line": 1,
      "column": 21
    }
  },
  "range": [
    0,
    21
  ],
  "comments": [],
  "sourceType": "module",
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 21,
      "loc": {
        "start": {
          "line": 1,
          "column": 0
        },
        "end": {
          "line": 1,
          "column": 21
        }
      },
      "range": [
        0,
        21
      ],
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 6,
          "end": 21,
          "loc": {
            "start": {
              "line": 1,
              "column": 6
            },
            "end": {
              "line": 1,
              "column": 21
            }
          },
          "range": [
            6,
            21
          ],
          "id": {
            "type": "Identifier",
            "start": 6,
            "end": 10,
            "loc": {
              "start": {
                "line": 1,
                "column": 6
              },
              "end": {
                "line": 1,
                "column": 10
              }
            },
            "range": [
              6,
              10
            ],
            "name": "name"
          },
          "init": {
            "type": "Literal",
            "start": 13,
            "end": 21,
            "loc": {
              "start": {
                "line": 1,
                "column": 13
              },
              "end": {
                "line": 1,
                "column": 21
              }
            },
            "range": [
              13,
              21
            ],
            "value": "Germey",
            "raw": "'Germey'"
          }
        }
      ],
      "kind": "const"
    }
  ]
}

一个js代码被混淆后,改变的只是他的代码呈现。但是他的语法树是不会有太大的一个变换的。那么我们就可以通过语法树去反推出一个易读的代码。

混淆与其反混淆手法

这里我们使用的是@babe工具,来处理AST语法树

babe库

详细使用https://evilrecluse.top/Babel-traverse-api-doc/#

根据官网介绍,它是一个JavaScript 编译器,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

  1. @babel/core:Babel 编译器本身,提供了 babel 的编译 API;
  2. @babel/parser:将 JavaScript 代码解析成 AST 语法树;
  3. @babel/traverse:遍历、修改 AST 语法树的各个节点;
  4. @babel/generator:将 AST 还原成 JavaScript 代码;
  5. @babel/types:判断、验证节点的类型、构建新 AST 节点等。
npm install @babel/core --save-dev
节点属性
通用属性
  • type: 节点的类型(如 "Identifier""Literal" 等)。
  • start: 节点在源代码中的起始位置(索引)。
  • end: 节点在源代码中的结束位置(索引)。
  • loc: 包含节点位置信息的对象,包括 startend,每个都有 linecolumn 属性。
特定属性
表示标识符(变量名、函数名等)。
{
  type: "Identifier",
  name: "x"
}
表示字面量(字符串、数字、布尔值等)
{
  type: "Literal",
  value: "Hello, World!",
  raw: "\"Hello, World!\""
}
表示函数声明
{
  type: "FunctionDeclaration",
  id: {
    type: "Identifier",
    name: "myFunction"
  },
  params: [
    {
      type: "Identifier",
      name: "a"
    },
    {
      type: "Identifier",
      name: "b"
    }
  ],
  body: {
    type: "BlockStatement",
    body: [
      // 函数体中的语句
    ]
  }
}

{
  表示变量声明。
  type: "VariableDeclaration",
  declarations: [
    {
      type: "VariableDeclarator",
      id: {
        type: "Identifier",
        name: "x"
      },
      init: {
        type: "Literal",
        value: 10,
        raw: "10"
      }
    }
  ],
  kind: "let" // 或 "const", "var"
}

二元表达式(如 `a + b`)
{
  type: "BinaryExpression",
  operator: "+",
  left: {
    type: "Identifier",
    name: "a"
  },
  right: {
    type: "Identifier",
    name: "b"
  }
}

表达式语句
{
  type: "ExpressionStatement",
  expression: {
    type: "CallExpression",
    callee: {
      type: "Identifier",
      name: "console.log"
    },
    arguments: [
      {
        type: "Literal",
        value: "Hello, World!",
        raw: "\"Hello, World!\""
      }
    ]
  }
}

更多的可以参考官方文档,或者问gpt(官方文档阅读性不太好)

generator与parser(代码与ATS的相互转化)
const code = `var a = !![];  
              var b = "abc" == "bcd";              var c = (1 << 3) | 2;              var d = parseInt("5" + "0");`;  
  
let ast = parse(code);  
let ast = parse(jscode);
traverse(节点操作)
节点对象
  • path 对象用于表示 AST 中的一个具体节点,并提供了许多操作方法和上下文信息,使得开发者能够方便地对节点进行访问、修改和操作。
  • node 对象是 AST 中一个具体节点的表示,包含了节点的类型、属性和值等信息,用于直接访问和修改节点的内容。
  • scope 对象用于描述变量和函数的作用域信息,提供了查询和操作作用域中变量和函数的方法。
  • hub 对象在 Babel 插件中提供了一些全局性的工具和信息,通常在一些上下文中使用,如获取文件信息或特定路径的作用域。
  • state 对象在 Babel 插件中用于传递和存储插件的全局状态或信息,允许在遍历过程中共享和修改这些信息。
  • opts 对象在 Babel 插件中用于接收插件的配置选项,允许插件根据配置选项的不同执行不同的操作或行为。
属性访问

一般是使用.的方式来访问对应的值

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
  
  
var jscode = `  
function f(){  
    var b = 123;    a = ['a', 'b'];}`;  
  
const visitor = {  
    BlockStatement(path)  
    {  
        console.log('当前路径 源码:\n', path.toString());  
        console.log('当前路径 节点:\n', path.node)  
        console.log('当前路径 父级节点:\n', path.parent);  
        console.log('当前路径 父级路径:\n', path.parentPath)  
        console.log('当前路径 类型:\n', path.type)  
        console.log('当前路径 contexts:\n', path.contexts);  
        console.log('当前路径 hub:\n', path.hub);  
        console.log('当前路径 state:\n', path.state);  
        console.log('当前路径 opts:\n', path.opts)  
        console.log('当前路径 skipKeys:\n', path.skipKeys)  
        console.log('当前路径 container:\n', path.container)  
        console.log('当前路径 key:\n', path.key)  
        console.log('当前路径 scope:\n', path.scope)  
    }  
}  
  
let ast = parse(jscode);  
traverse(ast, visitor);
方法使用

一般是通过get方法去获取对象调用方法

console.log(path.get("test").evaluate());

详细参考https://evilrecluse.top/Babel-traverse-api-doc/#/?id=nodepath-%e5%b1%9e%e6%80%a7

实际案例

说了这么多,可能你还是会说,我丢还是不太会啊,我们还是来几个实际案例来看看。

表达式还原
var a = !![];  
var b = "abc" == "bcd";              
var c = (1 << 3) | 2;              
var d = parseInt("5" + "0");

我们首先要获取到赋值语句的节点。我们问问gpt,或直接查看官方文档可以知道,他们为这4类

UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression"

对应代码就为

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import * as types from "@babel/types";  
  
const code = `var a = !![];  
              var b = "abc" == "bcd";              var c = (1 << 3) | 2;              var d = parseInt("5" + "0");`;  
  
let ast = parse(code);  
  
traverse(ast, {  
    "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": (  
        path  
    ) => {  
        console.log(path.node);  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

然后我们要获取到对应的一个值,这里我们使用对应的一个方法

 path.evaluate()
import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import * as types from "@babel/types";  
  
const code = `var a = !![];  
              var b = "abc" == "bcd";              var c = (1 << 3) | 2;              var d = parseInt("5" + "0");`;  
  
let ast = parse(code);  
  
traverse(ast, {  
    "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": (  
        path  
    ) => {  
        console.log(path.evaluate());  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

我们有了值,就要对代码进行替换。我们这里使用types.valueToNode方法,他可以把值替换为ast节点。

import _generate from "@babel/generator";  
const generate = _generate.default;  
import * as types from "@babel/types";  
  
const code = `var a = !![];  
              var b = "abc" == "bcd";              var c = (1 << 3) | 2;              var d = parseInt("5" + "0");`;  
  
let ast = parse(code);  
  
traverse(ast, {  
    "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": (  
        path  
    ) => {  
        console.log(types.valueToNode(path.evaluate()));  
  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

最后判断下值是否正确,并使用path.replaceWith方法进行替换即可。

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import * as types from "@babel/types";  
  
  
const code = `var a = !![];  
              var b = "abc" == "bcd";              var c = (1 << 3) | 2;              var d = parseInt("5" + "0");`;  
  
let ast = parse(code);  
  
traverse(ast, {  
    "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": (  
        path  
    ) => {  
        const { confident, value } = path.evaluate();  
        if (value == Infinity || value == -Infinity) return;  
        confident && path.replaceWith(types.valueToNode(value));  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

发现成功反混淆成功

var a = true;
var b = false;
var c = 10;
var d = 50;

这里有个注意的点是我们直接使用

const { confident, value } = path.evaluate();  
console.log(confident, value);

我们发现它不只4个值,这里的原因是一行不只一个node,可能一个node是另一个node内(ast语法树,是树的结构啦)。那么我们只要最外面的node的值即可。

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import * as types from "@babel/types";  
  
const code = `var a = !![];  
              var b = "abc" == "bcd";              var c = (1 << 3) | 2;              var d = parseInt("5" + "0");`;  
  
let ast = parse(code);  
  
traverse(ast, {  
    "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": (  
        path  
    ) => {  
        const { confident, value } = path.evaluate();  
        console.log(confident, value);  
        console.log(path.node.loc);  
  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

当我们替换最外层,内部的就不会再计算了。这就是替换为什么只有4次的原因。

字符串还原
const strings = ["\x68\x65\x6c\x6c\x6f\x77\x6f","\x72\x6c\x64"]
import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
  
const code =fs.readFileSync("1.js", "utf-8"); //注意要从文件读取,不然会被编译器自动替换了。
let ast = parse(code);  
traverse(ast, {  
    StringLiteral({ node }) {  
        if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {  
            node.extra.raw = node.extra.rawValue;  
        }  
    },  
});  
const { code: output } = generate(ast);  
console.log(output);

我们直接去把他的node对象的rawValue替换为node.extra.raw值,就可以了

无用代码剔除
const _0x16c18d = function () {
  if (!![[]]) {
    console.log("hello world");
  } else {
    console.log("this");
    console.log("is");
    console.log("dead");
    console.log("code");
  }
};

const _0x1f7292 = function () {
  if ("xmv2nOdfy2N".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();

即见使用了判断语句的进行干扰。
在 AST 中,IfStatement 节点有以下几个主要属性:

  • test: 条件表达式。
  • consequent: if 分支执行的代码。
  • alternate: else 分支执行的代码,如果没有 else 分支,则这个属性为 null

我们先获取到判断语句,直接搜索下!号位置

····
    test: Node {
      type: 'UnaryExpression',
      start: 40,
      end: 46,
      loc: [SourceLocation],
      operator: '!',
      prefix: true,
      argument: [Node]
    },
····

我们使用

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import fs from "fs";  
  
const code = `const _0x16c18d = function () {    if (!![[]]) {    console.log("hello world");  } else {    console.log("this");    console.log("is");    console.log("dead");    console.log("code");  }};  const _0x1f7292 = function () {      
 if ("xmv2nOdfy2N".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();`;  
let ast = parse(code);  
traverse(ast, {  
    IfStatement(path) {  
  
        console.log(path.get("test").evaluate());  
  
    }  
});  
  
const { code: output } = generate(ast);  
console.log(output);

去获取对应的值吗,然后去选择用consequent还是alternate替换原有的ast树节点。

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import fs from "fs";  
  
const code = `const _0x16c18d = function () {    
if (!![[]]) {    console.log("hello world");  } else {    console.log("this");    console.log("is");    console.log("dead");    console.log("code");  }};  const _0x1f7292 = function () {    
  if ("xmv2nOdfy2N".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();`;  
let ast = parse(code);  
traverse(ast, {  
    IfStatement(path) {  
        let { consequent, alternate } = path.node;  
        let testPath = path.get("test");  
        // console.log("test path", testPath);  
        // console.log(typeof testPath);       
        const evaluateTest = testPath.evaluateTruthy();  
        console.log("evaluateTest", evaluateTest);  
  
        if (evaluateTest === true) {  
            if (types.isBlockStatement(consequent)) {  
                consequent = consequent.body;  
            }  
            path.replaceWithMultiple(consequent);  
        } else if (evaluateTest === false) {  
            if (alternate != null) {  
                if (types.isBlockStatement(alternate)) {  
                    alternate = alternate.body;  
                }  
                path.replaceWithMultiple(alternate);  
            } else {  
                path.remove();  
            }  
        }  
    }  
});  
  
const { code: output } = generate(ast);  
console.log(output);
反控制流平坦化
const code = `
  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;
  }
`;

我们先查看对应的AST结构

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import fs from "fs";  
  
const code = `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;}`;  
let ast = parse(code);  
  
traverse(ast, {  
    WhileStatement(path) {  
        console.log(path)  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

发现东西有点多,搜索WhileStatement字段发现

 node: Node {

    type: 'WhileStatement',

    start: 45,

    end: 234,

    loc: SourceLocation {

      start: [Position],

      end: [Position],

      filename: undefined,

      identifierName: undefined

    },

    test: Node {

      type: 'BooleanLiteral',

      start: 52,

      end: 56,

      loc: [SourceLocation],

      value: true

    },

    body: Node {

      type: 'BlockStatement',

      start: 58,

      end: 234,

      loc: [SourceLocation],

      body: [Array],

      directives: []

    }

  },

使用

traverse(ast, {  
    WhileStatement(path) {  
        console.log(path.node.body)  
    },  
});

ast树
Node {
  type: 'BlockStatement',
  start: 58,
  end: 234,
  loc: SourceLocation {
    start: Position { line: 3, column: 13, index: 58 },
    end: Position { line: 5, column: 115, index: 234 },
    filename: undefined,
    identifierName: undefined
  },
  body: [
    Node {
      type: 'SwitchStatement',
      start: 64,
      end: 225,
      loc: [SourceLocation],
      discriminant: [Node],
      cases: [Array]
    },
    Node {
      type: 'BreakStatement',
      start: 227,
      end: 233,
      loc: [SourceLocation],
      label: null
    }
  ],
  directives: []
}

我们发现body有两个,我们要的是表达式,那么肯定是第一个(不知道就问gpt分别是什么意思)

WhileStatement(path) {  
    console.log(path.node.body.body[0])  
},



ast树
Node {

  type: 'SwitchStatement',

  start: 64,

  end: 225,

  loc: SourceLocation {

    start: Position { line: 4, column: 2, index: 64 },

    end: Position { line: 5, column: 106, index: 225 },

    filename: undefined,

    identifierName: undefined

  },

  discriminant: Node {

    type: 'MemberExpression',

    start: 72,

    end: 78,

    loc: SourceLocation {

      start: [Position],

      end: [Position],

      filename: undefined,

      identifierName: undefined

    },

    object: Node {

      type: 'Identifier',

      start: 72,

      end: 73,

      loc: [SourceLocation],

      name: 's'

    },

    computed: true,

    property: Node {

      type: 'UpdateExpression',

      start: 74,

      end: 77,

      loc: [SourceLocation],

      operator: '++',

      prefix: false,

      argument: [Node]

    }

  },

  cases: [

    Node {

      type: 'SwitchCase',

      start: 85,

      end: 130,

      loc: [SourceLocation],

      consequent: [Array],

      test: [Node]

    },

    Node {

      type: 'SwitchCase',

      start: 134,

      end: 176,

      loc: [SourceLocation],

      consequent: [Array],

      test: [Node]

    },

    Node {

      type: 'SwitchCase',

      start: 180,

      end: 222,

      loc: [SourceLocation],

      consequent: [Array],

      test: [Node]

    }

  ]

}

那么这下我们就成功获取到了对应的表达式的

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import fs from "fs";  
  
const code = `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;}`;  
let ast = parse(code);  
  
traverse(ast, {  
    WhileStatement(path) {  
        console.log(path.node.body.body[0].discriminant)  
        console.log(path.node.body.body[0].cases)  
  
    },  
});  
  
const { code: output } = generate(ast);  
console.log(output);

那么我们接下来的目标就是求值。首先我们如何获取switch表示式的值,我们使用方法scope.getBinding(arrName)去获取变量绑定的节点

traverse(ast, {  
    WhileStatement(path) {  
        //console.log(path.node.body.body[0].discriminant)  
        //console.log(path.node.body.body[0].cases)        console.log(path.scope.getBinding("s").path.node.init)  
    },  
});

这时就要涉及到对函数的调用了

object = init.callee.object; //获取node对象
property = init.callee.property; //获取到node对应的方法名称
let argument = init.arguments[0].value; //获取到node对应的方法的参数
let arrayFlow = object.value[property.name](argument);//通过方法名称获取到方法进行调用。

代码如下

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import fs from "fs";  
  
const code = `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;}`;  
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(arrayFlow)  
  
    },  
});

现在我们有了switch就要去匹配对应的一个case顺序了case的值是test.value属性

import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import { parse } from "@babel/parser";  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
const generate = _generate.default;  
import fs from "fs";  
  
const code = `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;}`;  
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;  
        //console.log(cases)  
        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(arrayFlow);  
  
  
        let resultBody = [];  
        arrayFlow.forEach((index) => {  
            let switchCase = cases.filter((c) => c.test.value == index)[0];  
            console.log(switchCase);  
  
        });  
        path.replaceWithMultiple(resultBody);  
    },  
  
});

这样就把执行顺序给排列清楚了。然后进行AST语法树替换即可,同时把 continue和break去除即可。

const code = `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;}`;  
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); 

		
        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);
整合代码
import _traverse from "@babel/traverse";  
const traverse = _traverse.default;  
import * as types from "@babel/types";  
import _generate from "@babel/generator";  
import {parse} from "@babel/parser";  
import fs from "fs";  
const generate = _generate.default;  
  
const code = fs.readFileSync("1.js", "utf-8");  
let ast = parse(code);  
  
function callToStr(path) {  
}  
  
traverse(ast, {  
    "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": (  
        path  
    ) => {  
        const { confident, value } = path.evaluate();  
        if (value == Infinity || value == -Infinity) return;  
        confident && path.replaceWith(types.valueToNode(value));  
    },  
});  
let { code: output } = generate(ast);  
ast = parse(output);  
  
traverse(ast, {  
    StringLiteral({ node }) {  
        if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {  
            node.extra.raw = "\""+node.extra.rawValue+"\"";  
        }  
    },  
});  
output = generate(ast).code;  
  
  
  
ast = parse(output);  
traverse(ast, {  
    IfStatement(path) {  
        let { consequent, alternate } = path.node;  
        let testPath = path.get("test");  
        const evaluateTest = testPath.evaluateTruthy();  
        if (evaluateTest === true) {  
            if (types.isBlockStatement(consequent)) {  
                consequent = consequent.body;  
            }  
            path.replaceWithMultiple(consequent);  
        } else if (evaluateTest === false) {  
            if (alternate != null) {  
                if (types.isBlockStatement(alternate)) {  
                    alternate = alternate.body;  
                }  
                path.replaceWithMultiple(alternate);  
            } else {  
                path.remove();  
            }  
        }  
    }  
});  
  
output = generate(ast).code;  
ast = parse(output);  
  
traverse(ast, {  
    WhileStatement(path) {  
        const { node, scope } = path;  
        const { test, body } = node;  
  
        if (!types.isLiteral(test, { value: true })) return;  
        if (body.body.length != 2) return;  
        let switchNode = body.body[0],  
            breakNode = body.body[1];  
        if (  
            !types.isSwitchStatement(switchNode) ||  
            !types.isBreakStatement(breakNode)  
        ) {  
            return;  
        }  
        let { discriminant, cases } = switchNode;  
        if (!types.isMemberExpression(discriminant)) return;  
        let { object, property } = discriminant;  
        if (!types.isIdentifier(object) || !types.isUpdateExpression(property))  
            return;  
        let arrName = object.name;  
        let binding = scope.getBinding(arrName);  
        if (!binding || !binding.path || !binding.path.isVariableDeclarator())  
            return;  
        let arrayFlow = binding.path.get("init").evaluate().value;  
        let resultBody = [];  
        arrayFlow.forEach((index) => {  
            let switchCases = cases.filter(  
                (switchCase) => switchCase.test.value == index  
            );  
            let switchCase = switchCases.length > 0 ? switchCases[0] : undefined;  
            if (!switchCase) {  
                return;  
            }  
            let caseBody = switchCase.consequent;  
            if (types.isContinueStatement(caseBody[caseBody.length - 1])) {  
                caseBody.pop();  
            }  
            resultBody = resultBody.concat(caseBody);  
        });  
  
        path.replaceWithMultiple(resultBody);  
    },  
});  
output = generate(ast).code;  
console.log(output);

参考内容

python3网络爬虫开发实践

posted @ 2024-06-19 17:30  Ho1d_F0rward  阅读(127)  评论(0)    收藏  举报