polyv加密key解密过程定位教程

1176天前 · 分享 · 2889次阅读

前言

  • 主要内容

    • 通用加密key解密过程定位技巧
    • polyv视频解密key还原

目标地址 https://www.exueshi.com/play/p201903981228671dd33a_5

过程

关于视频加密与解密

通过观察m3u8内容可以知道分段视频加密使用的应该是AES加密

但是它所请求返回的key大小是32字节,虽然AES确实可以是32字节作为key的,但是这里根据通常的经验来说,它肯定不是32字节的key

而对于在线视频文件,加密方案通常就那么几种,模式一般是下面这样

  • 服务端视频切片,采用某种加密方法,整体解密或者部分加密,提供文件下载服务
  • 客户端请求key,请求视频,根据加密方法,使用key解密

这其中,加强视频保护,通常在下面几个环节入手

  • 加强key下发给客户端过程的安全性,例如本案例中就是,返回的key无法直接使用
  • 采用非常规加密算法,例如使用RC4
  • 采用非常规加密方案,例如只加密特定部分,或者自定义加密部分
  • 客户端在黑盒中完成解密过程,最终输出画面,例如widevine

解密过程定位

对于本案例,它只是对key进行了保护,所以只需要将其解密为真正的key即可

在此提供几种定位加密key解密思路

  • 字符串搜索大法,在含有相关字符的附近下断点分析,例如

    • aes
    • aes-128
    • aes-cbc/aes-ecb
    • expandkey
    • importkey
    • inittable
    • decrypt
    • key
    • #EXT-X-KEY
  • 基于加密算法源代码特定分析

    • 各类加密算法往往会用到某些特定的数字或命名,在这些算法关键位置断点
    • 如果停在了断点位置,那么就可以根据这个断点向前分析调用栈寻找解密过程
  • 异常定位法

    • 对加密key进行替换,使解密为真实key过程出现异常,根据异常位置进行分析
  • 关键点追踪

    • 追踪请求完成后,观察key的内容都被进行了哪些处理

关键点追踪

方案一:单步追踪

首先查看请求调用栈,然后在loadInternal这个位置下debugger,等待key的请求

但是发现m3u8的请求也会停在这里,稍微修改一下,只有请求key的时候停下

接着就跟着走...

然后你就发现走了半天,走到了readystatechange,而这个方法正好在loadInternal下面

这个时候呢,稍微细心一点,你就能发现这里有一点似曾相识

这里有一个名为callbacks.onSuccess的函数,而且有response之类的名字

那么很可能就是在这里,拿到返回结果,进行了处理

一步一步走,可以确认这里确实是key请求的返回,那么接着进入callbacks.onSuccess

这下是不是就更加眼熟了,没错就是之前分析的内容加解密过程用到的函数,而t.data正是加密key

方案二:根据XMLHttpRequest特点快速定位

搜索XMLHttpRequest + loadInternal,会发现官方文档没有什么信息,但是能找到一些和hls相关的

不过这也很正常,毕竟这里是在播放视频,那么可以推测这里js的请求相当于是继承自XMLHttpRequest

那么它们应该有一些相似之处

这个时候,站在开发者角度,在请求完key之后,肯定需要去处理它,那么我们可以搜索XMLHttpRequest + 回调找一些例子

比如mozilla的说明

也就是说赋值给onreadystatechange的就是回调函数

至于这里怎么绑定的,具体原理就不展开了(我也不会),那么这样一下就能定位到请求成功后的处理函数了

再往后就和上面一样了

这里再扩展一下,搜一下onSuccess,然后下断点,就能找到每种请求分别绑定什么函数了

这样,后面再遇到类似的情况,就可以迅速找到需要的关键函数

异常定位法

比如我们在返回结果是key的时候,把它变成另外一个值,这里直接在js源码中修改的

尝试去掉token重定向到本地似乎不行,似乎有跨域限制,当然还有其他替换key内容的手段,抓包软件、重定向什么的

这里是把它从32位改成33位0,结果直接在解密的地方抛出异常了~~~

然后我们在出现异常的位置前面下断点,然后等它停在那里,然后再往上看一下调用栈,是不是就是解密过程了

解密过程代码分析

首先是函数D,进入后发现this.h就是字符h,而o()实际上是之前的md5函数

简言之,这里就是取一个变量minSeekHole的值,乘以2,转字符串,求md5

然后是取md5结果的前16位转为一个数组

接着把key用函数H进行了转,跳入可以看到就是转成了一个Uint8Array数组

而下一步解密用的IV变量I实际上是一个固定数组

最终解密结果放到了this.decryptkey*.frag.decryptdata.key

但是,但是这里并没有完,如果记忆力足够好,应该能记得之前At.m.c是三个参数

最后一个参数传了一个!0也就是true,显然这肯定不太一样

如果觉得一样可以手动验证一下之前的aes代码解密对比一下结果,就可以发现不一样

那么进入这个函数进行分析

进来就发现是一个if判断,这里要提一点

if (A, B, C){xxx}这种形式的判断,实际上判断的是最后一个条件C,但是前面的AB的代码实际上也会执行,具体参见下面的例子

演示代码

if(false){console.log('999')}
if(true){console.log('999')}
if(true, false){console.log('999')}
if(console.log('+++'), true){console.log('999')}

不过这里最后一个条件时this instanceof t肯定为真,因为这个函数最开始是new xxx来的,然后取反就是false,也就是说一定不会throw Error

但是前面部分是要计算的,现在来分析这一句话

void 0 === n && (n = !1), n || (i = H(i.map(function(t) {return t / 2}))), !(this instanceof t)

第三个参数ntrue,那么

  • void 0 === n 结果是 false
  • 不会进行 n = !1 这一步赋值
  • n第一个,之后还是true,那么||之后的部分就不会执行

第三个参数nundefined,那么

  • void 0 === n 结果是 true
  • 会进行 n = !1 这一步赋值 n 变成 false
  • n第一个,变成false,那么||之后的部分就会执行
  • i的值就会分别除以2

然后如果i传入的不是16位,那它就会被置为16位全0,也就是没有偏移量

按正常逻辑,我们可能推测_lastCipherblock还有对i的处理,但是由于之前验证iv过是没有变化时,是标准的aes

现在把截止这里对iv的处理,加入代码,然后验证,可以发现加密key解密结果与浏览器一致

也就是没有更多的处理(实际上某些网站_lastCipherblock这里还会继续处理,只是这里没有罢了,就不分析了)

那么最终key解密过程还原为下面的代码

小结

完成key的解密后,最终就能写出一个完整的解析脚本,只需要网页地址和视频VID

👍 18

none

最后修改于1011天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

狗头

原神

小黄脸

目录

avatar

未末

迷失

126

文章数

275

评论数

7

分类