/ 爬虫 / 1310浏览

JavaScript AST抽象语法树常见节点及结构

前言

最近开始接触AST,用于解决JS代码混淆的问题。

在看@渔哥的文章,虽然目前还是有点云里雾里的。

本文总结了JS抽象语法树中常见节点及结构。。

推荐使用下方网站,结合例子对照观察。

工具网站:https://astexplorer.net/


Program(程序)

结构:

{
    "type": "Program",
    "body": [], // 多个节点
    "sourceType": "module",
    "range": [] //代码起始位置
}

Identifier(标识符)

可以简单的理解为变量名,函数名,属性名

{
    "type": "Identifier",
    "name": "", //
    "range": [] // 代码起始位置
}

例子:

a
_$0x

Literal(字面量)

可以理解为一个具体的值

结构:

{
    "type": "Literal",
    "value": 1, // 真实值
    "raw": "1", // 字符串形式的值
    "range": []
}

例子:

1
'2'
0x10

VariableDeclarator(变量声明符)

属于declarations中的一个元素

var a = 1, b = 2;

其中 a = 1 b = 2属于两个变量声明符

结构:

{
    "type": "VariableDeclarator",
    "id": Identifier, // 变量名
   	"init": null, // 初始化,如果声明并赋值,此处就有值,只声明未赋值则为null
    "range": []
}

例子:

var a, b = 2;

Property(属性)

属于properties的元素

键值对

由于 ES5 语法中有 get/set 的存在,所以有一个 kind 属性,用来表示是普通的初始化,或者是 get/set

结构:

{
    "type": "Property",
    "key": "", // 键
    "computed": "",
    "value": "", // 值
    "kind": "init",
    "method": false,
    "shorthand": false,
    "range": []
}

Declaration(声明)

VariableDeclaration-变量声明

声明变量

结构:

{
    'type': "VariableDeclaration",
	'declarations':  [VariableDeclarator, VariableDeclarator...], // `具体的声明, 这是一个列表,因为可以一次性声明多个变量`
	'kind': "变量修饰符,例如,var、let、const",
	'range': []// 声明代码的起始位置
}

例子:

var a;
var c, b;

FunctionDeclaration-函数声明

函数的声明

结构:

{
    'type': 'FunctionDeclaration',
    'id': '', // 可以简单的理解为函数名
    'params': [], // 参数
    'body': BlockSatement // 函数的主体内容,多为块语句
    'generator': false,
    'expression': false,
    'async': false,
    'range': []
}

例子:

function func(a, b) {}

Statement(语句)

BlockStatement-块语句

代码块,简单点理解就是{}包起来的代码

结构:

{
    "type": "BlockStatement",
    "body": [], // 代码主体
    "range": []
}

例子:

function a () {}
{}

ExpressionStatement-表达式语句

结构:

{
    "type": "ExpressionStatement",
    "expression": // 表达式, 例如赋值表达式
    "range": []
}

IFStatement-如果语句

if 语句

if (a === 1) {}

结构:

{
    'type': 'IFStatement',
    'test': '', // 判断,if语句()中的内容
    'consequent': '', // 结果,就是满足这个条件后,后续要执行的代码块
    'alternate': '', // else 中的内容,如果没有else,则为null
    'range': 
}

举例:

if (a === 1) {
	console.log(1);
} else {
    console.log(2);
}

WhileStatement-While循环语句

结构:

{
    "type": "WhileStatement",
    "test": ,//
    "body": BlockStatement// 循环体内的代码块
    "range": []
}

例子:

while (1) {}

ForStatement-For循环语句

结构:

{
    "type": "ForStatement",
    "init": // 初始化, 多为变量的声明
    "test": BinaryExpression // 判断
    "update": UpdateExpression // 更新表达式
    "range": []
}

例子:

for (var i = 0; i < p.length; i++) {
}

ReturnSatement-返回语句

结构:

{
    "type": "RetuenStatement",
    "argument": // 返回的值, 可以是具体的值,也可以是一个表达式
    "range": []
}

例子:

function func() {
    return 1;
}

Expression(表达式)

如果要执行一个表达式,必定由 语句 携带着。

AssignmentExpression-赋值表达式

给变量赋值

结构:

{
    "type": "AssignmentExpression",
	"operator": "=",
	"left": Identifier, // 操作符左边, 多为Identifier
    "right": // 操作符右边, 可能为,Identifier,Literal, FucntionExpression
	"range": [] // 代码起始位置
}

例子:

a = 1;
a += 1;
a |= 1;
a ^= 2;

FunctionExpression-函数表达式

{
	"type": "FunctionExpression"
	"id": null
	"params": [Identifier, ] // 函数的参数
 	"body": BlockStatement// 函数体, 就是大括号里面的代码  
	"generator": false,
	"expression": false,
	"async": false,
	"range": []
}

BinaryExpression-二元表达式

结构:

{
    'type': 'BinaryExpression',
    'operator': '===' // 二元操作符,比较操作符, < , > , ===,+,- ...
    'left': '' // 操作符的左边
    'right': '' // 操作符的右边
}

MemberExpression-成员表达式

简言之,就是访问对象的某个属性,这个属性就是成员

结构:

{
    'type': 'MemberExpression',
    'computed': true, // 计算, a['a']为true, a.a为false
    'object': '', // 对象
    'property': '', // 成员名
    'range': // 代码起始位置
}

例子:

a.a
a['a']

两者对比

image-20210606110152169

CallExpression-调用表达式

用于函数的调用,

结构:

{
    'type': 'CallExpression',
    'callee': '', // 被调用者,可以理解为被调用的方法
    'arguments': [], // 传递的参数
    'range': []
}

举例:

console.log(1);

此时的callee,就是console.log,则类型为MemberExpression

atob('a')

此时的calledd,就是atob,则类型为Identifier

ObjectExpression-对象表达式

结构:

{
    "type": "ObjectExpression",
    "properties": [Property, Property], // 属性
    "range": []
}

ArrayExpression-数组表达式

结构:

{
    "type": "ArrayExpression",
    "elements": [], // 元素
    "range": []
}

例子:

a = [1, 2]

UpdateExpression-更新运算表达式

update 运算表达式节点,即 ++/--,和一元运算符类似,只是 operator 指向的节点对象类型不同,这里是 update 运算符。

结构:

{
    "type": "UpdateExpression",
    "operator": "++", // 或者--
    "argument": ,
    "prefix": false, // 运算符,是否在前面,++i则为true, i++为false
    "range": []
}

例子:

i++
++i

SequenceExpression-序列表达式

结构:

{
    "type": "SequenceExpression",
    "expressions": [], // 包含的序列节点
    "range": []
}

例子:

function func () {
	var a = 1;
  return a = 2, 2
}

a = 2, 2就是序列表达式

参考文章

[1] 13 个示例快速入门 JS 抽象语法树

小试牛刀-利用AST平坦化一段瑞数代码
小试牛刀-利用AST平坦化一段瑞数代码
【瑞数】维普期刊搜索接口逆向总结_2_获取Cookie
【瑞数】维普期刊JS逆向4000字详细流程_1_获取接口签名
【瑞数】维普期刊JS逆向4000字详细流程_1_获取接口签名
CSS字体加密反反爬通用方法
CSS字体加密反反爬通用方法
玩物得志H5列表页签名逆向总结
玩物得志H5列表页签名逆向总结
Python使用Protobuf&&如何赋值&&如何正反序列化