[图一乐]b站w_rid解密
作者:cclear116 \QYQTexas 特别感谢:QYQTexas
分析抓包
捕获请求
点开网站往下滑,刷新评论,开发者界面>网络>Fetch/XHR
我们可以发现每次下滑遇到“加载评论”时**main?~**就会出现一次
点开预览>data>replies可以发现所有评论的信息都被存在了这里
推理与猜测
- 第一次加载页面时浏览器会发送第一个main?~请求,这个请求的负载中 pagination_str没有内容。
pagination_str: {"offset":""}
- 此后,每当我们在页面往下滑一定高度(可能是根据浏览器高度而定),浏览器就会发送一次main?~请求。在这些请求 中
pagination_str: {"offset":"{\"type\":1,\"direction\":1,\"session_id\":\"1759659020379372\",\"data\":{}}"}
- 尝试不携带w_rid和wts直接请求 :
https://api.bilibili.com/x/v2/reply/wbi/main?oid=451990988&type=1&mode=3&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=&web_location=1315875
结果访问被服务器拒绝了 - 尝试修改w_rid和wts中的数据
- 多触发几次“加载评论”,分析其中的评论信息。我们可以发现除第一次请求以外剩余的所有请求表头都近乎一样,唯一不一样的参数就是w_rid和wts
- 进行大胆的猜测(纯主观,我真的不是很懂原理):w_rid和wts在浏览器生成, pagination_str用于向服务器发送“给我下一页评论内容”的命令。服务器收到命令后生成并验证w_rid和wts发现这两个参数与服务器生成的“密码”不匹配,最终因权限不足拒绝访问。
添加断点
添加断点,然后触发一次“加载评论”
查看被断处
fetch()
是一个内建的Web API函数,用于发送HTTP请求。它不是js代码,我们不再管它。
const {url: Q, method: Y, headers: z, credentials: J} = V.request;
这行代码从 V.request
对象中解构了四个属性:url
、method
、headers
和 credentials
,并将它们分别赋值给了新的常量变量 Q
、Y
、z
和 J
。
可以发现 V.request
包含了w_rid,即发送的请求,我们追查 V.request
好几千个v,我崩溃了,直接搜w_rid
跳过v,查找wts和w_rid
追查wts
在源代码中ctrl+f查找’wts’,发现:
继续,搜索上一个:
Math.round(Date.now() / 1e3)
的结果是自1970年1月1日 00:00:00 UTC以来的当前秒数(四舍五入到最接近的整数)。这通常用于获取当前的Unix时间戳(以秒为单位)
我们进行验证,先触发“加载评论”获取wts,再运行js制造一个wts
触发一次“加载评论”wts:1718418881
Math.round(Date.now() / 1e3)
:1718418890
对比两个wts可以认为wts就是“不带毫秒的Unix时间戳”(我手动操作的,所以有10s的间隔)
追查w_rid
源代码ctrl+f搜索 w_rid (早知道直接搜就好了,我逆向找了半天还没找到……)
md5
w_rid是由 md5(L + z)
得出,我们先搜索md5
getDefaultExportFromCjs(V)
进一步 我们搜索 function getDefaultExportFromCjs(V)
md5Exports和md5$1
var md5Exports = md5$1.exports;
md5$1.exports = function(J, X){略}
ctrl+f搜索md5$1发现 定义变量和定义函数的代码正好在一起
整合代码
如果我们想通过js生成w_rid就得把关键代码整合起来,通过分析代码我们可以发现以下内容都得保留:
记得打上断点辅助定位代码,免得整合代码块的时候搞错顺序
我们把上面这段代码先运行一下,观察报错:ReferenceError: cryptExports is not defined
根据报错我们只需找到 cryptExports
即可
继续运行查看报错: ReferenceError: crypt is not defined
重复以上步骤 ,最后整合好的代码大概250行
保存好整合的用于生成w_rid的代码,后面会用到
L + z
控制台输入 L+z
,运行结果如下:
'mode=3&oid=1005542455&pagination_str=%7B%22offset%22%3A%22%22%7D&plat=1&seek_rpid=&type=1&web_location=1315875&wts=1718433846ea1db124af3c7062474693fa704f4ff8'
我们有理由认为,w_rid由除了它以外的所有参数通过md5生成
MD5(Message-Digest Algorithm 5)是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在RFC 1321标准中被加以规范。
可以发现L+z由 url中的参数组成,因此不需要再追L+z,直接构造L+z就行
标头(负载)分析:
验证推理与猜测,得出结论
- 第一次加载页面时浏览器会发送第一个main?~请求,这个请求的负载中 pagination_str没有内容。
pagination_str: {"offset":""}
从请求到的数据中可以拿到用于后续请求的"session_id\"
- 此后,每当我们在页面往下滑一定高度(可能是根据浏览器高度而定),浏览器就会发送一次main?~请求。在这些请求中
pagination_str:
完全一致pagination_str: {"offset":"{\"type\":1,\"direction\":1,\"session_id\":\"1759687722741971\",\"data\":{}}"}
- wts是只计算秒数的时间戳,不重要
- w_rid由 请求负载中除它自己以外的所有内容 生成,考虑到其余参数不变,因此wts是影响w_rid生成结果的唯一变量。
- w_rid在服务器和浏览器都可使用md5生成,加密方式一致因此生成结果一致。 pagination_str用于向服务器发送“给我下一页评论内容”的命令。其中
"session_id\"
最为关键。 - 服务器首次收到命令后生成并验证w_rid和wts发现这两个参数与服务器生成的“密码”匹配,返回页面初次刷新时出现的评论数据,后续只需反复使用同一个url(必须携带 首次命令返回的评论数据中的
"session_id\"
)就能不断请求获取新的评论内容。