简述

之前爬网易云热评就对网易云音乐的加密研究了一下,这是一个分析网易web加密流程的记录,当然网上也有不少这种文章,不过我个人的总结我觉得比百度的详细了。

查看接口

通过xhr记录排查找到该数据由此接口返回

查看调用

第一个记录点进去可知最终由此方法发送请求

在这里插入图片描述

直接打上断点刷新页面

在这里插入图片描述

查看调用参数发现并不是热评接口,直接放行本次调用。

在这里插入图片描述

经过几次放行,发现热评接口。

在这里插入图片描述

查看请求参数发现参数被加密

在这里插入图片描述

何时加密

查看调用栈,寻找未被加密前是什么参数。

在这里插入图片描述

当点击此调用栈发现加密前参数

在这里插入图片描述

向上点击查看,当进入此调用栈时参数被加密

在这里插入图片描述

可以确定参数在此方法处理后被加密,将i7b这个参数打上断点刷新页面

在这里插入图片描述

第一次查看接口并不是热评接口

在这里插入图片描述

放行直到热评接口出现

在这里插入图片描述

由图可知此时的data没有被加密

在这里插入图片描述
在这里插入图片描述

由图可知这里的bWf7Y变量就是最终加密的参数,同时也能看出它是调用了asrsea这个方法后被加密了

在这里插入图片描述

加密过程

由上面可知加密使用的就是window.asrsea,直接搜索这个方法发现它指向了d方法

在这里插入图片描述
在这里插入图片描述

可以看到定义的d方法接收了4个参数分别是:d, e, f, g,查看调用所用到的参数

在这里插入图片描述

参数d就是将真实的参数序列化成字符串
参数e 调用bsG0x方法将返回值做为参数
搜索bsG0x方法查看定义

在这里插入图片描述

可以看到它返回一个字符串,因为加密调用bsG0x方法传入的是固定参数,此处可以直接在控制台执行这个方法,这个方法不管执行多少次都返回"010001"。

在这里插入图片描述

所以e参数即"010001"
在看f它也是使用方法返回值做为参数,直接执行它。

在这里插入图片描述

由此可知f就是"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"

因为它传入也是一个固定值,所以无论执行几次返回都是一样的值

在这里插入图片描述

最后一个参数g不用看直接执行

在这里插入图片描述

由上面步骤可得4个参数值
d:序列化真实的参数这个参数不是固定值
e:"010001"
f:"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g:"0CoJUm6Qyw8W8jud"

现在我们知道了d方法所需参数,现在回过头看d方法

function d(d, e, f, g) { var h = {} , i = a(16); return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h}

d方法先定义一个空对象和i变量,i变量使用a方法返回值做为值(传入固定值16),查看a方法的定义

function a(a) { var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = ""; for (d = 0; a > d; d += 1) e = Math.random() * b.length, e = Math.floor(e), c += b.charAt(e); return c}

a方法主要做的就是返回16位随机字符串,for循环中的e变量就是生成随机数

Math.random返回介于 0(包含) ~ 1(不包含) 之间的一个随机数

然后对e取最大整数

Math.floor返回小于等于x的最大整数。

c就是拼接每次随机的字符,现在继续看d方法

function d(d, e, f, g) { var h = {} , i = a(16); return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h}

可以看到d方法最终返回的就是h变量,先在h对象定义encText属性,第一次encText 使用b方法返回值做为值。

先看c方法,b方法后面说

function c(a, b, c) { var d, e; return setMaxDigits(131), d = new RSAKeyPair(b,"",c), e = encryptedString(d, a)}

从d方法的调用就可以知道,c方法的a,b,c参数除了b和c是固定值,只有a每次不一样。
里面的setMaxDigits和encryptedString来自rsa插件,这里内部不做详解
打上断点查看a的返回值

在这里插入图片描述

本次随机了16位字符:OzxSbrV3WrPa6Qf7

现在看d方法中的h.encTex它第一次使用b方法传入d和g做为参数

function b(a, b) { var c = CryptoJS.enc.Utf8.parse(b) , d = CryptoJS.enc.Utf8.parse("0102030405060708") , e = CryptoJS.enc.Utf8.parse(a) , f = CryptoJS.AES.encrypt(e, c, { iv: d, mode: CryptoJS.mode.CBC }); return f.toString()}

a参数不固定就是序列化请求参数字符串后结果,b参数是固定值,d里面第一次给定的是g也就是

"0CoJUm6Qyw8W8jud"

b方法做的就是使用AES加密最后返回一个字符串,这里关于具体的AES加密算法和CryptoJS不做详解
这里的iv就是指加密偏移量
mode指的的是加密模式,这里使用的CBC

第二次还是使用b方法传入第一次加密结果和16位随机字符串
最后在h对象加入encSecKey属性,值来自c方法,最终加密结果即h对象。