0x01 简单介绍
JS逆向,这门技术一般会应用在电脑浏览器上,相对于手机端的App逆向而言,研究它会省去很多繁冗的步骤,比如:平台搭建、环境模拟等。换句话说,你只需要一台能玩扫雷的电脑,就可以让它无处遁形,当然了,如果你手里真的有这样一台电脑,不建议你拿来研究逆向,而是应把它先扛到古玩市场。
0x02 先从结果分析
先看一眼原始数据
这样的数据除了让人感到眼花外,还有就是能激发你的好奇心——这是啥?但,如果把它们经过一些特殊服务处理后,就会产生一种意想不到的结果,而这个过程在密码学中又称之为AES对称加密
0x03 怎么理解
鉴于网上对这种算法的介绍过于专业,只适合那些经常失眠或有睡眠障碍的人来研究。所以特地我写了一篇轻松点文章来帮你搞懂它。
0x04 加密定位
因为某瓣的响应中所携带的加密数据储存到了window.__DATA__属性中,所以可以直接全局搜索这个关键字
根据索引位置,直接定位到一个名为bundle.js文件。这里提示一下,针对于这种显而易见的东西,我们直接搜索,确实能够快速定位到加密位置,但相对于某音算法或者某数算法来讲,就算你把电脑搜冒烟,也不会有任何收获,因为它们采用的jsvmp逻辑层加密。
直接下断点,F5刷新浏览器
这里看到已经了通过Mt.dispatch函数所运行之后的结果
按F11跟进,对加密位置锁定
0x05 单例分析
从上图可以看出,它的整个解密流程大致分可为六个步骤, 最终的结果会在 n 函数中所呈现,也就是 return 的返回值,由此我们可以得出结果,把下面代码填充完整自然会水到渠成。
var a = e.from(r, "base64")
, s = Math.max(Math.floor((a.length - 2 * i) / 3), 0)
, u = a.slice(s, s + i);
a = e.concat([a.slice(0, s), a.slice(s + i)]);
var c = Object(o.hash)(e.concat([u, e.from(t)]));
return n((l = {},
l[c] = a,
l));
先看 a 所对应的 e.from 函数 鼠标悬停会自动提示函数所对应的位置
直接切入到vendor.js文件中 e.from 所指向的函数中
这里有个坑注意下,e.from 指向的是 vendor.js文件中 i 函数,所以误以为,把 i 函数扣出来就可以解决问题,但这样想无异于管中窥豹,因为 e.from 指向的 i 函数的过程, 而不是单一的函数
根据大括号代码段的提示,很容易就能定位到这个过程的开端与结尾
新建个tests.html文件并且在文件中输入以下代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DouBan-Decrypt</title>
</head>
<body>
<script type="text/javascript">
</script>
</body>
</html>
直接将代码粘贴 todo something 位置 鉴于代码太多我这里只做截图,就不对代码进行公布了。
通过浏览器打开这个文件,然后进入到console控制台,直接调用该函数, 发现关于 r 函数没有定义
确定报错位置
打断点跟官方代码进行比对
发现 r(72) 是个对象
0x06 r 怎么处理
这个 r 不太好处理的原因是因为它采用webpack压缩方式生成的,我没有对此架构进行过细致的研究。但我的想法是这样的,既然 r 传入的任何值都会生成一个对象,那它内部的方法,肯定能指向这个对象的整体,所以,我们只需要对它任意一个方法进行分析即可“以偏概全“
点击蓝色的链接完成跳转
看这里,你会对这个函数的作用彻底明了,它最后返回的正是 r(72) 所对应的
将代码整体复制,这里需要注意最重要的一点就是,要在最后给他增加返回机制,以便能把整个对象映射出去。
在console控制台进行调用,经观察得出结论, e 不能被 set property ,也就是说它没有设置属性的条件。
那就给他一个可以设置属性的条件: new Object
0x07 补全信息
r(72) 对象被拿到手了,下面要对 stringBuffer 开头那个函数进行补全
再次在控制台进行调用,经观察后,又发现一个报错信息,根据报错信来看,r 依旧未定义
跟官方比对之下,发现了 r(73) 对象中所包含的信息
有了对 r(72) 的分析过程,这次你可以根据经验把 r(73) 对象的数据进行补全,没有多难,照本宣科而已。
0x08 关于入参的问题
将 stringBuffer 也就是 i 函数所有报错信息处理完后,在控制台再次调用去校验结果
根据报错信息提示,切入进去一看,是有关于参数 t 的报错
再次打断点跟官方进行比对,我们发现传入的 t 参数是整个 window 对象
那就把window对象也给它
没有报错了,而且根据它返回的结果来看,它莫名的指向了一个函数
验证一下它的属性
这是本地调用 e.from 函数所生成的结果
这是官方调用 e.from 函数所生成的结果
0x09 关于内置函数
完成对 e 对象的分析后,我们再次回顾一下代码
var a = e.from(r, "base64")
, s = Math.max(Math.floor((a.length - 2 * i) / 3), 0)
, u = a.slice(s, s + i);
a = e.concat([a.slice(0, s), a.slice(s + i)]);
var c = Object(o.hash)(e.concat([u, e.from(t)]));
return n((l = {},
l[c] = a,
l));
由于 step 2~step 4 都是利用JavaScript内置函数完成的,我们可以直接在console控制台这样写
由此可见 step 1~step 4 都已经走通,目前还差step 5 step 6,也就是说只要把这两步走通,我们就会大功告成。但这两步在补全的过程中,你不会让你感到很难,但会发现它很坑。
0x10 避坑指南 I
上述出现的问题,是因为 o 对象未定义(情理之中),不过再对 o 对象补全的时候,不知道你是否注意到,有些函数看似没有被调用,实则在运行过程中被调用了。比如:
如果按官方所给出的那样只把 r(76) 指向所对应的函数而不去调用的话 ,那么就会影响 u = n(s)的结果
而官方所展现的结果是这样的
其原因就是在于必须对 r(76) 调用才会有相同的结果
0x12 避坑指南 II
我们再对stringBuffer对象补全完成后,一定要将它设置为全局变量,而且要用e对它进行定义。因为这个对象,在整个解密流程中都占有着举重若轻的地位,比如:下图这个 e 就指向了stringBuffer
0x13 最终效果
等把上述六个步骤全部用代码补全后,然后用node一执行,相信成就感肯定会在你脸上油然而生(至少我是这样的)
0x11 文末总结
1.对于JS逆向这门技术,我们没必要去刻意学习它,因为不同的网站会有不同的加密手段。所以我们侧重点在于如何培养逆向思维,比如:想明白这件事。 2. 对于研究比较复杂的网站而言,我们一定要恪守程序员的职责,不然真容易刷上瘾。 3. 你在阅读本篇文章的时候,可能会被它不严谨得逻辑搞得心乱如麻,没关系,如有需要你可以把邮箱留言下方,我直接把源码给你怼过去。对照学习后,肯定让你心如死灰心花路放 。
|