JavaScript-混淆与逆向必读之-AST-节点类型名词基础

3次阅读

共计 3793 个字符,预计需要花费 10 分钟才能阅读完成。

我在《Python3 反爬虫原理与绕过实战》一书中给出了“爬虫与反爬虫都是综合技术的利用”、“技术在反抗中提高”这样的观点。随着工夫的推移、技术的遍及和提高,Web 利用方给爬虫减少了越来越多的限度,其中成果最显著的就是代码混同。

单纯的加密算法或者自定义的字符处理函数曾经无奈满足进攻需要了,Web 利用方将眼光转移到了代码混同技术。代码混同有几个长处:

  • 操作门槛低,有现成可用且收费的混同产品;
  • 混同成果好,混同后真的是连亲妈都不意识;
  • 浏览器可能失常解析混同后的代码,一万行以内的小规模混同对性能影响不大;
  • 混同带来的性能影响能够通过其余优化升高,不慌;

加密算法和字符串处理函数配合代码混同,防御力直线回升。举个简略例子,一个简略的字符处理函数如下:

这里有三个函数,stringArray 返回一个蕴含字符的数组对象、mergeArray 将数组对象里的元素拼接成为一个字符串并返回、main 调用 stringArray 函数和 mergeArray 函数并打印失去的字符串,下方的 output 正文即运行后果。

这么清晰明了的函数调用,爬虫工程师能看不懂吗?

咱们看看下面三个函数混同后的样子:

一样的性能、一样的输入,然而代码却齐全不一样了,变得不可读。如果把上面的正文去掉,那你基本就不晓得产生了什么,也不晓得会输入什么,这就是代码混同给 Web 利用方带来的防御力。

作为一名爬虫工程师,你当初有两个抉择:

  1. 通过一个入口函数强行找出关联的函数调用,直到铺满调用链后拿到正确的输入;
  2. 还原局部混同,从这堆横七竖八的代码中捋清逻辑,再依据复杂度抉择用其余语言实现或回到第一步;

第一种办法,就是平时爬虫工程师说的“硬扣”,如果有跨文件的函数调用和简短简单的调用链,那“硬扣”真的是会 掉头发 的。

第二种办法的技术门槛略微高一些,须要爬虫工程师懂得 AST 实践,并学会编写还原代码,将横七竖八的代码浏览难度升高,从而升高本人浏览代码逻辑或者整顿调用链的难度与老本。

什么是 AST?

这里援用百度百科对 AST 的解释:

在计算机科学中,形象语法树 Abstract Syntax Tree,AST),或简称 语法树(Syntax tree),是源代码语法结构的一种形象示意。它以树状的模式体现编程语言的语法结构,树上的每个节点都示意源代码中的一种构造。之所以说语法是“形象”的,是因为这里的语法并不会示意出实在语法中呈现的每个细节。比方,嵌套括号被隐含在树的构造中,并没有以节点的模式出现;而相似于 if-condition-then 这样的条件跳转语句,能够应用带有两个分支的节点来示意。

嗯,这看起来有点绕,我打算用一个例子来表述。JavaScript 变量申明和赋值的代码示例如下:

var nick = "vansenb";

这一行代码会被解析成很长的语法树,具体解析可通过 AST Explorer 查看。以下是 JavaScript 语句和语法树的对应关系:

图有点含糊,想看清晰构造的请移步 AST Explorer。

AST 有什么用?

上图的语法树中表明了 程序主体、申明类型、标识符、字面量 等信息,由此咱们能够得出:

  • var – VariableDeclarator 变量申明;
  • nick – Identifier 标识符;
  • vansenb – Literal 字面量;

从人类浏览的角度来看,这行代码:申明了一个名为 nick、值为 vansenb 的变量。

如果你想扭转这行代码,将它变成:

var nick = "James";

只须要扭转语法树中 type 为 Literal 下的 value 属性对应的值即可,那么代码的语义就变成了:申明了一个名为 nick、值为 James 的变量。理解到这一点之后,咱们就能够思路放在代码的混同和还原下面了。

你想想,当你应用那些一键混同 / 还原工具的时候,是不是只须要将代码粘贴到输入框并点击“混同”按钮即可失去混同后的代码?而且雷同构造的代码混同后的构造也是雷同的?

这阐明一键混同 / 还原工具通过扭转原代码的形象语法树实现混同 / 还原的成果,例如在树的某个节点前后减少或删除节点,亦或在混同时将本来间接能够输入后果的单个函数转换为互相调用的多个函数。

罕用的 JavaScript AST 解析库

语法树并不是 JavaScript 独有的,简直所有编程语言都有语法树,例如 Golang、Python 和 Java。JavaScript 的语法树呈现频次较高,这是因为 JavaScript 隔代语法的差别和不得不思考的兼容性造成的,ES5 和 ES6 语法隔代,在理论利用中会须要进行语法的转换,这就使得语法树可能在理论场景中发挥作用。

语法树的作用就像是一个转接头,把代码的表现形式 A 转换为表现形式 B

JavaScript 畛域罕用的 AST 解析库有 babel、esprima、espree 和 acorn 等,各位工程师可依据本人的爱好和格调抉择趁手的库。

这些库经常被前端开发工程师用来编写代码转换的工具或者代码混同工具,甚至是将 React 和 Vue 的工程代码编译为浏览器能运行的 JavaScript 代码,而在爬虫工程师这里,大概率会用来辅助本人逆向 JavaScript 代码。

AST 节点类型名词根底

语法树相干的常识和技巧须要肯定的工夫学习(大略一两个月),对此感兴趣的你能够通过以下几篇实战型文章理解它的具体利用:

AST 还原 obfuscator 混同

操作 AST 还原混同代码根底系列课程三: 十六进制字符串还原

操作 AST 还原混同代码: 让代码剖析变得如此简略

AST 实战: 全自动解密经 obfuscator 混同的加密字符串

操作 AST 还原混同代码课程九: 还原简略的 CallExpression 类型

下面列举了罕用的几个 AST 解析库,尽管各个库解析同一份代码失去的构造不完全一致,但用于示意节点类型的名词简直都是统一的,例如 VariableDeclaration 代表这是变量申明语句、CallExpression 代表这是调用表达式。

把握节点类型的名词,有助于咱们在浏览语法树结构时更清晰地 理解节点的作用和用意 ,也能够说节点名词是咱们成为代码混同巨匠或代码逆向巨匠的必经之路, 十分重要

咱们以下图的代码为例,看看 AST 中罕用的节点类型名词有哪些。

上图代码蕴含了 JavaScript 语法中罕用的语句,例如变量申明、函数申明、三元表达式、if 控制流语句、switch 控制流、函数调用、赋值语句、数组申明、for 循环等。

将下面的代码复制到 AST Explorer 便能够失去语法树,依据左侧的代码和右侧的语法树,咱们能够统计语法树节点名词和具体形容,如下表:

序号 类型原名称 中文名称 形容
1 Program 程序主体 整段代码的主体
2 VariableDeclaration 变量申明 申明一个变量,例如 var let const
3 FunctionDeclaration 函数申明 申明一个函数,例如 function
4 ExpressionStatement 表达式语句 通常是调用一个函数,例如 console.log()
5 BlockStatement 块语句 包裹在 {} 块内的代码,例如 if (condition){var a = 1;}
6 BreakStatement 中断语句 通常指 break
7 ContinueStatement 继续语句 通常指 continue
8 ReturnStatement 返回语句 通常指 return
9 SwitchStatement Switch 语句 通常指 Switch Case 语句中的 Switch
10 IfStatement If 控制流语句 控制流语句,通常指 if(condition){}else{}
11 Identifier 标识符 标识,例如申明变量时 var identi = 5 中的 identi
12 CallExpression 调用表达式 通常指调用一个函数,例如 console.log()
13 BinaryExpression 二进制表达式 通常指运算,例如 1+2
14 MemberExpression 成员表达式 通常指调用对象的成员,例如 console 对象的 log 成员
15 ArrayExpression 数组表达式 通常指一个数组,例如 [1, 3, 5]
16 NewExpression New 表达式 通常指应用 New 关键词
17 AssignmentExpression 赋值表达式 通常指将函数的返回值赋值给变量
18 UpdateExpression 更新表达式 通常指更新成员值,例如 i++
19 Literal 字面量 字面量
20 BooleanLiteral 布尔型字面量 布尔值,例如 true false
21 NumericLiteral 数字型字面量 数字,例如 100
22 StringLiteral 字符型字面量 字符串,例如 vansenb
23 SwitchCase Case 语句 通常指 Switch 语句中的 Case

这只是罕用的那局部,更多节点类型名词在你须要用到时再补充即可。我会继续更新相干材料,感兴趣的敌人能够到夜幕团队的 GitHub 仓库 https://github.com/NightTeam/… 查看。

有了这些名词对照关系之后,咱们浏览语法树结构就变得简略了。当你看到节点 tpye 为 IfStatement 的时候,你晓得前面必定会有至多一个 BlockStatement,即 if (condition){}。

更多对于 AST 实践和实战的内容请关注夜幕团队公众号 NightTeam 或团队仓库 https://github.com/NightTeam。

正文完
 0