菜鸟学Python编程AST抽象语法树-真要学ast
工作中碰到一个网站需要过极验验证码,去年11月左右,当时一点js不会就妄想破解极验,结果当然是输的很惨。最后选择selenium。到了今年4月份左右js正则匹配多个字符串,因为破解的去哪儿的web的js,感觉懂了很多js逆向的知识,有点小膨胀,想重新挑战极验。竟然还真挑战成功了。
多亏了蔡老板的AST教程让我事半功倍。看我也就图一乐,真要学ast还得看蔡老板。附上公众号名:菜鸟学Python编程
AST抽象语法树,听这个名字觉得很牛皮,高端,其实也就那么回事,把他理解成一个json就行了,没有那么可怕。很多混淆、反混淆的工作都是通过AST来完成的。
下面进入正题,我当时破解的时候,极验的fullpage版本还是8.9.3,今天打开网站一看,有8.9.5了,那我就直接搞最新的吧(有些网站应该还没上,不过都一样,猜测都应该是基于geetest6.0.9.js来的)
直接开搞,把这串js格式化后,我们先找一下eval这个关键词(因为我一开始没找,后面反混淆完后发现还有eval里面的没反混淆,又要回过头来反混淆。所以最好是一开始先找出来,一起反混淆掉)
很多这种类型的我们先去浏览器调试看下这是什么
都是一些代码,说明我们可以直接把得出的代码格式化后替换到js中。
然后看到有很多unicode格式的编码,这种用ast一键去除。
随便选中一个unicode编码,运行一下可以发现他的真实。
在ast在线编辑网站上()可以看到unicode编码就是因为有extra这个属性,我们只需把这个属性删掉,就能展示原来的值了(16进制同理)
导入依赖
<pre class="code-snippet__js" data-lang="javascript">const parser = require("@babel/parser");//将JS源码转换成语法树的函数
`const traverse = require("@babel/traverse").default;//遍历AST的函数const types = require("@babel/types");//操作节点的函数,比如判断节点类型,生成新的节点等:
const generator = require("@babel/generator").default;//将语法树转换为源代码的函数`const fs = require('fs');//const fs = require('fs')
</pre>
<pre class="code-snippet__js" data-lang="typescript">
`const visitor = { StringLiteral: {
enter: [replace_unicode]//遍历所有StringLiteral属性 }
};
function replace_unicode(path){ delete path.node.extra;
}var jscode = fs.readFileSync("fullpage8.9.5.js", {
encoding: "utf-8"});
let ast = parser.parse(jscode);traverse(ast,visitor);
let {code} = generator(ast);
fs.writeFile('fullpage8.9.5_1.js', code, (err)=>{});`</pre>
删除raw属性,然后保存,验证一下unicode编码是否都被还原了
只剩这些正则的pattern无法还原了。第一步反混淆就成功了。
(这里AST替换unicode编码有点小问题,就是不能把中文的unicode编码转回去,这点我也还没搞清楚,碰到中文的unicode的话还是找个网站转码吧。)
接下来通过调试可以发现出现了很多这种东西
DAi其实就是个类似数组的东西,传入索引就返回值,
EMf对代码功能上来说一点用没有,就是用来制造平坦化控制流的东西。
我们现对DAI进行反混淆。
把所有这种格式的还原成字符串
<pre class="code-snippet__js" data-lang="css">AJgjJ.DAi(36)
</pre>
在AST网站上解析
思路就是这样。
<pre class="code-snippet__js" data-lang="cs">function replace_DAi(path){
` var node = path.node; if(node.callee == undefined || node.callee.property ==undefined )
return; if (node.callee.property.name == "DAi"){
let arg = node.arguments[0].value; let value = AJgjJ.DAi(arg);
PathToLiteral(path,value) }
}function PathToLiteral(path,value){
switch (typeof value) { case 'boolean':
path.replaceWith(types.booleanLiteral(value)); break;
case 'string': path.replaceWith(types.stringLiteral(value));
break; case 'number':
path.replaceWith(types.numericLiteral(value)); break;
default: console.log("出现其他类型" + value + "类型:" +typeof value);
console.log(value); break
}`}
</pre>
这里替换的时候需要类型判断,写了个通用的方法,如果出现其他类型,打印出来然后自行判断后写替换代码。
DAi()类型的都成功替换了
但是极验这里为了防止一键替换所有数组,这里做了个小处理。
出现很多这种代码
给很多随机名称的变量赋值后,在通过这个变量去拿数组中的真实值。
通过观察代码可以得出DVmS 和 EKsN 这两个位置的变量 就是用来赋值的功能。
只需把这两个位置的所有变量的名字取出js正则匹配多个字符串,存到一个数组中,然后判断所有类似SQxP(1598)类型里面是否存在这个数组中的名字即可,如果存在着直接取出value后执行DAi赋值。
<pre class="code-snippet__js" data-lang="javascript">function get_name_Array(path){
` var node = path.node; if (node.declarations == undefined
|| node.declarations.length !=3 || node.declarations[0].init == undefined
|| node.declarations[0].init.property == undefined ) return;
if (node.declarations[0].init.property.name != "DAi") return;
let name1 = node.declarations[0].id.name; let name2 = node.declarations[2].id.name;
name_Array.push(name1,name2);`}
</pre>
获取所有name 存入Array
然后就是替换了
<pre class="code-snippet__js" data-lang="javascript">function replace_name_Array(path) {
` var node = path.node; if(node.callee == undefined || node.callee.name ==undefined )
return; if (name_Array.indexOf(node.callee.name) == -1)
return; let arg = node.arguments[0].value;
let value = AJgjJ.DAi(arg); PathToLiteral(path,value)
}`</pre>
这就把所有和DAi相关的数组替换了
我们用ctrl+F正则匹配搜索括号里面带数字类型的
直接少了5000多个,剩下的都是别的类型的。
现在其实差不多把极验js的关键代码反混淆了。
已经可以去调试了。
但是追求极致的我觉得还不够。
还有这么多无用的代码,需要删除,不然看的真的难受。
继续干!
继续在AST网站上分析
先把上面那种删除吧。
<p><pre class="code-snippet__js" data-lang="javascript">function check_DAi(declaration) {
` if (declaration.init == undefined || declaration.init.property == undefined)
return ; if (declaration.init.property.name == "DAi")
return true;}
function del_DAi(path) { var node = path.node;
var arrNode = node.declarations;` for(var i=0; i
发表评论
热门文章
Spimes主题专为博客、自媒体、资讯类的网站设计....
一款个人简历主题,可以简单搭建一下,具体也比较简单....
仿制主题,Typecho博客主题,昼夜双版设计,可....
用于作品展示、资源下载,行业垂直性网站、个人博客,....
热评文章
最新评论
Z.
11月29日
博主你好,Deng插件,这个点击不进去,提示这个(Warning: require_once(/www/wwwroot/w.zzy2020.com/usr/plugins/Deng/Deng/html/profile.php): failed to open stream: No such file or directory in /www/wwwroot/w.zzy2020.com/Fresh/extending.php on line 26
Fatal error: require_once(): Failed opening required '/www/wwwroot/w.zzy2020.com/usr/plugins/Deng/Deng/html/profile.php' (include_path='.:/www/server/php/72/lib/php') in /www/wwwroot/w.zzy2020.com/Fresh/extending.php on line 26)
点都德
2天前
:喷::喜欢::怒::黑线: