赞
踩
基于Crypto的python3 AES CBC pcks7padding 中文、英文、中英文混合加密
具体加密解密方式请自行百度或者谷歌,不做详细说明(因为实在是很麻烦~!)
安装方式 pip install pyCrypto
这个m3u8文件并不是一个视频,而是一个记录了视频流下载地址的文件,所以我们需要下载并打开这个文件,用文本的方式打开之后是这个样子的
这里涉及到了一些m3u8的基础,简单来说就是这个ts文件是经过加的,加密方式是method后面的值,偏移量是iv后面的值,这里加密方式比较奇葩,是第三方网站自写的加密方式,也就是文章开头提到的AES CBC方式加密的
然后我们通过浏览器断点,发现实例的代码在一个JS文件里,这个文件包含了该网站绝大多数的JS代码
通过断点会发现一个很有用的参数 DRMKey,然后我们会发现DRMKey这个参数很奇怪,它并不是常见的一种密钥,并且通过断点得知它是这个样子的
稍微有点基础的同学可能知道,在源代码里面的是16进制的,而这里面的是十进制的,所以我们需要用到进制转换,包括后面我们还要再次转成ascii码,代码我就直接贴在这里了
def get_asc_key(key): ''' 获取密钥,把16进制字节码转换成ascii码 :param key:从网页源代码中获取的16进制字节码 :return: ascii码格式的key ''' # 最简洁的写法 # asc_key = [chr(int(i,16)) for i in key.split(',')] # 通俗易懂的写法 key = key.split(',') asc_key = '' for i in key: i = int(i, 16) # 16进制转换成10进制 i = chr(i) # 10进制转换成ascii码 asc_key += i return asc_key
此时我们已经找到很关键的值了 asc_key 密钥
那么现在我们所需要的2个值就已经全部找到了,asc_key和iv
不过这个iv有点特殊,是32位的,所以我们需要进行切片取前16位,16位是固定位数,必须这么取
说一下我是怎么知道它是aes cbc加密的吧
逆向JS需要比较强的推测能力,既然ts文件中含有加密方式和偏移量,那么JS代码中肯定有加密的方法,因此我全局搜索的关键词就是aes decrypt ,然后发现pkcs7这种加密方式
然后查了一下pkcs7这个东西,发现它其实就是aes的一种加密方式,在已知加密方式,密钥和iv的情况下,就很好破解了,一下是完整的代码,比较简洁,爬取思路的话有一些变化,因为我发现随便打开一个视频都能获取到其他视频的标题和m3u8链接,所以我随机打开了一个免费视频的页面,并通过这个页面获取这个免费课程下所有的视频
# coding:utf-8 import os import re import requests from Crypto.Cipher import AES from lxml import etree class Spider(): def __init__(self): self.asc_key = '' def down_video(self, title, m3u8): ''' 通过m3u8文件获取ts文件 :param title:视频名称 :param m3u8: m3u8文件 :return: None ''' ts_files = re.findall(re.compile("\n(.*?.ts)"), m3u8) # ts文件下载路径 ivs = re.findall(re.compile("IV=(.*?)\n"), m3u8) # 偏移量 for index, (ts_file, iv) in enumerate(zip(ts_files, ivs)): ts_file = 'xxxx' + ts_file content = requests.get(ts_file, headers=headers).content iv = iv.replace('0x', '')[:16].encode() # 去掉前面的标志符,并切片取前16位 content = self.decrypt(content, self.asc_key, iv) # 解密视频流 open('video/%s/%s.ts' % (title, index), 'wb').write(content) # 保存视频 print('下载进度:%s/%s' % (index, len(ts_files))) print(title, '下载成功') def get_asc_key(self, key): ''' 获取密钥,把16进制字节码转换成ascii码 :param key:从网页源代码中获取的16进制字节码 :return: ascii码格式的key ''' # 最简洁的写法 # asc_key = [chr(int(i,16)) for i in key.split(',')] # 通俗易懂的写法 key = key.split(',') asc_key = '' for i in key: i = int(i, 16) # 16进制转换成10进制 i = chr(i) # 10进制转换成ascii码 asc_key += i return asc_key def makedirs(self, path): if not os.path.exists(path): os.makedirs(path) def decrypt(self, content, key, iv): cipher = AES.new(key, AES.MODE_CBC, iv) msg = cipher.decrypt(content) paddingLen = msg[len(msg) - 1] return msg[0:-paddingLen] if __name__ == '__main__': headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3676.400 QQBrowser/10.5.3738.400" } spider = Spider() spider.run()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。