ximalaya(三) playUriList值解密--webpack
本文主要介绍解密音频播放url参数。
本文仅代表个人理解,如有其他建议可在评论区沟通。
声明
仅仅记录一下自己的学习方法,不作为其他参考、更不作为商业用途。如有侵犯请联系本人删除
目标地址:aHR0cHM6Ly93d3cueGltYWxheWEuY29tL3NvdW5kLzM3NTA4NDM2OQ==
前面两张分析了xm-sign的生成逻辑,
ximalaya 新xm-sign逆向思路-CSDN博客
ximalaya 新xm-sign逆向思路(二)-CSDN博客
下面主要讲解如何逆向解密url播放地址。
该文章是按照博主从0到1的思路,其中也包含了博主踩过的坑,供各位老板借鉴。
错误示范:
一、播放地址定位。
点击下一个之后,可以看到浏览器发起了如下请求,
其中就有一个媒体请求,打开发现是本章的mp3声音,下面需要分析这个媒体请求。
二、媒体请求分析
请求携带了sign值,大胆猜测这个sign值,是我们需要逆向的,可以赋值url生成request请求去严正下,如果sign的值改变了,请求会返回404报错。只有sign正确时,才会加载mp3声音。
三、sign值是从哪里生成的。
这里我们点开启动器,发现无法跟栈。那需要尝试关键字搜索。
将可疑位置全部打上断点后,切换下一章节,发现并没有断住,思路就此打断,放弃逆向
但是博主不信邪,困难像弹簧,你强它就弱,你若他就强!
正确示范:
一、分析媒体播放地址。
除了媒体请求外,这两个请求是不是有点可疑?其实逆向本身就有一定的偶然性,三分经验七分猜。
1.interval请求
发现没啥用
2.另一个请求
这可不得了,里面这么多信息,部分人可能直接放弃了,但是俗话说得好,你看到的信息越多,你看到的信息就越多!咱们逐级点开分析一下
看看,这是什么?playUrlList! 翻译成中文就是:
翻译成中文就是:播放列表url
至此逆向结束,大功告成!我们查看下playUrlList,答案近在咫尺。
这是什么东西!怎么全是密文
接下来有两个选择:
放弃 继续肝
二、放弃 继续肝
但是到这里已经感觉无从下手了,因为这个请求只有一个逆向值xm-sign,前面已经讲过,在和里面全是固定的,返回的数据中包含了加密的url地址。
https://pinoss.com/pdan123/i/pdan1/2021/10y/16/20211016a18.gif
遇到加密我们需要考虑下,服务器肯定会解密并且返回一个正确的url地址,供浏览器调用从而播放音频。结合前面两张分析xm-sign的步骤,前面最喜欢用的就是异步调用获取返回结果,这里百分之99.9999999也是使用了异步,进行url解密,解密后发起请求获取音频进行播放。
我们只需要先这样,再这样,再那样,然后这样,就解密成功啦!逆向结束。
1.跟栈分析,直接跟最后一个栈
加上断点后点下一首,然而并没有发现什么有用的信息,这时候肯定大多数人就放弃了,博主也放弃了,后来过了2、3天有人联系博主继续讲解,看着他们对知识的渴望,才又重新捡起来分析。
2、继续往下跟栈,发现还是些无用信息
这时候就需要我们反省下,是不是哪个步骤做错了?
重新分析了下,发现这个断点请求的url,并不是我们所需要的
我们需要跟栈的url是https://www.ximalaya.com/mobile-playpage/track/v3/baseInfo开头的,所以之前我们什么有用的信息也获取不到。
3.增加xhr断点,跟栈分析
增加xhr断点后重新请求下一首,断住后查看url,发现这才是我们需要分析的url。
继续往后看堆栈即可,看下哪个环节有体现有用的信息
一直跟到e这个栈,发现代码就比较可疑了,逆向的时候,对于不熟悉的代码要抱着可疑的态度去阅读。下面主要分析e这个堆栈
三、e堆栈分析
可疑处打上断点,重新请求,进行分析B(Qt({....})),控制台输入后发现是异步的,加一个then,再跟个箭头函数,然后放开断点,查看下异步执行结果。
怎么样,牛不牛,牛的给我喊声666,顺便评论收藏+关注支持下老弟。
成功获取到了这个目标的响应值。
接下来分析then之后的函数。
t.trackInfo就是响应结果中的trackInfo
playUrlList
继续往下加断点分析,发现src就是最后服务器解密后的播放地址。复制粘贴验证一下
四、参数解密分析
1.还原解密部分js代码
2.补getPlayUrl
执行后报错,提示 getPlayUrl is not defined,扣JS代码即可。补上getPlayUrl函数,接着执行
3.补decryptFn函数
去掉try catch之后,继续执行强行抛出异常。
4.补getSoundCryptLink
这里需要着重讲一下,博主之前硬扣的getSoundCryptLink 函数,最后补全之后执行发现返回一串乱码。这里感兴趣的同学可以自己尝试扣一下,博主觉得麻烦没再继续往下扣了就。
1.点进去getSoundCryptLink 函数。
将这段包含getSoundCryptLink函数的父级节点一并阅读下,很像webpack结构,是使用【】数组方式调用的。所以博主从webpack入手。在该断函数结尾打上断点,找到加载器。
加上断点后,刷新页面,跟进去r函数,r函数就是加载器
2.定义1个全局变量loader
loader用于接收加载器。
然后将这个页面替换文本,调整其中的代码。CTRL+S保存后重新刷新页面。
3.定位getSoundCryptLink函数所在模块的索引位置。
控制台执行以下代码,f(t.replace是该模块中所包含的内容,需要保证全局唯一,可以使用全局搜索确认下
所以该函数所在的模块索引是206,将加载器代码都复制到本地,并且将数组[]改成{}格式。执行代码查看报错
补window环境后执行。查看报错
执行后提示getSoundCryptLink 找不到。现在我们知道getSoundCryptLink 函数所在模块是索引206的位置。直接将该代码复制下来,补到模块中。
4.补模块206
控制台输入loader.m[206],点击结果定位目标位置后,复制该段代码。然后我们还需要定义一个变量接收206模块中的t.getSoundCryptLink函数
直接用window.getSoundCryptLink接收即可。
执行后报错如下:直接补全,window.getSoundCryptLink,然后再次执行。
发现这次报错还是没变化,还是找不到getSoundCryptLink函数。
原因是因为我们只补了加载器和模块,还没有调用模块,所以拿不到206模块中的getSoundCryptLink函数。只需要在代码中调用206模块即可
5.调用206模块
添加上代码
loader(206),然后执行代码,发现报错变了,提示缺少535模块。
后面的按提示补模块即可,一共有7个模块。
其中补索引5模块的时候会有报错如下:
直接在开发者工具源代码中查看 award_17d4b是啥,然后补到window环境下面就行。
5.补全后执行代码,得到mp3播放地址
总结:
喜马拉雅使用了大量的异步请求,将异步执行的结果用于发起新的请求。
本文主要就是异步调用目标链接,拿到响应值后,对响应值的trackInfo--playUrlList中的数据进行解密,从而得到一个新的播放地址。如果想下载音频文件的话,使用python的request库下载即可。不再做详细描述。
webpack源码不放了,太多了。只放扣的JS函数源码,相信你也可以自己做出来。欢迎评论区讨论补充。
loader(206)//加载器调用206模块
function decryptFn(e) {
// try {
return (0,
window.getSoundCryptLink)({
deviceType: "www2",
link: e
})
// } catch (e) {
// return console.error(e, "new sound Link occur error"),
// ""
// }
}
function getPlayUrl(t) {
var e, r = this, n = 1;
return this.mediaType && t.some((function (t) {
return t.type.indexOf(r.mediaType) >= 0 && (e = t.url,
!0)
}
)),
e || (e = t[0].url),
t && t.length && (n = t[0].qualityLevel),
{
qualityLevel: n,
encodeText: e
}
}
function getsrc(trackInfo) {
var n = trackInfo
, a = n.playUrlList
, s = n.type;
var u = getPlayUrl(a)
, l = u.encodeText
, c = u.qualityLevel;
// src = decryptFn(l) 需要处理换行符
src = decryptFn(l).replace(/\n/g, '');
console.log(src)
}
trackInfo ={
"trackId": 375093753,
"title": "第二十九章 重要的日子",
"categoryId": 3,
"categoryName": "有声书",
"intro": "",
"headSkip": 0,
"tailSkip": 0,
"paidType": 0,
"processState": 2,
"createdAt": 1610718355000,
"coverSmall": "http://imagev2.xmcdn.com/storages/66a0-audiofreehighqps/B2/6B/CMCoOR4D11VfAABKVAB8EFBN.jpg!op_type=3&columns=100&rows=100",
"coverMiddle": "http://imagev2.xmcdn.com/storages/66a0-audiofreehighqps/B2/6B/CMCoOR4D11VfAABKVAB8EFBN.jpg!op_type=3&columns=180&rows=180",
"coverLarge": "http://imagev2.xmcdn.com/storages/66a0-audiofreehighqps/B2/6B/CMCoOR4D11VfAABKVAB8EFBN.jpg!op_type=3&columns=1000&rows=1000",
"videoCover": "",
"uid": 162260752,
"nickname": "原点儿",
"isLike": false,
"isPublic": true,
"likes": 17,
"comments": 19,
"shares": 1,
"userSource": 1,
"status": 1,
"duration": 499,
"sampleDuration": 0,
"isPaid": false,
"isFree": false,
"isAuthorized": true,
"isVideo": false,
"isDraft": false,
"isRichAudio": false,
"isAntiLeech": false,
"vipFirstStatus": 0,
"ximiFirstStatus": 0,
"playUrlList": [
{
"huaweiSound": false,
"type": "M4A_64",
"fileSize": 4047211,
"sampleSize": 0,
"url": "Fils1uhGGP6hvq1Ejr-FabguUfIhWc-DnarBSIq6soVXJDVMJMeIq1lBF12KDMmjzEiz7OPIy0dlrEK07r5b1W911_ks5xu2N5XcernanGrRm1Edhn5-8fqIqvYJX_DFY85xjgR_3bYrubrNb78N8jf6bqBLjj_tAGxhgqQHZDXgBVX1zWbXgQP3F80jEzh3GuUUrumOWUour4h2At8g0PMF3dHJLShb2IHimXuavcqg5BQdQo5McFV2rzGR5xdLtZmWyCQZjdn-6_A2tgsEpqXPX4WUFqS0AAkupa4E300",
"qualityLevel": 1,
"uploadId": 10539947180,
"width": 0,
"height": 0,
"version": 1
},
{
"huaweiSound": false,
"type": "MP3_64",
"fileSize": 4004964,
"sampleSize": 0,
"url": "xqMQcqR0FtShvq1EjkStN9zWpd5ZfxvRnarBSIoi7TgeYzMsW9PKwllBF12KNsYdmCtVLjfRvQdlrEK07qTeiJUiLQQZOc-aK5Buerk14FSjfKWq9eyaEvqIqg8JFwpPOiI_dVtRspl-uYlKIwyeTuAJdvQq9ZxBLmzAgqTzOHZp4rPharNjzQP3F80j8WSJB6sxMSNDONkur4h2Ah6w_WPilRsrWh912IHimXtWnrEgzzH0fkPFflV2rzGRa87jnvA_Qlvrxkr-6_A2tnU3TvhBhGtqRBRWAAkupa4FUR8",
"qualityLevel": 1,
"uploadId": 10539947180,
"width": 0,
"height": 0,
"version": 1
},
{
"huaweiSound": false,
"type": "M4A_24",
"fileSize": 1547815,
"sampleSize": 0,
"url": "BczWdNSHOMWhvq1Ejr9lxRFDl20bWN_7narBSIpcYvNwXLctmSjxE1lBF12KDAEbxXSduV0unmllrEK07r7cfNUf2c9Fmvp9GYDcernaJfoFz5fcPAHSivqIqkdGum4gJ563QTwwv30Z7LGaZjbAfcKBm2TxwK2OGGFJgqQHI26ok1mz_SVLNgP3F80jE4i_muZhoDZVFlsur4h2At-r3L-TdO51lslK2IHimXuaXkTxfGHc7VX_GVV2rzGR5_436MZzFJlJ_HX-6_A2tgubtDtaw4KNzpgKAAkupa4Eo5c",
"qualityLevel": 0,
"uploadId": 10539947180,
"width": 0,
"height": 0,
"version": 1
},
{
"huaweiSound": false,
"type": "MP3_32",
"fileSize": 2006075,
"sampleSize": 0,
"url": "sdIP9VSDhzmhvq1EjkSJeU6z7PP7KchVnarBSIryktJrsWDrc8VhB1lBF12KNkAL_8jIsWNLw6VlrEK07qQIRr5Xop8ji_zcKx_cerk10uVk_uyAZiezMfqIqjBZsSlra8b45p9C3YDrt8p9c_GMU3eB9Ym5fVkw1TL4gqTzJ4oDgIfWf0RYaAP3F80j8ZCp0lH1jBkLP_our4h2Ah48IrqAAI6O6dsf2IHimXtWeDTKn_VrewuRpVV2rzGRa-lfy7uiQ3MM-uD-6_A2tnUivbcLQ8g_pajDAAkupa4FGYc",
"qualityLevel": 0,
"uploadId": 10539947180,
"width": 0,
"height": 0,
"version": 1
},
{
"huaweiSound": false,
"type": "AAC_24",
"fileSize": 1999517,
"sampleSize": 0,
"url": "biq_EoexXXWhvq1Ejr9poc-QIN8ukCKBnarBSIq6eUgJudAZr3dyvVlBF12KDGN8jh45atADTkZlrEK07r5IcsOkU6ts-_jJniOKerna8-6-k-CyAaNjWfqIqupUQOjB-QIk-YI56UvrgcohoVaj1qRc31LdY7LH6hz4gqQHQjxaz0ozNR-agwP3F80jEzo_av8ekOVXiWIur4h2At_FXsjPO0OUeArU2IHimXua_9Y_4h7woldO01V2rzGR5xMB7mr9jq_GIcX-6_A2tgu4kcc6aZrpYItEAAkupa4EVDw",
"qualityLevel": 0,
"uploadId": 10539947180,
"width": 0,
"height": 0,
"version": 1
}
],
"childAlbumInWhiteList": false,
"recommendSkip": {
"recommendHeadSkip": 15000,
"recommendHeadSkipCount": 2905,
"recommendHeadTail": 10000,
"recommendHeadTailCount": 1312
},
"isEnjoying": false,
"offlineVisibleType": 0,
"hasShqAuthorized": false,
"isXimiUhqTrack": false,
"isXimiUhqAuthorized": false,
"playtimes": 18930
}
getsrc(trackInfo)