IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【web逆向】一次获取视频解析接口地址的分析 -> 正文阅读

[JavaScript知识库]【web逆向】一次获取视频解析接口地址的分析

一、啰啰嗦嗦

本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,本文部分内容学习于网络,若有侵权,请联系我立即删除!

二、前言

现在越来越多的视频解析接口出现,于是就查了一下,找了一个排名靠前的分析一下,如下图:

三、目标

?四、无限debugger


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

?

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

Function.prototype.constructor_ = Function.prototype.constructor;
Function.prototype.constructor = function (a) {
if(a == "debugger") {
return function (){};
}
return Function.prototype.constructor_(a);
};

?五、分析

?

ok,我们干掉了烦人的无限debugger,我们抽取接口地址:

aHR0cHM6Ly9qeC5wYXJ3aXguY29tOjQ0MzMvcGxheWVyL2FuYWx5c2lzLnBocD92PWh0dHBzOi8vdi5xcS5jb20veC9jb3Zlci96cjVhNjdsMzMzZWh6dTkuaHRtbA==

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

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

?一个JM的视频地址,一个JM混淆的abc函数代码;

ok,我们来看看abc函数:

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");

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

['sojson.v4']+[])["constructor"]['fromCharCode']['apply'](null,"123456..."['split'](/[a-zA-Z]{1,}/))))['sojson.v4']

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

''.constructor.fromCharCode.apply(null,"123456...".split(/[a-zA-Z]{1,}/))

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

String.fromCharCode(“1”“2”...)

我们在控制台输出一下;

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

(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);
})

?

??六、OB混淆


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

?

?

'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')]();
    }

?

?

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

?

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

'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的排列方式函数,我们改写一下。
?

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解密,我们也改写一下

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"]);
}

?

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

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,至此播放地址拿到手了。

?七、结束。

这样详细解析就到这里了,本文部分内容参考借鉴与网络,其中如有侵权联系删。谢谢啦。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:03:16  更:2022-03-03 16:06:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:30:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码