记一次对网易云前端接口的逆向

记一次对网易云前端接口的逆向

从二维码登录接口入手

  1. 界面如图
  1. 扫个码看看

是一串链接,有一个 codekey 参数

  1. 打开控制台抓包,找到关键的数据包,返回的unikey对应的就是codekey
  1. 看看payload,两个参数,paramsencSecKey
  1. 点入发起程序
  1. 打个断点,刷新并重新触发
  1. 看到了熟悉的参数

下断点看看值t

  1. 根据堆栈一步一步往前找到生成参数的位置
  1. 找到了
  1. 关键部位
  1. 打开控制台,先看看params如何生成
  1. d的值

  2. d的赋值代码

1
2
3
4
5
6
d = (0,y.asrsea)(
JSON.stringify(c),
(0,y.emj2code)(["流泪", "强"]),
y.BASE_CODE,
(0,y.emj2code)(["爱心", "女孩", "惊恐", "大笑"]
)

其中 (0,y.emj2code)(["流泪", "强"]) 相当于 y.emj2code(["流泪", "强"])

  1. 进一步发现,这几个值应该都是常量
1
2
3
4
5
6
7
8
9
10
11
> JSON.stringify(c)
'{"type":1,"noCheckToken":true}'

> (0,y.emj2code)(["流泪", "强"])
'010001'

> y.BASE_CODE
'00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

> (y.emj2code)(["爱心", "女孩", "惊恐", "大笑"])
'0CoJUm6Qyw8W8jud'
  1. 接着分析y.asrsea函数

打个断点,运行到这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
t.asrsea = function(e, t, n, r) {//e,t,n,r是上文中提到的常量
var o = {}
, i = function(e) {
for (var t, n = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", r = "", o = 0; o < e; o += 1)
t = Math.random() * n.length,
t = Math.floor(t),
r += n.charAt(t);
return r
}(16);
return o.encText = a(e, r), //进行了两次加密获取到encText
o.encText = a(o.encText, i),
o.encSecKey = u(i, t, n),//进行一次加密获取到enSecKey
o
}
  1. 看看 a 函数

AESivCBC 没加密字段,一看就知道啥加密方式了 XD

使用了AES的CBC模式进行加密,e为待加密文本,t为密钥,偏移量iv0102030405060708

  1. 验证一下,对上了
  1. 第二次加密同理,使用m3iws/mCg11hFMWMpeAXIJ33nggE141odvQwXdMOvv8=为密文,随机的16位a-zA-Z0-9字符作为密钥。
  1. 再看看 encSecKey怎么生成
  1. 很巧,就在 a 函数附近, u 函数就是用来生成 encSecKey
1
2
3
4
5
function u(e, t, n) {// e是随机的16位密钥,t是10001(十六进制的65537),n是前文提到的base_code
return i.default.setMaxDigits(131),
n = new i.default.RSAKeyPair(t,"",n),
i.default.encryptedString(n, e)
}
  1. 三个函数
  1. 到这里第一眼没看出来是什么加密方法,看函数名应该和RSA有关。把代码喂给ChatGPT,告诉我是一个nopadding的RSA加密。

  2. 开开心心让它写了一段RSA加密的python代码来验证。

    然后傻眼了,加密结果一直在变,前端的加密结果是不变的。

    又花了一段时间在互联网搜寻一番,发现了原来python的rsa没有实现nopadding的加密。。。

    从网上找了一篇解决方案https://www.cnblogs.com/pythonClub/p/10464745.html

  3. 校验一下,没问题

看看其他的接口

分析了一下其它的接口,发现用的都是同一套加密方案。

于是,在这里简单整理一下收集到的其他接口

获取二维码

1
POST /weapi/login/qrcode/unikey
POST参数 类型 描述
params string 必选 {"type":1,"noCheckToken":true}字符串AES加密结果
encSecKey string 必选 AES_KEY的RSA加密结果

获取登录状态

1
POST /weapi/login/qrcode/client/login
POST参数 类型 描述
params string 必选 {"type":1,"noCheckToken":true,"key":"{codekey}"}字符串AES加密结果,其中{codekey}获取二维码接口生成
encSecKey string 必选 AES_KEY的RSA加密结果
返回值 类型 描述
code int 800 -> 二维码不存在或已过期
801 -> 等待扫码
802 -> 授权中
803 -> 授权登陆成功
message string 消息

获取歌曲播放地址

1
POST /weapi/song/enhance/player/url/v1?csrf_token={csrf_token}
POST参数 类型 描述
params string 必选 {"ids":"[{song_id}]","level":"standard","encodeType":"aac","csrf_token":"{csrf_token}"}字符串AES加密结果,其中{codekey}获取二维码接口生成
encSecKey string 必选 AES_KEY的RSA加密结果

获取歌曲评论

1
POST /weapi/comment/resource/comments/get
POST参数 类型 描述
params string 必选 {"rid":"R_SO_4_{song_id}","threadId":"R_SO_4_{song_id}","pageNo":"{pageNo}","pageSize":"{pageSize}","cursor":"{timeStamp}","offset":"0","orderType":"1","csrf_token":"{csrf_token}"}字符串AES加密结果,其中{codekey}获取二维码接口生成
encSecKey string 必选 AES_KEY的RSA加密结果

(完)


记一次对网易云前端接口的逆向
http://xciphand.github.io/2024/02/05/记一次对网易云前端的逆向/
作者
xciphand
发布于
2024年2月5日
许可协议