记录一次樱某动漫网逆向

前言

寒假闲来无事,无聊看看动漫,想着看樱某也有好几年了,就研究一下


\99999.png)

绕过无限debugger

随机点进一个视频,打开f12看到下图

可以看到这是一个无限debugger,它会阻止你看他代码不断的产生不可回收的对象,占据你的内存,造成内存泄漏,没过多久浏览器就会卡顿

这个debugger是从这个构造器这边进来的

绕过debugger的方式有很多种,如

  • 全局/局部禁用断点

  • fd中间人替换

  • 置空关键函数

​ 这里直接在debugger右键禁用局部断点就好,然后在上一个堆栈的409行以及411行打上断点,这个时候就可以成功绕过这个debugger了.....


接着因为是视频是通过ts文件传输的,所以先找一下m3u8 的地址

介绍一下m3u8文件

M3U8是一种用于播放网络媒体的文件格式,它是一种轻量级的播放列表文件,使用UTF-8编码。 M3U8文件通常包含一组媒体文件的URL,这些文件可以是音频或视频流。

M3U8文件通常用于实现流媒体播放,例如在线视频和音频。它们可以用来实现分段加载,这样可以在网络环境较差的情况下提高播放质量。

可以看到请求m3u8 的地址形如

https://www.xxxxx.me/tmm3p/index.m3u8?path=%2Ftmm3p%2Ffabf72a50836ffa29443abd5d187d1b4.m3u8&t=1674229096&dpvt=93013199994611210910010412146119119119

先下一个xhr断点,成功断下后分析调用堆栈,当前是在send

往下找到__getset_play这个栈

image

在第174行找到了m3u8的地址生成,接下来就是查看vurl是怎么构造出来的,

跟进__getplay_rev_data()函数发现如下内容,可以直接将其扣出就好~

function __getplay_rev_data(_in_data){
  if(_in_data.indexOf('{') < 0){
    ;var encode_version = 'jsjiami.com.v5', unthu = '__0xb5aef',  __0xb5aef=['wohHHQdR','dyXDlMOIw5M=','dA9wwoRS','U8K2w7FvETZ9csKtEFTCjQ==','wo7ChVE=','VRrDhMOnw6I=','wr5LwoQkKBbDkcKwwqk='];(function(_0x22b97e,_0x2474ca){var _0x5b074e=function(_0x5864d0){while(--_0x5864d0){_0x22b97e['push'](_0x22b97e['shift']());}};_0x5b074e(++_0x2474ca);}(__0xb5aef,0x1ae));var _0x2c0f=function(_0x19a33a,_0x9a1ebf){_0x19a33a=_0x19a33a-0x0;var _0x40a3ce=__0xb5aef[_0x19a33a];if(_0x2c0f['initialized']===undefined){(function(){var _0x4d044c=typeof window!=='undefined'?window:typeof process==='object'&&typeof require==='function'&&typeof global==='object'?global:this;var _0x1268d6='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x4d044c['atob']||(_0x4d044c['atob']=function(_0x2993de){var _0x467e1d=String(_0x2993de)['replace'](/=+$/,'');for(var _0x22a01d=0x0,_0x1ee2a1,_0x2cf5ea,_0x3a84f7=0x0,_0x5c0e64='';_0x2cf5ea=_0x467e1d['charAt'](_0x3a84f7++);~_0x2cf5ea&&(_0x1ee2a1=_0x22a01d%0x4?_0x1ee2a1*0x40+_0x2cf5ea:_0x2cf5ea,_0x22a01d++%0x4)?_0x5c0e64+=String['fromCharCode'](0xff&_0x1ee2a1>>(-0x2*_0x22a01d&0x6)):0x0){_0x2cf5ea=_0x1268d6['indexOf'](_0x2cf5ea);}return _0x5c0e64;});}());var _0x3c81da=function(_0x457f21,_0x6cb980){var _0x133a9b=[],_0x749ec5=0x0,_0x3ceeee,_0x1df5a4='',_0x35a2a6='';_0x457f21=atob(_0x457f21);for(var _0x9a0e47=0x0,_0x4a71aa=_0x457f21['length'];_0x9a0e47<_0x4a71aa;_0x9a0e47++){_0x35a2a6+='%'+('00'+_0x457f21['charCodeAt'](_0x9a0e47)['toString'](0x10))['slice'](-0x2);}_0x457f21=decodeURIComponent(_0x35a2a6);for(var _0x2ef02e=0x0;_0x2ef02e<0x100;_0x2ef02e++){_0x133a9b[_0x2ef02e]=_0x2ef02e;}for(_0x2ef02e=0x0;_0x2ef02e<0x100;_0x2ef02e++){_0x749ec5=(_0x749ec5+_0x133a9b[_0x2ef02e]+_0x6cb980['charCodeAt'](_0x2ef02e%_0x6cb980['length']))%0x100;_0x3ceeee=_0x133a9b[_0x2ef02e];_0x133a9b[_0x2ef02e]=_0x133a9b[_0x749ec5];_0x133a9b[_0x749ec5]=_0x3ceeee;}_0x2ef02e=0x0;_0x749ec5=0x0;for(var _0xa5d5ef=0x0;_0xa5d5ef<_0x457f21['length'];_0xa5d5ef++){_0x2ef02e=(_0x2ef02e+0x1)%0x100;_0x749ec5=(_0x749ec5+_0x133a9b[_0x2ef02e])%0x100;_0x3ceeee=_0x133a9b[_0x2ef02e];_0x133a9b[_0x2ef02e]=_0x133a9b[_0x749ec5];_0x133a9b[_0x749ec5]=_0x3ceeee;_0x1df5a4+=String['fromCharCode'](_0x457f21['charCodeAt'](_0xa5d5ef)^_0x133a9b[(_0x133a9b[_0x2ef02e]+_0x133a9b[_0x749ec5])%0x100]);}return _0x1df5a4;};_0x2c0f['rc4']=_0x3c81da;_0x2c0f['data']={};_0x2c0f['initialized']=!![];}var _0x4222af=_0x2c0f['data'][_0x19a33a];if(_0x4222af===undefined){if(_0x2c0f['once']===undefined){_0x2c0f['once']=!![];}_0x40a3ce=_0x2c0f['rc4'](_0x40a3ce,_0x9a1ebf);_0x2c0f['data'][_0x19a33a]=_0x40a3ce;}else{_0x40a3ce=_0x4222af;}return _0x40a3ce;};var panurl=_in_data;var hf_panurl='';const keyMP=0x100000;const panurl_len=panurl['length'];for(var i=0x0;i<panurl_len;i+=0x2){var mn=parseInt(panurl[i]+panurl[i+0x1],0x10);mn=(mn+keyMP-(panurl_len/0x2-0x1-i/0x2))%0x100;hf_panurl=String[_0x2c0f('0x0','1JYE')](mn)+hf_panurl;}_in_data=hf_panurl;;(function(_0x5be96b,_0x58d96a,_0x2d2c35){var _0x13ecbc={'luTaD':function _0x478551(_0x58d2f3,_0x3c17c5){return _0x58d2f3!==_0x3c17c5;},'dkPfD':function _0x52a07f(_0x5999d5,_0x5de375){return _0x5999d5===_0x5de375;},'NJDNu':function _0x386503(_0x39f385,_0x251b7b){return _0x39f385+_0x251b7b;},'mNqKE':'版本号,js会定期弹窗,还请支持我们的工作','GllzR':'删除版本号,js会定期弹窗'};_0x2d2c35='al';try{_0x2d2c35+=_0x2c0f('0x1','s^Zc');_0x58d96a=encode_version;if(!(_0x13ecbc[_0x2c0f('0x2','(fbB')](typeof _0x58d96a,_0x2c0f('0x3','*OI!'))&&_0x13ecbc[_0x2c0f('0x4','8iw%')](_0x58d96a,'jsjiami.com.v5'))){_0x5be96b[_0x2d2c35](_0x13ecbc[_0x2c0f('0x5','(fbB')]('删除',_0x13ecbc['mNqKE']));}}catch(_0x57623d){_0x5be96b[_0x2d2c35](_0x13ecbc[_0x2c0f('0x6','126j')]);}}(window));;encode_version = 'jsjiami.com.v5';

  }
  return _in_data;
}

然后_json_obj的来源于JSON.parse(_in_data),_in_data 是一个get请求成功时的回调参数,根据这个_getplay_url 的路径可以看到该请求地址形如:

www.xxxx.cc/_getplay?aid=23104&playindex=1&epindex=0&r=0.02312744250345955

接下来跟踪_getplay_url变量,看一下它这个地址是如何生成的,

可以看到其实_getplay_url来源于cb_getplay_url();

直接跟进cb_getplay_url扣下代码就好,cb_getplay_url函数如下图

Over~~

思路整理

第一次请求获得加密参数vurl-->通过vurl获得m3u8-->ts文件

此请求会返回vurl,经过测试还发现,多次请求vurl会为空,后端做了ip检测

GET /_getplay?aid=22350&playindex=1&epindex=0&r=0.27333462814440845 HTTP/1.1
Host: www.yhdmp.cc
sec-ch-ua: "Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
Referer: https://www.yhdmp.cc/vp/22269-1-0.html
Cookie: Hm_lvt_1e08261c27df359f682f0548bf78342c=1673847965,1673866113,1673873515,1673922180; qike123=%u9B54%u738B%u5B66%u9662%u7684%u4E0D%u9002%u5408%u8005%20%u7B2C%u4E8C%u5B63%20%u7B2C1%u96C6^https%3A//www.yhdmp.cc/vp/21208-1-0.html_$_%u6218%u6597%u5458%u6D3E%u9063%u4E2D%uFF01%20%u7B2C1%u96C6^https%3A//www.yhdmp.cc/vp/21162-2-0.html_$_%u81F4%u4E0D%u706D%u7684%u4F60%20%u7B2C%u4E8C%u5B63%20%u7B2C5%u96C6^https%3A//www.yhdmp.cc/vp/22111-1-4.html_$_%u81F4%u4E0D%u706D%u7684%u4F60%20%u7B2C%u4E8C%u5B63%20%u7B2C2%u96C6^https%3A//www.yhdmp.cc/vp/22111-1-1.html_$_%u4E00%u5FF5%u6C38%u6052%20%u7B2C1%u96C6^https%3A//www.yhdmp.cc/vp/20376-1-0.html_$_%u522B%u5F53%u6B27%u5C3C%u9171%u4E86%uFF01%20%u7B2C1%u96C6^https%3A//www.yhdmp.cc/vp/22350-1-0.html_$_%u98D9%u901F%u5B85%u7537%20%u7B2C%u4E94%u5B63%20%u7B2C5%u96C6^https%3A//www.yhdmp.cc/vp/22508-1-4.html_$_%u53C8%u9177%u53C8%u6709%u70B9%u5192%u5931%u7684%u7537%u5B69%u5B50%u4EEC%20%u7B2C1%u96C6^https%3A//www.yhdmp.cc/vp/22349-1-0.html_$_%u4E07%u4E8B%u5C4B%u658B%u85E4%u5230%u5F02%u4E16%u754C%20%u7B2C1%u96C6^https%3A//www.yhdmp.cc/vp/22269-1-0.html_$_|; Hm_lpvt_1e08261c27df359f682f0548bf78342c=1673925191; t1=1873927596691; k1=19497283; k2=2554949298976; t2=1873927622356

​ 数据包是否可以重发成功与Cookie 中的t1t2 有关,如果不修改t1, t2 直接重返数据包会显示 error:timeout ,测试发现将t1 的值改大,然后让t2 略大于t1 ,即可成功返回数据,正常返回的数据包如下, 可以看到它还套了cf

HTTP/1.1 200 OK
Date: Tue, 17 Jan 2023 04:59:56 GMT
Content-Type: text/plain; charset=utf-8
Connection: close
strict-transport-security: max-age=2592000; includeSubdomains; preload
CF-Cache-Status: DYNAMIC
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=L6%2F0yi%2FmtXJXYeRjbtqjeGS3JtZNmsWaA9dq3D8ASy1RCOFEpbItEQNXNQbL%2BGg0HG1Q5jV6sPLiDnJktMq5L4GziyiGrJ%2FzN6jhufQQNabjXyeoqoaKUITjQSTZeuk%3D"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 78ac8640ee697d56-LAX
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
Content-Length: 453

{"purl":"59878c8e3e485388807b514042877e713b7d6f82697376346a767a7a2f","vurl":"afb4b1afacb2abaeaca6b8a697e5a6a193a5e19ed797a09a9ac79cc6969397c58ec38d908c90ba8b868a8989b38ab1b47fae7cb0af828e797b7769b375aeadb3846f716d5f7d6b5c9ea995a378645567a3609959a28e8c958f6b564892548d8c92634e407f86468a84767a808a3f878685533e30503b2d48392a7773767568","playid":"\u003cplay\u003ePC-XXX\u003c/play\u003e","vurl_bak":"","purl_mp4":"/yxsf/player/ckx1/?url=","ex":"","inv":"0"}

ip检测

它后台还会检测当前IP,当响应完一次vurl后就不再响应,试了一下要更换IP才能继续获取数据;而且樱花dm还会检测发包频率,频率过高直接封IP可以考虑用代理池的方式解决