来自高纬度的对抗:AST解密JS代码实战(上)

前言

AST全名"Abstract Syntax Code",抽象语法树。他能够将JS源代码通过词法分析、语法分析后,按代码结构转换为树状显示,每个节点代表代码中的一个结构。AST在前端构建中起了非常大的作用,可以说充分体现了JavaScript这门语言的精髓。很多前端技术例如ES6、TypeScript的发展都离不开AST。

使用在线结构化工具(https://astexplorer.net/)写一个简单的函数如下:


左边是输出hello world的简单代码,右边是经过结构化后的树状结构。这里面的节点说明,可以参照https://github.com/yacan8/blog/blob/master/posts/JavaScript%E6%8A%BD%E8%B1%A1%E8%AF%AD%E6%B3%95%E6%A0%91AST.md

本文的重点并不是介绍AST,如果有读者对AST不了解的话,强烈建议先学习下AST。

开始

这次用的代码是某V5的加密,直接耶稣也解不开的配置拉满被。。

瞄一眼代码:
除了能认出好像是JS以外,啥都看不出来。没关系,我们慢慢来分析。
先把形如\x5d这种编码的字符串还原,随便找个格式化网站美化一下去掉\x..。。。

我太懒了,呜呜呜,对不起自己。

好,我们观察一下代码:

可以看到,代码里有大量类似“_0x515a("0x4", "z#w7")”这种形式的代码,点过去看下,做了一层加密。那么执行这个方法,就可以还原真实的参数。
丢浏览器里执行试试:
执行之后得到解密字符串。这种混淆在我之前的文章模拟登录一号店时也遇到了,当时是写正则脚本调用加密函数处理。这次换一种方式,人力分析。

以加密函数为界,我们将代码分为两个部分,第一部分为加密部分,第二部分为原本的逻辑部分。
我们先把第一部分剪出来。大概160行。我尝试过了将第一部分转为AST处理,但是貌似开启了代码保护模式,无法转换为AST树。无妨,那这部分我们手动处理。

满篇的十六进制字符串确实不好分析,为了方便理清逻辑,我们用编译器对重点参数批量替换。
先把这个大数组替换一下:
查看这个大数组的引用:

就是这个数组正下方的自执行函数。从这个函数开始扒:
通过上面的查看调用处,我们可以知道这里第一个参数_0x501011就是大数组,_0x442532是一个数字,这里传入的106。我们将_0x501011重命名为argument_list,_0x442532不知道干嘛用了,先命名为argument_num。
然后看下面的_0xf3ad3函数,一个while循环,貌似做了把大数组按某种序列重新排序的操作。继续替换,将函数名_0xf3ad3替换sort_list,参数_0x35e21d的引用只有在这里,不管他也无妨。然后继续往下看
一个新的函数_0x1e0679,里面定义了一个对象_0x48a671,定义了一些cookie的操作。那我们就把_0x48a671命名为cookie_object。
然后我们可以先合上他不管了,接着看:
这段貌似东西有点多,先批量改个名~~
清晰多啦,首先是定义了一个update_cookie方法,里面用正则对cookie_object的removeCookie方法的字符串匹配。这里有一个大坑,我开始做的时候也没有发现,后来百度了一下,才找到这个坑。把代码放进浏览器里执行:

看到这个方法返回了false,看到这里你是不是想去找false执行的逻辑了?如果真的这样去做,那就掉进了作者挖的坑里了。仔细观察你会发现这段正则里是没有匹配换行的,初始加密的代码也是没有换行,所有代码都在一行里。然而我们输入到浏览器的是格式化之后的代码,有大量的换行,必定是返回false啊。

我们把代码压缩一下再试试:
!神奇的事情发生了,原来这里真正返回的是true。他在这里实际上是检测了代码是否被格式化过,如果格式化了出现换行,就代表有人尝试以其他方式在运行这段代码,那么后续就不走正常的逻辑。这里需要注意。
那我们接着就看返回ture的逻辑吧:
第一个参数为null,第二个参数为字符串“counter”,getCookie内部首先判断第一个参数是否为空,如果为空就返回调用它时传入的参数。也就是_0x1a5844方法会返回传入的字符串。为了方便理解,我们直接改下这个函数被:
然后对第二个参数“counter”做了一些处理。我们先看看哪里用到了运行结果_0x2d894f。
好像这个_0x2d894f之后也没什么用,整个getCookie得到的结果也没有再使用。那么这个东西就是在混淆视听。他实际上真正要执行的是下面这段代码:
那整个getCookie的逻辑已经很清晰了,改写一下:
这里面的sort_list方法就是上面对数组进行重排序的方法,argument_num是整个自执行函数传入的第二个参数,也就是106。
至此,第一个自执行函数执行完毕。什么setCookie,removeCookie都是吓唬你的。

然后看第二个函数,也就是我们的加密函数。

一步一步来,先把我们看到的出现频次较高的变量重命名,改成人类能理解的形式:
逐行解读,首先第一行把arg1-0,就是把arg1强制转换为整型,这个也是js的特点,类型自动转换,比如我们想要整数10变成字符串的"10",只需要10+""就可以了。
然后在大数组里查找序号为arg1的元素赋值给argument。看到这里大家已经有眉目了吧,这个大数组就是和用到的参数映射起来的啊,不过是在映射的时候加了一层加密。

if判断,不用说了吧,这个东西必定是undefined,肯定会走这里的逻辑。进去是一个自执行函数。改一下名:
window.abot是js解base64的方法,这里将这个方法重写。
接下来是rc4的加密逻辑了,没必要管他,直接跳过:
往下:
emmm,故技重施,一样的,直接走最里层if里的逻辑。又是一层乱七八糟的加密逻辑。不看了,头大。。感兴趣的同学可以分析一下。
到这里代码的第一部分,加密部分就分析完了。我们把整段JS压成一行试下,随便拿一个调用输出试试:
没问题~~

下一步要请出AST了,解释一下第一部分为什么我没有用AST,一是代码逻辑比较零散,用AST处理事倍功半,每行代码都去找节点更麻烦,二是加入了代码保护,无法顺利的转为AST树,需要另外处理。于是直接手动分析了,也不是很麻烦,这样也能学会一些v5加密的逻辑。

接下来的第一步,把所有加密参数都解密。我们先把逻辑部分的代码复制进AST结构化工具里观察,找一个调用加密代码的地方:
首先节点类型是CalleeExpression,也就是函数执行表达式,内部最明显的特征是callee节点中的name属性为"_0x515a",并且arguments,也就是实参个数为2。通过以上规则,我们可以确定一个节点是否调用了解密函数。编写代码,首先新建一个js文件,把原始js代码以字符串的形式导出。为什么?因为不想看见他,恶心!
再新建一个js文件,把解密函数导出,这里解密函数还是用最初没有改变量名的吧,天晓得我们改的时候会不会在某些地方改错了:

然后是处理代码:
const template = require("@babel/template").default; 
const traverse = require("@babel/traverse").default; 
const t = require("@babel/types"); 
const generator = require("@babel/generator").default;   

const path = require('path'); 
const fs = require('fs')

const {jscode} = require('./v51');
const {encrypt} = require('./encrypt1')
// 将代码解析为AST树
const ast = parser.parse(jscode);

// 遍历Literal节点
traverse(ast, {
    CallExpression: decryptStr
})

function decryptStr(path) {
    var node = path.node;
    // 筛选符合条件的节点
    if (!t.isIdentifier(node.callee))
        return;

    if (node.callee.name!="_0x515a")
        return;
    
    if (!node.arguments||node.arguments.length!=2)
        return;
    // 替换节点值
    var new_str = encrypt(node.arguments[0].value, node.arguments[1].value);
    path.replaceWith(t.stringLiteral(new_str));
        
}

let {code} = generator(ast);
console.log(unescape(code.replace(/\\u/g, '%u')));

还原结果:

调用解密函数0x515a的全部部分已经被替换了。

总结

你肯定想问,还没完呢,咋就总结了?已经11点啦··明天还要上班,姑且算是前篇,后篇我会在近两天找时间更新。

其实这篇并没有用到多少AST,代码第一部分没有用的原因也提到了。第二部分的解密也刚刚开始,AST崭露头角。
这篇大部分篇幅都在分析解密函数上,我的想法还是这样,通常对一个反爬方案或者加密方案的处理是比较容易找出来的,更多的时候不需要去分析他处理的逻辑,node黑盒调用的场景太多了。但是如果永远不去分析,就永远学不会。只是了解怎么应对,不去理解原理,是没法解决100%的问题的。V5是比较优(e)秀(xin)的JS加壳之一,能够通过简单的分析理解他初步的加密原理也是很大的收获。

面对严重的加密混淆,一定要稳住,看见乱七八糟的代码不要怕,慢慢找入口点,最开始我也被这些代码吓到了,尤其是第一部分无法转成AST树,并且第一部分格式化后在浏览器中运行内存拉满也运行不出结果。直到分析以后才发现,他对JS代码做了一层校验,我输入格式化后的代码,正中圈套。

后篇将针对剩余的代码逻辑,力求还原给人类看的代码。

额,发现好像最近简书挖的坑有点多。。想起爱奇艺的m3u8下篇还没写。。。好吧,这次保证不会鸽,毕竟我也想看看自己能把这个加密还原成什么样~~~当然,无论结果如何,我都会把我最后取得的进度和过程分享出来,希望和需要的人一同进步。

最后

声明:本文分析过程仅供学习,并无任何个人以及商业或其他用途。如有不慎侵权,请联系我删除。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容