手撕某解析网站,获取真实播放地址(保姆级讲解)(转)


原贴地址:手撕某解析网站,获取真实播放地址(保姆级讲解) - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 文章的网址做了脱敏处理,笔者用了BASE64编码,请自行解码。

文章里面的Fiddler使用了编程猫专用插件,请自行百度下载。

一、声明

本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!

二、前言

最近追上了电视剧,为了方便自己,准备去抓几个接口,于是就度娘一下,除了广告还有这个么多,很好,那我们就拿排名第一的分析分析,文才不佳,还请各位将就着
<ignore_js_op>

三、目标

主页:dHYuaHp3ZGQuY24=

接口:aHR0cHM6Ly9qeC5wYXJ3aXguY29tOjQ0MzMvcGxheWVyL2FuYWx5c2lzLnBocD92PWh0dHBzOi8vdi5xcS5jb20veC9jb3Zlci96cjVhNjdsMzMzZWh6dTkuaHRtbA==

四、无限debugger

F12打开控制台后,会进入一个无限debugger,为了我们以后的调试,我们不得不将它干掉

<ignore_js_op>

我们回溯到这里,发现调用 setInterval 方法,该方法可按照指定的周期(以毫秒计)来调用函数或计算表达式,
很明显,它调用了check,注意check函数,是将debugger 传递给了构造方法constructor,所以这里hook掉constructor

<ignore_js_op>

我们打开fiddler,开启hook代码如下

[JavaScript]  纯文本查看 复制代码
?
1
2
3
4
5
6
7
Function.prototype.constructor_ = Function.prototype.constructor;
Function.prototype.constructor =  function  (a) {
if (a ==  "debugger" ) {
return  function  (){};
}
return  Function.prototype.constructor_(a);
};



<ignore_js_op>

五、抓包分析
ok,我们干掉了烦人的无限debugger,我们抽取接口地址:

aHR0cHM6Ly9qeC5wYXJ3aXguY29tOjQ0MzMvcGxheWVyL2FuYWx5c2lzLnBocD92PWh0dHBzOi8vdi5xcS5jb20veC9jb3Zlci96cjVhNjdsMzMzZWh6dTkuaHRtbA==
重新加载一篇,很明显视频可以正常播放,而且我们的目标播放地址也能看到了

<ignore_js_op>

接下来我们分析播放地址是怎么来的,我们用接口地址访问,整个页面非常简单,我们看看接口地址返回了什么

<ignore_js_op>


一个加密的视频地址,一个加密混淆的abc函数代码


ok,我们来看看abc函数

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function  abc(){
         [ "sojson.v4" ][ "\x66\x69\x6c\x74\x65\x72" ][ "\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72" ]((([ "sojson.v4" ]+[])[ "\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72" ]
 
[ "\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65" ][ "\x61\x70\x70\x6c\x79" ]
 
( null , "118M97i114L32C95D112e114M32G61l32C36A40O39B109e101w116m97N91Y110y97I109I101J61q34E118i105N101h119R112I111G114T116z34Z93o39m41o46S97o116b116T114C40Q39r105I100m39O41f46U1
 
14P101B112t108d97g99M101X40Z39P118u111s100A95O39A44e39g39J41Y59l10b32j32e32e32l32Q32n32f32t32a118D97w114r32U95V112F117z32K61r32u36G40g39d109N101d116S97V91d99M104m97q114M115y10
 
1f116h61K34w85c84X70z45j56J34p93O39e41V46G97P116a116d114P40O39i105B100S39x41s46l114Y101I112E108w97m99h101q40y39Q118Y111A100D95p39V44X39z39a41k59d10l32M32v32d32n32C32G32G32y32Y
 
118K97O114u32v95f112e117z65W114w114y32i32f61T32Y91i93E59x10x32v32T32e32F32b32U32r32a32i118t97z114A32W95U110r101v119r65O114i114Q32O61b32E91L93N59u10R32u32A32c32y32R32I32o32Y32W
 
118A97L114D32l95E99E111W100P101N32D32N32o61n32d39r39Q59g10e32K32g32w32X32k32b32j32p32A10r32X32X32r32U32D32j32q32F32v102U111D114X40g118e97v114V32B105N61c48U59v105z60L32m95z112Z
 
117S46y108a101b110K103a116h104H59O32A105Z43o43m41Y123Q10e32c32v32B32G32E32J32a32W32t32O32J32s32j10C32T32K32A32I32A32h32Z32V32J32T32b32C32A95X112e117z65C114t114M46Z112J117P115l
 
104d40w123z32e39j105C100S39Q58a95S112I117K91j105n93t44k32P39w116Q101p120K116x39p58p32R95z112D114A91S105Z93U32x125Q41J59i10H32c32l32G32D32r32A32R32L32G125F10Q32h32M32W32j32E32Y
 
32m32J32P10L32v32y32A32C32t32U32B32F32B47D47G23545R23494p38053e37325J26032k36827g34892u25490S24207C10x32l32G32B32d32m32E32i32u32i95d110c101L119a65o114y114k32C61p32k95Y112l117v
 
65w114h114i46w115f111r114A116X40T80Z65Q82U46v99c111s109f112g97f114p101g40q34x105x100U34i41e41i59J10a32U32m32y32y32X32s32K32W32P10J32V32H32w32E32x32U32Q32s32M102B111V114Y40j118
 
e97h114K32v105L61J48h59d105G60A32g95B110Z101F119J65v114x114v46B108c101j110g103W116R104F59f32a105p43b43f41b123o10Q32Z32I32J32e32A32m32k32r32p32b32Q32W32G10P32J32H32T32F32V32U32
 
V32g32W32p32A32n32W95q99w111Y100k101e43b61C95A110p101v119I65k114F114V91d105G93N91D39y116d101X120u116H39t93a59b10h32Z32D32E32F32S32K32D32a32l125J10O32P32n32b32c32K32u32S32b32u9
 
9t111x110K102Q105h103D46T117p114L108l32q61u32t32H80X65I82r46Q115H101x99D114Y101a116V40C99c111I110h102K105f103X46Q117N114w108E44f32f95U99b111W100R101a44L32u116U114P117q101I41w5
 
9" [ "\x73\x70\x6c\x69\x74" ](/[a-zA-Z]{1,}/))))( "sojson.v4" );



我们先把字符串编码解决一下,大概就是这个样子

[JavaScript]  纯文本查看 复制代码
?
1
[ 'sojson.v4' ]+[])[ "constructor" ][ 'fromCharCode' ][ 'apply' ]( null , "123456..." [ 'split' ](/[a-zA-Z]{1,}/))))[ 'sojson.v4' ]


根据个人理解,上面几乎可以等于以下代码

[JavaScript]  纯文本查看 复制代码
?
1
'' .constructor.fromCharCode.apply( null , "123456..." .split(/[a-zA-Z]{1,}/))


大概意思就是把"123456..."转换成字符数组“1”“2”...,然后用fromCharCode方法转换成字符串

[JavaScript]  纯文本查看 复制代码
?
1
String.fromCharCode(“1”“2”...)


我们在控制台输出一下

<ignore_js_op>

ok,我们得到如下字符串代码

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
( function  anonymous(
) {
var  _pr = $( 'meta[name="viewport"]' ).attr( 'id' ).replace( 'vod_' , '' );
          var  _pu = $( 'meta[charset="UTF-8"]' ).attr( 'id' ).replace( 'vod_' , '' );
          var  _puArr  = [];
          var  _newArr = [];
          var  _code   =  '' ;
          
          for ( var  i=0;i< _pu.length; i++){
              
              _puArr.push({  'id' :_pu[i],  'text' : _pr[i] });
          }
          
          //对密钥重新进行排序
          _newArr = _puArr.sort(PAR.compare( "id" ));
          
          for ( var  i=0;i< _newArr.length; i++){
              
              _code+=_newArr[i][ 'text' ];
          }
          config.url =  PAR.secret(config.url, _code,  true );
})



我们分析分析,获取接口地址返回pr和pu,然后组装成puArr数组,对puArr通过PAR.compare方法按照id大小重新排序,
然后得到_code,然后通过PAR.secret方法,传入页面加密的url和_code和true
得到一个新的url,这里,我们大胆一点,新的url应该就是真实播放地址

<ignore_js_op>

ok,流程理清楚了,现在关键的就是PAR.compare和PAR.secret这2个函数了

我们全文模糊搜索PAR  PAR:PAR= var PAR等关键字,很快就定位到尾号为676f.js里
<ignore_js_op>



五、OB混淆
我们不难发现,整个尾号为676f.js是一个混淆代码,我们拷贝下来,分析分析

<ignore_js_op>


开头定义了一个大数组,然后对这个大数组里的内容进行位移,再定义一个解密函数。后面大部分的值都调用了这个解密函数,以达到混淆的效果。
这个就是传说中的OB混淆我们先看看PAR.compare和PAR.secret

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
'compare' function (_0x209a13) {
         var  _0x2b0a5b = {
             'hupwx' function (_0x28592c, _0x5c0aae) {
                 return  _0x28592c - _0x5c0aae;
             }
         };
         return  function (_0x4490c6, _0x47bde8) {
             var  _0x11d139 = _0x4490c6[_0x209a13];
             var  _0x2b383f = _0x47bde8[_0x209a13];
             return  _0x2b0a5b[_0x15b4( '6e' 'yYg5' )](_0x11d139, _0x2b383f);
         }
 
 
'secret' function (_0x141a71, _0x433196, _0x45cf51) {
         var  _0x20b4ff = {
             'bCQqZ' : _0x15b4( '6f' 'xfLW' ),
             'hAcOs' : _0x15b4( '70' 'wHsA' )
         };
         _0x433196 = CryptoJS[_0x15b4( '71' 'p5&!' )](_0x433196)[ 'toString' ]();
         var  _0x4f0cf6 = CryptoJS[ 'enc' ][_0x15b4( '72' 'a]Oa' )][_0x15b4( '73' 'Knq)' )](_0x433196[ 'substring' ](0x0, 0x10));
         var  _0x1030c4 = CryptoJS[ 'enc' ][_0x15b4( '74' 'eKXt' )][ 'parse' ](_0x433196[ 'substring' ](0x10));
         if  (_0x45cf51) {
             if  (_0x20b4ff[_0x15b4( '75' 'eKXt' )] !==  'vLPSI' ) {
                 return  CryptoJS[_0x15b4( '76' 'AIBX' )][_0x15b4( '77' 'tzTY' )](_0x141a71, _0x1030c4, {
                     'iv' : _0x4f0cf6,
                     'padding' : CryptoJS[_0x15b4( '78' 'I0@(' )][ 'Pkcs7' ]
                 })[ 'toString' ](CryptoJS[ 'enc' ][_0x15b4( '79' 'vNOA' )]);
             else  {
                 PAR[_0x15b4( '7a' 'wHsA' )][ 'post_r' ](a, b, c, d, _0x20b4ff[_0x15b4( '7b' 'xB(L' )]);
             }
         }
         return  CryptoJS[_0x15b4( '7c' 'bVZ)' )][_0x15b4( '7d' 'Bb3L' )](_0x141a71, _0x1030c4, {
             'iv' : _0x4f0cf6,
             'mode' : CryptoJS[_0x15b4( '7e' 'vzyU' )][_0x15b4( '7f' 'Ya)8' )],
             'padding' : CryptoJS[_0x15b4( '80' 'jX$g' )][_0x15b4( '81' 'xB(L' )]
         })[_0x15b4( '82' 'OZnY' )]();
     }



看样子混淆程度不高,一些关键字眼我们还是可以认出来,比如说CryptoJS,我们可以选择动态调试硬钢
不过,我还是选择把它还原一下,本地慢慢分析。
我们刚讲了,尾号为676f.js为标准的OB混淆样式,解密函数很明显就是_0x15b4,更何况PAR.compare和
PAR.secret多处调用,我们只需要把尾号为676f.js的前三段提取出来运行一遍,就能得到类似于_0x15b4('6e', 'yYg5')的值
这里可以选择硬钢替换,也可以选着用AST还原

<ignore_js_op>

不过需要注意的是这里有个暗坑,此段代码会检测你是否格式化,如果格式化了,就会内存爆破,进去无限循环,直到浏览器崩溃,有兴趣的同学可以试试

<ignore_js_op>

<ignore_js_op>

简单还原一下,然后PAR.compare和PAR.secret就成了这个样子

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
'compare' function  (_0x209a13) {
     var  _0x2b0a5b = {
       'hupwx' function  (_0x28592c, _0x5c0aae) {
         return  _0x28592c - _0x5c0aae;
       }
     };
     return  function  (_0x4490c6, _0x47bde8) {
       var  _0x11d139 = _0x4490c6[_0x209a13];
       var  _0x2b383f = _0x47bde8[_0x209a13];
       return  _0x2b0a5b[ "hupwx" ](_0x11d139, _0x2b383f);
     };
   },
   'secret' function  (_0x141a71, _0x433196, _0x45cf51) {
     var  _0x20b4ff = {
       'bCQqZ' "\u8D4C\u535A\u8BC8\u9A97" ,
       'hAcOs' "LSEHx"
     };
     _0x433196 = CryptoJS[ "MD5" ](_0x433196)[ 'toString' ]();
 
     var  _0x4f0cf6 = CryptoJS[ 'enc' ][ "Utf8" ][ "parse" ](_0x433196[ 'substring' ](0x0, 0x10));
 
     var  _0x1030c4 = CryptoJS[ 'enc' ][ "Utf8" ][ 'parse' ](_0x433196[ 'substring' ](0x10));
 
     if  (_0x45cf51) {
       if  ( "LSEHx"  !==  'vLPSI' ) {
         return  CryptoJS[ "AES" ][ "decrypt" ](_0x141a71, _0x1030c4, {
           'iv' : _0x4f0cf6,
           'padding' : CryptoJS[ "pad" ][ 'Pkcs7' ]
         })[ 'toString' ](CryptoJS[ 'enc' ][ "Utf8" ]);
       else  {
         PAR[ "danmu" ][ 'post_r' ](a, b, c, d,  "\u8D4C\u535A\u8BC8\u9A97" );
       }
     }
 
     return  CryptoJS[ "AES" ][ "encrypt" ](_0x141a71, _0x1030c4, {
       'iv' : _0x4f0cf6,
       'mode' : CryptoJS[ "mode" ][ "CBC" ],
       'padding' : CryptoJS[ "pad" ][ "Pkcs7" ]
     })[ "toString" ]();
   },



先看PAR.compare,我们发现就是sort的排列方式函数,我们改写一下

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
function  compare(d){
   function  hupwx(a, b) {
       return  a - b;
   }
   
   return  function  (a1, b1) {
     var  a = a1[d];
     var  b = b1[d];
     return  hupwx(a, b);
   };
}



至于PAR.secret,细心的童鞋已经发现他是调用加解密库(crypto-js)进行的一个AES解密,我们也改写一下

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
function  secret (url, _bcode) {
       
     code = CryptoJS[ "MD5" ](_bcode)[ 'toString' ]();
 
     var  _0x4f0cf6 = CryptoJS[ 'enc' ][ "Utf8" ][ "parse" ](code[ 'substring' ](0x0, 0x10));
 
     var  _0x1030c4 = CryptoJS[ 'enc' ][ "Utf8" ][ 'parse' ](code[ 'substring' ](0x10));
  
     return  CryptoJS[ "AES" ][ "decrypt" ](url, _0x1030c4, {
         'iv' : _0x4f0cf6,
         'padding' : CryptoJS[ "pad" ][ 'Pkcs7' ]
         })[ 'toString' ](CryptoJS[ 'enc' ][ "Utf8" ]);
}



几个重要的参数'iv','padding',key清新可见

我们来梳理一下,前面我们说了对puArr通过PAR.compare方法重新排序,然后得到_code,然后传入加密url和_code,
通过md5加密得到code,取code前16位得到IV,取后16位得到key,最后decrypt解密成明文url
因此我们改写一下abc函数,全局搜索CryptoJS,拿到尾号为cd51.js,稍微改写一下来验证一下(当然这里直接调用crypto-js加解密库也是一样)
<ignore_js_op>

需要注意,每次请求接口地址url和pr/pu都要变化,为了方便我们暂时先固定一下

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function  abc(){
     
            //var _pr = $('meta[name="viewport"]').attr('id').replace('vod_','');
            //var _pu = $('meta[charset="UTF-8"]').attr('id').replace('vod_','');
            var  _pr = "vod_dQb7shnQWf" .replace( 'vod_' , '' );
            var  _pu = "vod_5318769240" .replace( 'vod_' , '' );
 
            var  _puArr  = [];
            var  _newArr = [];
            var  _code   =  '' ;
            var  url= "a7GlmXWmVruOTAcF8VaB3idxA924haXMwKMPzu7dl0NDlQQVrqVTZwUvzFj8KRYjkthOjRXHwPtT86kzfj46WVnIA3DbSmBfQEE2ICd3OI2FBhOfcRauLNQ2kSxcrRlDOl8R6wY3q7tG6xBA0k
 
+Drs/iZFbc/llpLEyk06vOMkdt/3qxWxazor5E6mIvJ0PoieCvj9FJoooWECzXOiWgMBIX0EmKw5o4zWWRipLZTXdo69bbSFUJ1PphkS50snOkvdtzh1uKd2xTKqERLC
 
+iOTqRvBz8YtqjashI5MW0yhhkboK8StIK2QUIa0AACQBDJfhk3bxkmLyW8zdFBZa5iRfW5BNcq3DulkpKK8wY/+c40wesKygPXgBpgultR82yZmPCMHXrxDpxJDDywKXhYauH/iYyGO05V
 
+2pLMx6i9Biaq6XUPluh2VsMUe7GjWE1cKneEkmyJw4bMB23YY9Rr0RrV4gzoQ08xT+n2LeLsOKU4APkXuVEIKLXx+Wgo5SzpWqJL5N4MahNRpcetHavjoynwvCX1Mh59U4U67x5TKan8a9hO4209Qvc+0n
 
+fWxWjYZ0erlgTrxvlwU1EVQeRincA0jI0DF5tVtErYxs90=" ;
            for ( var  i=0;i< _pu.length; i++){
                
                _puArr.push({  'id' :_pu[i],  'text' : _pr[i] });
            }
            
            //对密钥重新进行排序
            
            
            _newArr = _puArr.sort(compare( "id" ));
            console.log(_newArr);
            for ( var  i=0;i< _newArr.length; i++){
                
                _code+=_newArr[i][ 'text' ];
            }
            
            url1 =  secret(url, _code);
            
            return  url1;
}



测试OK,至此播放地址拿到手了
<ignore_js_op>


以下附上js源码和简单的AST源码,如果对AST感兴趣,我们下次再聊

链接:https://pan.baidu.com/s/16ThFDjy9acw5eOH8LMaM5Q
提取码:52pj


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM