快音视-视频id解密

一、背景

今天看到一篇破解js混淆的文章,里面针对快音视的视频id加密,做了分析破解(链接:https://www.jianshu.com/p/5c945834270d ),刚好闲着无事,也分析一波,记录下自己的采坑经历

二、过程

1、确定加密参数

抓包后发现数据接口:https://kuaiyinshi.com/api/kuai-shou/recommend/?callback=showData&_=1557698894077, 里面的video_url字段,即视频下载链接为:

该链接无法打开,查看页面展示的实际下载链接为:

尝试全局搜索short_id, play_id, data_id, video_id, 在搜索video_id时找到js文件

2、确定加密函数

全局搜索video_id,发现两个js链接

先看下public.js,相关代码如下
在这里插入图片描述
大致意思就是根据不同平台的链接,将video_id取出来,无加密函数
再看下main.js函数,该js文件进行了混淆,关键字都用数组__Ox27641通过下标获取,同时将十进制数字转为16进制,如
var sc = getQueryString(__Ox27641[0x9]);
var st = getQueryString(__Ox27641[0xa]);
var video_id = getQueryString(__Ox27641[0xb]);

搜索后发现很多video_id,一开始以为是var video_id = getQueryString(__Ox27641[0xb]);即getQueryString是加密参数,进入publish.js里查看getQueryString函数
在这里插入图片描述
打断点调试后,发现请求一次,会调用三次该函数,传入的那么分别为source, st,video_id,但是没有发现加密
重新看main.js,看到ab3d7fc(video_id, ky),代码如下

function ab3d7fc(_0x871ex15, _0x871ex16) {
    var _0x871ex17 = [];
    var _0x871ex18 = _0x871ex15[__Ox27641[0x1e]](__Ox27641[0x1d]);
    delete _0x871ex18[0x0];
    var _0x871ex19 = [];
    var _0x871ex1a = _0x871ex16[__Ox27641[0x1e]](__Ox27641[0xc]);
    _0x871ex1a[__Ox27641[0x17]](function(_0x871ex1b) {
        _0x871ex19[__Ox27641[0x20]](_0x871ex1b[__Ox27641[0x1f]]())
    });
    var _0x871ex1c = _0x871ex19[__Ox27641[0x21]];
    _0x871ex18[__Ox27641[0x17]](function(_0x871ex1d, _0x871ex1e) {
        _0x871ex17[__Ox27641[0x20]](parseInt(_0x871ex1d) - (0xFF & _0x871ex19[(_0x871ex1e - 1) % _0x871ex1c]))
    });
    var _0x871ex1f = __Ox27641[0xc];
    _0x871ex17[__Ox27641[0x17]](function(_0x871ex13) {
        _0x871ex1f += String[__Ox27641[0x16]](_0x871ex13)
    });
    return _0x871ex1f
}

该函数出现了好几次,然后尝试打断点,发现该函数传入加密后video_id值,返回解密后的video_id,确定该函数为加密函数

3、处理js函数

(1)传入参数确定:
第一个参数为video_id, 第二个参数: ky,在console中输入ky,获得该值的数据:‘A0MjZfMTY0NDA3Mj’,固定值
(2)解析函数
第一步:将所有16进制数字转为10进制:

function ab3d7fc(_0x871ex15, _0x871ex16) {
    var _0x871ex17 = [];
    var _0x871ex18 = _0x871ex15[__Ox27641[30]](__Ox27641[29]);
    delete _0x871ex18[0];
    var _0x871ex19 = [];
    var _0x871ex1a = _0x871ex16[__Ox27641[30]](__Ox27641[12]);
    _0x871ex1a[__Ox27641[23]](function(_0x871ex1b) {
        _0x871ex19[__Ox27641[32]](_0x871ex1b[__Ox27641[31]]())
    });
    var _0x871ex1c = _0x871ex19[__Ox27641[33]];
    _0x871ex18[__Ox27641[23]](function(_0x871ex1d, _0x871ex1e) {
        _0x871ex17[__Ox27641[32]](parseInt(_0x871ex1d) - (255 & _0x871ex19[(_0x871ex1e - 1) % _0x871ex1c]))
    });
    var _0x871ex1f = __Ox27641[12];
    _0x871ex17[__Ox27641[23]](function(_0x871ex13) {
        _0x871ex1f += String[__Ox27641[22]](_0x871ex13)
    });
    return _0x871ex1f
}

第二步:根据__Ox27641数组的值及下标,替换关键字,同时使用短变量名重新命名变量,方便查看

function ab3d7fc(video_id, ky) {
    var aa = [];
    var bb = video_id.split(':');
    delete bb[0];
    var cc = [];
    var dd = ky.split("");
    dd.forEach(function(ff) {
        cc.push(ff.charCodeAt())
    });
    var xx = cc.length;

    bb.forEach(function(val, key) {
        aa.push(parseInt(val) - (255 & cc[(key - 1) % xx]))
    });
    var hh = '';
    aa.forEach(function(pp) {
        hh += String.fromCharCode(pp)
    });
    return hh
}

执行到这一步,就面临两个选择,第一个是直接执行该js获取数据,方便简单,但效率慢,第二个就是用python重写该函数
第三步:执行js函数或Python实现该函数
 方法一:执行js:选择js2py库,效率相对于PyV8和pyExecJS,效率更高,但要注意js2py执行复杂js代码会出错,所以要先试着能否js2py库正常执行,不行可以换成上面的两个库

import js2py
context = js2py.EvalJs()
context.execute(js)
video_id = context.ab3d7fc(':131:125:183:171:210:181:145:149:141:125:184:153:184:130:145:191:185:125:199:188:192:179:183:157:138:126:162:149:184:128:145:191:118:136:199:205:141:180:161:157:142:127:146:153:115:130:161:180:167:125:163:162:212:197:181:184:140:143:144:169:167:105:178:157:118:100:178:163:142:158:131:184:144:101:134:169:114:153:130:155:167:98:130:206:192:157:175:182:146:98:130', 'A0MjZfMTY0NDA3Mj')
    print(video_id)

 方法二:Python重写该函数

def get_video_id(video_id, ky='A0MjZfMTY0NDA3Mj'):
  aa = []
  bb = video_id.split(':')
  del bb[0]
  print(bb)
  cc = []
  for d in ky:
    cc.append(ord(d))
  print(cc)
  xx = len(cc)
  for key, val in enumerate(bb):
    aa.append(int(val)- (255&cc[(key)%xx]))
  print(aa)
  hh = ''
  for a in aa:
    hh+= chr(a)
  return hh

至此,完成该video_id的破解

三、知识点&踩坑

js混淆:关键字都用数组__Ox27641通过下标获取,同时将十进制数字转为16进制

int(), 第一个参数是字符串  ,第二个参数是说明,这个字符串是几进制的数, 转化的结果是一个十进制数
    2进制转10进制:int('10100111110',2)  ==> 1342
    16进制转10进制: int('0x10', 16)  ==>  16
    8进制转10进制:int('17',8) ==> 15
oct(), 将 任意进制的数 转换成 8进制的
    10进制转8进制: oct(11) ==> '013'
    16进制转8进制: oct(0xf)  ==> '017'
    2进制转8进制:   oct(0b1010) == > '012'
hex(), 10进制转16进制
    10进制转16进制: hex(16)  ==>  0x10
    8进制转16进制: hex(int('17',8)) ==> '0xf'
    2进制转16进制: hex(int('101010',2)) ==> '0x2a'
bin(), 10进制转2进制
    10进制转2进制: bin(10) ==> '0b1010'
    16进制转2进制: bin(int('ff',16)) ==> '0b11111111'
    8进制转2进制: bin(int('17',8)) == > '0b1111'

2、js的forEach和Python的enumerate
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值