之前碰到有混淆的代码,都是依靠别人写好的插件,能不能还原听天由命

如果要冲高级工程师的话,AST是必备的一项技能,刚好现在有空,就花时间来研究一番……

参考资料:

环境准备:

  1. AST 在线解析网站:https://astexplorer.net/

image.png

纯白色实在太过亮眼了,官方没有提供模式切换,不过这里有DIY方案,效果如下:

image.png

  1. 安装babel功能包:npm install @babel/core @babel/parser @babel/traverse @babel/generator @babel/types

解析代码 -> 语法树(parser)

1
2
3
4
5
6
7
8
9
10
// 从文件读取
const fs = require("fs");
const code1 = fs.readFileSync('code.js', 'utf-8');

const parser = require("@babel/parser");
const code2 = "const name = 'cxs';";

// sourceType 默认为 script,当代码中含有 import 、export 等关键字时会报错,需要指定为 module
const ast = parser.parse(code2, {sourceType: "module"})
console.log(ast)

语法树 -> 生成代码(generator)

1
2
3
4
5
6
7
8
9
10
11
12
13
const parser = require("@babel/parser");
const generator = require("@babel/generator").default;

const code2 = "const name = 'cxs';";
const ast = parser.parse(code2, {sourceType: "module"})

// 修改源码
ast.program.body[0].declarations[0].id.name = "age";
ast.program.body[0].declarations[0].init.value = 25;
const result = generator(ast) // options
console.log(result.code);

>>> const age = 25;

image.png

遍历节点(traverse)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const parser = require("@babel/parser");
const generate = require("@babel/generator").default
const traverse = require("@babel/traverse").default

const code = `
const name = "";
const age = 0;
console.log(xxx);
`
const ast = parser.parse(code)

// 在 visitor 里面自定义处理逻辑,多个操作逗号xiagn
const visitor = {
NumericLiteral(path){
path.node.value = 25
},
StringLiteral(path){
path.node.value = "cxs"
},
CallExpression(path){
path.remove() // 删除节点
}
}

traverse(ast, visitor)
const result = generate(ast)
console.log(result.code)

`
输出:
const name = "cxs";
const age = 25;
`

构造新节点(types)

对照 AST网站函数的使用说明倒序的方式构造节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const parser = require("@babel/parser");
const generate = require("@babel/generator").default;
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");

const code = `
const name = "cxs";
`
const ast = parser.parse(code)

const visitor = {
VariableDeclaration(path){
let id = types.identifier("age");
let init = types.numericLiteral(25);
let declarator = types.variableDeclarator(id, init);
let declaration = types.variableDeclaration("const", [declarator]); // declarator 必须为 Arra
path.insertAfter(declaration);
path.stop(); // 停止遍历节点,防止无限循环
}
}

traverse(ast, visitor)
const result = generate(ast)
console.log(result.code)

`
const name = "cxs";
const age = 25;
`

进入和退出节点(enter / exit)

image.png