反爬虫技术须要,最近调研了关于 Javascript 静态分析相关知识,主要是协助进行 Javascript 的反混淆工做。javascript
本身自己对于 Javascript 的技术掌握接近于 0,在网上也找到很多的反混淆的工具,这里放出来几个比较好用的:java
prepack 代码简化在线工具node
jstillery javascripts 代码反混淆在线工具git
经过 https://obfuscator.io/ 生成的代码放到上面的两个工具中,基本上均可以反混淆出东西来,可是本身遇到的场景与上述仍是有差别的,须要本身定制化一些东西,也就须要对 AST 这个工具比较熟悉,而且可以灵活使用起来。github
AST 是什么
不管是 javascripts 仍是 Python 的 AST 他们的做用都是类似的,就是把原代码转换成 token,这个过程是程序编译过程当中一个重要的步骤。AST 全称是抽象语法树,简单点理解就是有了这个树你能够更加方便的遍历和修改以前的逻辑了,使用程序修改源代码远比使用编辑器更加的快速准确,自动化程度更高,使用抽象语法树还能够自动生成源代码。express
初识 Javascript AST
Javascript AST 有不少的工具,并且不少都很成熟,能够把源代码快速成 JSON 对象,一个样例以下:npm
源代码:微信
console.log("hello, world")
解析事后的 AST ,使用 https://astexplorer.net/ 生成:编辑器
{
"type": "Program",
"start": 0,
"end": 27,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 27,
"expression": {
"type": "CallExpression",
"start": 0,
"end": 27,
"callee": {
"type": "MemberExpression",
"start": 0,
"end": 11,
"object": {
"type": "Identifier",
"start": 0,
"end": 7,
"name": "console"
},
"property": {
"type": "Identifier",
"start": 8,
"end": 11,
"name": "log"
},
"computed": false
},
"arguments": [
{
"type": "Literal",
"start": 12,
"end": 26,
"value": "hello, world",
"raw": "\"hello, world\""
}
]
}
}
],
"sourceType": "module"}
其实看起来也不是很难,先是一个表达式,里面再是函数调用,而后是函数的来源,参数等。若是有兴趣能够本身去在线的站点输入本身 熟悉的语句,看看各个语句有什么不一样的地方。关于抽象语法树的标准文档看这里。函数
遍历 AST
解析 javascript 抽象语法书并不难,我以为困难的地方是怎么遍历的问题,这就是很关键的一个部分了,有些库给的文档很全,有些库虽然 不少 star,可是文档太少了,对于初学者来讲也不是好库。通常遍历一个树有深度优先和广度优先两个方式,这两个方式取到的东西的顺序也就不一样。
这里我使用的一个库是 https://github.com/estools/estraverse,从源码考证是一个深度优先遍历工具,具体的咱们能够写个 demo 测试一下:
function level1() {
function level2() {
;
}
}
function level1_1() {
;
}
若是是深度优先的话,首先读取到的应该是 leve1 -> level2 -> level1_1。
测试代码以下:
var esprima = require("esprima");
var estraverse = require("estraverse");
var ast = esprima.parse(`
function level1() {
function level2() {
;
}
}
function level1_1() {
;
}
`);
estraverse.traverse(ast, {
enter: enter,
leave: leave
});
function enter(node) {
if (node.type === "FunctionDeclaration") {
console.log("enter function: ", node.id.name);
}
}
function leave(node) {
if (node.type === "FunctionDeclaration") {
console.log("leave function: ", node.id.name);
}
}
安装代码须要的依赖:
npm install esprima
npm install estraverse
代码执行结果以下:
enter function: level1
enter function: level2
leave function: level2
leave function: level1
enter function: level1_1
leave function: level1_1
了解遍历顺序对于后面的实战更加剧要,反混淆过程当中一般会涉及到做用域的问题,若是自身对于遍历顺序不清楚的话,那么很难操做函数和变量的做用域。
实战获取函数的全部参数
接下来思考如何利用 AST 获取函数的全部入参,好比下面的示例代码:
var esprima = require("esprima");
var estraverse = require("estraverse");
var ast = esprima.parse(`
function level1(a, b) {
function level2(c, d) {
;
}
}
function level1_1(e, f) {
;
}
`);
var scopeList = [];
estraverse.traverse(ast, {
enter: enter,
leave: leave
});
function enter(node) {
createNewScope(node);
if (node.type === "FunctionDeclaration") {
let scope = scopeList[scopeList.length - 1];
for (const item of node.params) {
scope.push(item.name);
}
}
}
function leave(node) {
if (node.type === "FunctionDeclaration") {
const a = scopeList.pop();
console.log("params defined in ", node.id.name, ", params list: ", a);
}
}
function createNewScope(node) {
if (node.type === "FunctionDeclaration") {
scopeList.push([])
}
}
如何获取函数体内定义的全部变量
如何利用 AST 获取某个做用域里面的全部变量?如何检查某个变量在某个做用域下面是否已经定义?下节再续~
本文分享自微信公众号 - 茶歇小栈(smilehackerboy)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。