吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11967|回复: 144
收起左侧

[Web逆向] 某网课平台m3u8 key解密算法分析以及python实现

    [复制链接]
Zizz 发表于 2023-10-20 17:01
本帖最后由 Zizz 于 2023-10-26 17:58 编辑

目标:aHR0cHM6Ly9kYXRhbmcwMS55dW54dWV0YW5nLmNuLw==



播放任意一个视频,F12开发者工具找到keyurl的请求,响应结果中有一个encryptedVideoKey,应该是加密后的key



                                    2.png




然后来看一下js,不出意外应该是这个了。

                                    3.png


点进去搜索ctrl+F 搜索关键词decrypt,发下有90多个结果



                                    4.png


一个个看太费劲了,搜prototype.decrypt(别问为什么搜这个,问就是 经验......)


                                     5.png


这样就舒服多了。

                                     6.png


ModeOfOperationCBC.prototype.decrypt 这个方法比较像,最后返回的plaintext可能就是解密后的key。

所以在return plaintext处打个断点,然后刷新网页重新播放视频。


但是却没有像预期的那样断下来。显然不是这个并不是解密方法。


那我们就把所有的xxxx..prototype.decrypt方法都打上。重新刷新网页

                                     7.png


这一次在ModeOfOperationECB.prototype.decrypt处断了下来。


                                              8.png

验证下plaintext是不是解密。
这里使用python将uint8Array转为十六进制字符串,代码如下

[Python] 纯文本查看 复制代码
Uint8Array = [113, 108, 67, 89, 72, 112, 78, 115, 88, 65, 70, 112, 102, 84, 51, 122]
hex_string = bytes(Uint8Array).hex() # 716c435948704e73584146706654337a



然后使用N_m3u8DL下载:
  
                                              9.png

(这里十六进制key会被N_m3u8DL自动Base64编码)

可以下载。


key解密位置已经找到。下面来分析一下key的具体解密算法以及如何使用python实现(便于批量下载)






回到刚才,我们看下调用堆栈紧挨着的这个匿名函数


                                           10.png
         
点击进去:
                                 11.png
                           
这里我们看到了一些熟悉的东西,videoKeyId , playerId,encryptedVideoKey。也就是keyurl返回的那些,
很显然,这段js代码就是整个加密key到解密key的算法。


下面来分析代码逻辑:
       var _encryptedVideoKey = _response.encryptedVideoKey;
       将keyrul返回的encryptedVideoKey取出来
       var _key = _aes2["default"].utils.utf8.toBytes("72Fhskjglp8qjpqx")
       将"72Fhskjglp8qjpqx"传给_aes2["default"].utils.utf8.toBytes()方法生成_key
       var _aesEcb = new _aes2["default"].ModeOfOperation.ecb(_key)
       创建一个AES.ECB模式的加密器_aesEcb,使用_key进行初始化。
       var _bbb = []
       for (var i = 0; i < _encryptedVideoKey.length; i += 2) {
             _bbb.push(parseInt(_encryptedVideoKey + _encryptedVideoKey[i + 1], 16)
        }
       创建空数组_bbb,将十六进制字符串encryptedVideoKey转为字节数组添加到_bbb中
       var _decryptedBytes = _aesEcb.decrypt(_bbb)
       使用_aesEcb.decrypt方法对_bbb解密得到解密后的key


这一段代码使用python实现:




写累了................
以为发布了可以设置成仅自己自己可见,好像并不行,那就先这样吧,有时间再继续写。
第一次写文章,真累      





接着来看这部分代码:


[JavaScript] 纯文本查看 复制代码
var _encryptedVideoKey = _response.encryptedVideoKey;
// 将keyrul返回的encryptedVideoKey取出来

var _key = _aes2["default"].utils.utf8.toBytes("72Fhskjglp8qjpqx");
/* 将"72Fhskjglp8qjpqx"传给_aes2["default"].utils.utf8.toBytes()方法生成_key
   其实也就是将字符串72Fhskjglp8qjpqx转为字节 */
    
var _aesEcb = new _aes2["default"].ModeOfOperation.ecb(_key);
// 创建一个AES.ECB模式的加密器_aesEcb,使用_key进行初始化。

var _bbb = [];
for (var i = 0; i < _encryptedVideoKey.length; i += 2) {
     _bbb.push(parseInt(_encryptedVideoKey + _encryptedVideoKey[i + 1], 16)}
/* 创建空数组_bbb,将十六进制字符串encryptedVideoKey转为字节数组添加到_bbb中
   其实就是将十六进制字符串key切片成每两个字符一组,再将每组字符转为十六进制整数*/

var _decryptedBytes = _aesEcb.decrypt(_bbb);
var _decryptedText = _aes2["default"].utils.utf8.fromBytes(_decryptedBytes);
/* 调用刚刚的生成的加_aesEcb下的decrypt方法将_bbb解密。得到解密后的key。
   这个decypt方法也就是刚刚我们断下的ModeOfOperationECB.prototype.decrypt方法 */



python实现:
[Python] 纯文本查看 复制代码
_encryptedVideoKey  = '848e11b61279fd861d0b7d4805906ce4'
_key = list(b'72Fhskjglp8qjpqx')
aesEcb = ModeOfOperation(_key)
_bbb = []
for j in range(0,len(enc_key),2):
    _bbb.append(int(enc_key[j:j+2],16))
key_intArray = aesEcb.decrypt(_bbb)
decypt_key_str = bytes(key_intArray).decode('utf-8') # qlCYHpNsXAFpfT3z


缺什么找什么,
我们进入_aes2["default"].ModeOfOperation.ecb方法(进入方法如下图,鼠标选中这个方法在弹出的页面点击FunctionLocation后面的链接)

                                        12.png

可以看到它是一个类

                                     13.png

也就是我们我们刚刚代码里提到的,_key实例化了这个ModeOfOperationECB类。然后又调用了ModeOfOperationECB类下的decrypt方法对_bbb去解密。
我们先用python写一下相关代码,然后再去分析下一步。
此处python代码实现:

[Python] 纯文本查看 复制代码
class ModeOfOperation:
    def __init__(self,key):
        self.key=key
        self.aes =AES(self.key) # 这里的aes是另外一个类的实例化

    def decrypt(self,ciphertext):
        plaintext = [0] * len(ciphertext)
        block = [0] * 16
        for i in range(0, len(ciphertext), 16):
            copy_array(ciphertext, block, 0, i, i + 16)
            block = self.aes.decrypt(block)
            copy_array(block, plaintext, i)
        return plaintext
    
    def encrypt(self,ciphertext):
        pass
        # 用不到,不做分析
        



我们继续进入AES这个类内部。需要先在this._aes = new AES(key)处打个断点,再用上文的进入方法

   
                                              14.png

AES内部:


                                        15.png


                                           16.png
                  
AES类下同样有很多方法_prepare() ,encrypt ,decrypt等,其中只有一部分我们需要用到。

如:_prepare()就是它的实例化方法(实际上它的作用就是生成this._Kd和this._Ke,后续解密需要用得到这两个参数),
         decrypt()方法实际上在ModeOfOperationECB类下的decrypt方法被调用。此处不贴图了,可以回头看一下上面的图片。

接下来就是用python去写AES类,过程太复杂,这里直接贴代码了。
其中涉及到的S,Si,T1-T8,U1-U4等常量,由于长度原因这里不贴出来了,js里都有。
[Python] 纯文本查看 复制代码
class AES:
    def __init__(self,key):
        self.key = key
        self.Ke = []
        self.Kd = []
        self.prepare()

    def prepare(self):
        key_len = len(self.key)
        rounds = 10
        for i in range(11):
            self.Ke.append([0, 0, 0, 0])
            self.Kd.append([0, 0, 0, 0])
        roundKeyCount = 44
        kc = 4
        tk = convert_to_int32(self.key)
        index = None
        for i in range(kc):
            index = i >> 2
            self.Ke[index][i % 4] = tk[i]
            self.Kd[rounds - index][i % 4] = tk[i]
        rconpointer = 0
        t = kc
        tt = None
        while t < roundKeyCount:
            tt = tk[kc - 1]
            tk[0] ^= S[tt >> 16 & 255] << 24 ^ S[tt >> 8 & 255] << 16 ^ S[tt & 255] << 8 ^ S[tt >> 24 & 255] ^ rcon[rconpointer] << 24
            rconpointer += 1
            if kc != 8:
                for _i in range(1, kc):
                    tk[_i] ^= tk[_i - 1]
            else:
                for _i2 in range(1, kc // 2):
                    tk[_i2] ^= tk[_i2 - 1]

                tt = tk[kc // 2 - 1]
                tk[kc // 2] ^= S[tt & 255] ^ S[tt >> 8 & 255] << 8 ^ S[tt >> 16 & 255] << 16 ^ S[tt >> 24 & 255] << 24

                for _i3 in range(kc // 2 + 1, kc):
                    tk[_i3] ^= tk[_i3 - 1]

            i = 0
            r = None
            c = None

            while i < kc and t < roundKeyCount:
                r = t >> 2
                c = t % 4
                self.Ke[r][c] = tk[i]
                self.Kd[rounds - r][c] = tk[i]
                i += 1
                t += 1
        for r in range(1, rounds):
            for c in range(4):
                tt = self.Kd[r][c]
                self.Kd[r][c] = U1[tt >> 24 & 255] ^ U2[tt >> 16 & 255] ^ U3[tt >> 8 & 255] ^ U4[tt & 255]
        # 这里字节序原因会导致与js生成的结果不一致,但是不影响最终计算

    def decrypt(self,ciphertext):
        rounds = len(self.Kd) - 1
        a = [0, 0, 0, 0]
        t = convert_to_int32(ciphertext)

        for i in range(4):
            t[i] ^= self.Kd[0][i]

        for r in range(1, rounds):
            for i in range(4):
                a[i] = T5[t[i] >> 24 & 255] ^ T6[t[(i + 3) % 4] >> 16 & 255] ^ T7[t[(i + 2) % 4] >> 8 & 255] ^ T8[t[(i + 1) % 4] & 255] ^ self.Kd[r][i]
            t = a[:]

        result = [0] * 16
        tt = None

        for i in range(4):
            tt = self.Kd[rounds][i]
            result[4 * i] = (Si[t[i] >> 24 & 255] ^ tt >> 24) & 255
            result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 255] ^ tt >> 16) & 255
            result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 255] ^ tt >> 8) & 255
            result[4 * i + 3] = (Si[t[(i + 1) % 4] & 255] ^ tt) & 255

        return result



最终结果验证:
                                                            17.png


















免费评分

参与人数 52吾爱币 +47 热心值 +45 收起 理由
宇宙狂人 + 1 + 1 用心讨论,共获提升!
笙若 + 1 + 1 谢谢@Thanks!
Z781287 + 1 谢谢@Thanks!
gzl2878 + 1 我很赞同!
情定苞米地 + 1 + 1 我很赞同!
aflysnail + 1 谢谢@Thanks!
hnulyt + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
MuYuLinFeng + 1 + 1 谢谢@Thanks!
aigc + 1 热心回复!
xieyang + 1 + 1 用心讨论,共获提升!
TCC2012058 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
gjdjjwzx + 1 + 1 为了分享给大家,辛苦了!
linzero + 1 + 1 用心讨论,共获提升!
woniu777 + 1 谢谢@Thanks!
DJZZH + 1 + 1 厉害,感谢分享,长知识了!
zss10086 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
zhanghahaha + 1 + 1 我很赞同!
ForeverDreaming + 1 + 1 谢谢@Thanks!
Kristin_ + 1 我很赞同!
tianss6 + 1 谢谢@Thanks!
hero0281 + 1 + 1 看不明白,也支持一下
cick + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
allspark + 1 + 1 用心讨论,共获提升!
关照之路 + 1 + 1 我很赞同!
moogmoog + 1 + 1 我很赞同!
tomhex + 1 + 1 用心讨论,共获提升!
kuailebaoshi + 1 + 1 用心讨论,共获提升!
wudi2019 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lclp1314 + 1 + 1 用心讨论,共获提升!
imumu1239 + 1 + 1 谢谢@Thanks!
basfan + 1 + 1 用心讨论,共获提升!
liuluwawj + 1 我很赞同!
Tysaay + 1 用心讨论,共获提升!
星期日 + 1 + 1 谢谢@Thanks!
likebbs + 1 用心讨论,共获提升!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
starcrafter + 1 用心讨论,共获提升!
fsjt2016 + 1 + 1 我很赞同!
爱卡 + 1 + 1 我很赞同!
caicai12 + 1 + 1 我很赞同!
威水爷008 + 1 + 1 我很赞同!
gaoxch + 1 + 1 我很赞同!
aighsn + 1 我很赞同!
s347758 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
superworker2022 + 1 + 1 我很赞同!
luolifu + 1 + 1 谢谢@Thanks!
anysoft + 2 + 1 用心讨论,共获提升!
myair + 1 + 1 谢谢@Thanks!
aabbcc123123 + 1 + 1 谢谢@Thanks!
喵呜酸奶冰 + 1 + 1 我很赞同!
iteamo + 1 + 1 鼓励转贴优秀软件安全工具和文档!
lingyun011 + 1 + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

collinchen1218 发表于 2023-10-25 21:16
这个应该是阿里云加密吧,我记得有一个aliyun-m3u8-downloader下载器可以直接使用
 楼主| Zizz 发表于 2023-10-25 21:14
才发现审核过了,那明天接着更新后面的部分。
flyzhuhzu 发表于 2023-10-23 12:36
ameiz 发表于 2023-10-23 13:27
yunxuetang 律师函安排
GoldenGodwin 发表于 2023-10-23 15:58
ameiz 发表于 2023-10-23 13:27
yunxuetang 律师函安排

怎么知道是yunxuetang的,楼主一开始的目标是网址吗?
最近公司的网络学院变成的yuanxuetang的,正好在研究自动刷课
iteamo 发表于 2023-10-23 16:07
GoldenGodwin 发表于 2023-10-23 15:58
怎么知道是yunxuetang的,楼主一开始的目标是网址吗?
最近公司的网络学院变成的yuanxuetang的,正好在 ...

你试一下那堆字符串base64  解码
lucool 发表于 2023-10-23 19:47
某浪的key能不能搞出来?
mfzy888 发表于 2023-10-23 22:10
虽然看不懂,也要学习一下
zhengsg5 发表于 2023-10-25 21:05
Python代码没有贴上来
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 00:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表