ligxi 发表于 2022-4-8 15:10

某视频网站流媒体初次分析全过程

本帖最后由 我没有失眠啊i 于 2022-4-8 16:37 编辑

## 开篇

最近呢,学了一点js逆向的知识,一直想找个可以实践一下的平台来试试学习效果。偶然间看到了这个平台,发现很适合拿来练手,不过由于这个网站视频开始播放前都有那种不和谐的广告,所以地址就不能贴了,贴图也做了一些脱敏处理!

**需要准备的工具:**

- 一些JavaScript基础知识,能大概看懂就行
- 浏览器:Chrome浏览器、Edge浏览器等能方便进行调试的,这里我用的是Chrome浏览器
- 抓包工具:Fiddler,用来替换和保存一些数据
- 分析工具:Winhex,用来查看内容
- 下载工具:IDM,用来测试内容是否可以正常下载
- M3U8下载器:https://www.52pojie.cn/thread-1374045-1-1.html

## 第一次分析

------

打开目标网站首页,选择任意一个视频信息点击进入后:!(https://s1.ax1x.com/2022/04/08/LpH2se.png)

可以直接看到资源下载地址,不过这次目的是来逆向的,所以**看不到!看不到!看不到!**😎

### 1.1 过控制台检测

接下来就到了我们的分析时间,点击【在线播放】后尝试使用快捷键F12打开控制台,然后就弹出了以下提示:!(https://s1.ax1x.com/2022/04/08/LpHvon.png)

打不开控制台不要紧,选择浏览器右上角的三个小点,从更多工具中打开控制台:

!(https://s1.ax1x.com/2022/04/08/LpbFL4.png)



### 1.2 过无限debug

控制台一打开,立马跳出个无限debuuger,看来还是做了一些反调试措施的:

!(https://s1.ax1x.com/2022/04/08/LpbGTA.png)

从右边的堆栈中向下查找入口,从上到下依次点击看看:

!(https://s1.ax1x.com/2022/04/08/LpbaSf.png)

很显然这个并不是,再往下点时,就发现了非常关键的函数调用:

!(https://s1.ax1x.com/2022/04/08/Lpbgf0.png)

其实这个无限debugger卡了挺长时间的,一开始开始尝试删除这个关键位置的调用,***删除整个debugger的自调用函数,甚至删除整个外层的debugger函数***,但是发现这些操作会导致视频无法正常加载,控制台过段时间就会直接卡死等莫名其妙的问题,说明这些函数中有些关键地方还是有用的,那看不懂这些混淆的代码又该怎么办呢?难道此次实战就此终结!!!

在前思来想去后,突然想到既然debugger关键字会触发断点,那要是我把这个**关键字给改了**是不是就停不下了?说干就干,把含有无限debugger的js文件在源代码中直接另存为或者从Fiddler另存为一份到本地,方便进行修改。
从上图可以看到前面的内容为“bugger”,后面的内容为“de”,要修改的关键就在这里里,以文本方式打开对应的js,直接搜“de”,发现有5个地方有这个信息:

!(https://s1.ax1x.com/2022/04/08/LpqS7d.png)

全部替换,这里我替换成“dd”,替换后保存:

!(https://s1.ax1x.com/2022/04/08/Lpqk1f.png)

打开Fiddler工具,右边选择自动响应,勾选启用规则和请求传递,填写js网络地址和本地js地址,并保存:

!(https://s1.ax1x.com/2022/04/08/Lpqm7j.png)

确认Fiddler能获取浏览器请求后,打开浏览器里的设置,选择清空之前的数据,这样才会重新加载本地替换的js文件:

!(https://s1.ax1x.com/2022/04/08/LpqYB4.png)

重新打开播放页面后,再次打开控制台,发现不仅无限debugger没了,控制台也正常了,视频也能正常加载了,很好!可以愉快的玩耍了。

### 1.3 寻找m3u8地址

!(https://s1.ax1x.com/2022/04/08/LpqaNR.png)

打开网络面板,会发现很多的302请求,302表示重定向,这个请求会在响应中返回一个新的地址。开始查找视频文件地址,发现m3u8文件也被**重定向**了:

!(https://s1.ax1x.com/2022/04/08/LpqyuD.png)

用IDM工具下载这个m3u8文件看看,可以看到返回的居然是一个张图片???😂

!(https://s1.ax1x.com/2022/04/08/LpqTKS.png)

打开看看,额,居然是二维码,扫了一下返回是视频网址,那m3u8信息哪去了?

!(https://s1.ax1x.com/2022/04/08/L9tc9S.png)

用winhex打开看看,看这个头信息好像就是一个图片!(https://s1.ax1x.com/2022/04/08/LpL1IA.png)

查找一个关键内容看看,winhex直接搜m3u8:

!(https://s1.ax1x.com/2022/04/08/LpLyR0.png)

发现并不是想要的内容,用winhex前后翻看发现基本都是乱码,那么极有可能m3u8的内容**被加密**了,存放在这个图片中。尝试在源代码中查找m3u8文件的调用堆栈发现事情并不简单,很多地方的js代码都被混淆了,所以要找到还原方法实属头大,可是没有代码就不能还原出m3u8的内容怎么办?于是想到加载ts文件不是也得从m3u8的内容中读取吗?所以开始尝试查找ts的调用堆栈,这里可以看到ts的请求也是302状态码,先不管:

!(https://s1.ax1x.com/2022/04/08/LpLoJ1.png)

把鼠标放在其中一个ts请求的启动器上,点击浮窗中展示的js文件进入调式:!(https://s1.ax1x.com/2022/04/08/LpOEwQ.png)

这里会自动跳转到源代码中js的点击位置,先在这里打上一个断点,免得调试过头了。

!(https://s1.ax1x.com/2022/04/08/LpOnWq.png)从右下角的

堆栈从上往下依次点击看看,在每个堆栈位置前后翻翻,然后发现了一个有关m3u8的信息,在这里下断点:

!(https://s1.ax1x.com/2022/04/08/LpO8w4.png)

F5重新加载页面,可以看到m3u8的内容此时已经被加载了:

!(https://s1.ax1x.com/2022/04/08/LpOaSx.png)

尝试把这个m3u8信息保存下来,打开控制台输入了以下代码:

```javascript
const data = t.data; // 这里填内容的字符串
const blob = new Blob(, {type: "text/plain"});
const a= document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "demo.m3u8"; // 这里填保存的文件名
a.click();
URL.revokeObjectURL(a.href);
a.remove();
```

!(https://s1.ax1x.com/2022/04/08/LpODmD.png)

成功保存,打开看看,正好是m3u8的内容:

!(https://s1.ax1x.com/2022/04/08/LpXk1x.png)

### 1.4 ts解密

尝试用IDM访问前面一两个ts链接看看有没有加密:

!(https://s1.ax1x.com/2022/04/08/LpX3gP.png)

怎么返回的又是图片?直接打开看看:


!(https://s1.ax1x.com/2022/04/08/LpXYDS.png)

啥呀这是,一张花屏的图片,用winhex打开看看:

!(https://s1.ax1x.com/2022/04/08/LpXqVe.png)

上下拖动内容后发现并不是常规的图片内容,果然有猫腻,在图片的下面发现了视频相关的头信息,难道这是图片伪装的视频文件?从其它平台找一个正常的ts文件对比看看:

!(https://s1.ax1x.com/2022/04/08/LpXjPA.png)

哦豁,原来后面的内容确实是视频文件,只是前面一小部分是BMP文件的头信息,那简单了,直接删了试试:

!(https://s1.ax1x.com/2022/04/08/Lpjex0.png)!(https://s1.ax1x.com/2022/04/08/LpjQZF.png)

修改后另存为一份,后缀改为ts,避免修改错误导致源文件信息丢失!(https://s1.ax1x.com/2022/04/08/LpjBIH.png)

打开修改后的ts文件,正常播放:
!(https://s1.ax1x.com/2022/04/08/Lpx9Ag.png)

PS:后来发现论坛里,逍遥大佬写的m3u8下载器可以自动去除这种不是ts内容的信息:

!(https://s1.ax1x.com/2022/04/08/Lpx3g1.png)

至此第一阶段分析结束,我以为也就这样了,但是再尝试打开几个视频后,发发发现居然有不一样的加密!!!

OK,既然被发现了,那必须得再分析看看=>

## 第二次分析

### 2.1 获取m3u8和key

有了前面那些踩坑,这次分析起来就快多了,再次尝试打开视频页面,通过调试再次拿到m3u8文件,拖入m3u8下载器看看:

!(https://s1.ax1x.com/2022/04/08/LpxtHO.png)

发现这次需要key,通过不断的调试终于hook到key的位置,这里先把key拿来用一下,填入key,发现又解码失败了:

!(https://s1.ax1x.com/2022/04/08/LpxW5Q.png)

### 2.2 ts解密

难道这个ts与众不同?打开m3u8文件后发现ts的链接长度明显变短了,下载ts后再次打开文件看看:!(https://s1.ax1x.com/2022/04/08/LpziVO.png)

又是熟悉的花屏图片,用winhex打开看看:

!(https://s1.ax1x.com/2022/04/08/Lpz1Ig.png)

发现这次怎么也找不到ts头信息了,搜也搜不到了,而且内容非常乱,看起来应该是被加密了,这样只能调试代码看看怎么回事了。这次换个方法找,直接搜,为啥呢,因为调试了半天也跳到解密位置,直接搜碰碰运气:

!(https://s1.ax1x.com/2022/04/08/L9khPf.png)



看到有很多结果,前面两个js可以忽略,第3、4个js文件很可疑,打开相应的js再次搜索关键字,发现有很多结果,从上到下依次都大概看一下。然后发现这个位置很可疑,其它地方都没混淆代码,偏偏这里有混淆,此地无银三百两,打上断点试试:

!(https://s1.ax1x.com/2022/04/08/L9SCSs.png)

再次F5加载页面或者继续播放,点击格式化后再搜关键字,可以看到代码都被混淆了,但是有些关键信息还是可以看到的:

!(https://s1.ax1x.com/2022/04/08/L9So7T.png)



统统打上断点看看,F8依次执行,可以看到rsp就是网站加载的原始ts文件,大小一模一样,这里我从Fiddler保存了一份做对比:

!(https://s1.ax1x.com/2022/04/08/L9ptbV.png)

F8继续执行,发现len变量的值为1243440,应该是确定加密范围的:

!(https://s1.ax1x.com/2022/04/08/L99rFS.png)



点击继续下一个断点,发现rsp变了,长度不是1244642,而是1243440了:

!(https://s1.ax1x.com/2022/04/08/L99IFU.png)

依葫芦画瓢,这里先保存一下rsp内容,此时的rsp是不能播放的:

!(https://s1.ax1x.com/2022/04/08/L9CCSH.png)

打开本地保存的后缀为.bmp的ts文件和这个enc.ts进行对比,并搜搜看:

!(https://s1.ax1x.com/2022/04/08/L9CAmt.png)!(https://s1.ax1x.com/2022/04/08/L9CVTf.png)

原来是通过一定的算法把前面和后面一部分内容给去掉了,那么剩下的应该就是被加密的视频文件了,删除后大小刚好就是enc.ts的大小。下一个断点,来到uText这个变量,发现这个变量的长度刚好和前面的rsp长度是一样的,这里应该就是做了转换,为解密做准备:

!(https://s1.ax1x.com/2022/04/08/L9CG7V.png)

下一个断点,来到解密函数,这里可以清楚看到加密的内容uTxt、key、iv,虽然看不太懂混淆的代码,但这显然是一个标准的AES-CBC解密操作:

!(https://s1.ax1x.com/2022/04/08/L9CctO.png)
而最后的newRresponse=new Uint8Array(arr);就是解密好的视频流,当然这里可以用代码来实现,这里的key和iv都是同一个值,控制台输出一下:

!(https://s1.ax1x.com/2022/04/08/L9CO3Q.png)

这里用Python代码实现解密:

```python
from Crypto.Cipher import AES
import base64

fr = open(r'enc.ts', 'rb')
content = fr.read()
key = iv = base64.b64decode("9Kq58j61Jx42io3e37Qomg==")
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.decrypt(content)
with open(r'dec.ts', 'wb') as fw:
fw.write(data)
```

成果展示



!(https://s1.ax1x.com/2022/04/08/L9iVJS.png)

## 总结

------

通过这次实战见识到了很多花里胡哨的操作,还是相当有收获的。也明白还是需要不断学习才能走的更远,再接再厉!当某种逆向思路不行就得尝试换种思路,还有一点就是逆向一定要有耐心!
**不足**:
由于还没学会如何使用AST解这些混淆代码,导致看不懂核心逻辑,所以以下问题暂时没有得到解决

1. m3u8地址下载的文件如何从图片提取并还原成正常的m3u8内容?
2. 第二种加密的每个ts似乎都对应着一个key,那么这些hexkey应该如何计算出来?
3. 第二种ts文件从什么地方开始到结束才是真正需要解密的内容?
4. 嗯,就先这样吧!

漁滒 发表于 2022-4-9 18:25


[*]m3u8地址下载的文件如何从图片提取并还原成正常的m3u8内容?
[*]第二种加密的每个ts似乎都对应着一个key,那么这些hexkey应该如何计算出来?
[*]第二种ts文件从什么地方开始到结束才是真正需要解密的内容?

第一个问题,代码来自于play.new.js,反混淆后核心代码如下
_0x109702["onload"] = function (e) {
    var buffer = _0x109702["response"];
    var t = new Uint8Array(buffer);
    t = t["slice"](3354);

    var _0x424177 = pako["inflate"](t);

    var res = "";
    var _0x3295a8 = 16384;

    var _0x6ef1a3;

    for (_0x6ef1a3 = 0; _0x6ef1a3 < _0x424177["length"] / _0x3295a8; _0x6ef1a3++) {
      res += String["fromCharCode"]["apply"](null, _0x424177["slice"](_0x6ef1a3 * _0x3295a8, (_0x6ef1a3 + 1) * _0x3295a8));
    }

    res += String["fromCharCode"]["apply"](null, _0x424177["slice"](_0x6ef1a3 * _0x3295a8));
    res = res["replace"](/.*?\.ts/g, "https://vod.bdys.me/$&");
    lines["push"]("data:application/vnd.apple.mpegurl;base64," + btoa(res));

    if (next) {
      next();
    }
};

这是请求成功的回调函数,可以看到先截取3354字节后面的内容,然后使用pako进行解压,python代码如下

    response = requests.get(m3u8_1, headers=headers).content
    m3u8 = gzip.decompress(response).decode()

第二个问题和第三个问题,代码来自于hls.min.js,反混淆后核心代码如下

                if (t["url"]["indexOf"](".ts") > 0 && "arraybuffer" === t["responseType"]) {
                  try {
                  var buffer = e["response"];
                  var rsp = new Uint8Array(buffer);

                  if (rsp == 138) {
                      let len = arrayToInt(new Uint8Array(, rsp, rsp, rsp]));
                      let hexKey = Array["from"](rsp["slice"](142, 158), _0x144ed7 => _0x144ed7["toString"](16)["padStart"](2, "0"))["join"]("");
                      rsp = rsp["slice"](158, 158 + len);
                      const key = CryptoJS["enc"]["Hex"]["parse"](hexKey, "uft-8");
                      let uTxt = CryptoJS["lib"]["WordArray"]["create"](rsp);
                      let decrypted = CryptoJS["AES"]["decrypt"]({
                        "ciphertext": uTxt
                      }, key, {
                        "iv": key,
                        "mode": CryptoJS["mode"]["CBC"],
                        "padding": CryptoJS["pad"]["Pkcs7"]
                      });
                      let arr = wordToByteArray(decrypted["words"]);

                      if (arr["length"] > 0 && arr == 71 && arr == 64) {
                        newResponse = new Uint8Array(arr);
                      }
                  }
                  } catch (e) {}
                }

第二个问题就很明显了,key取的是142字节到158字节
第三个问题这里首先计算了ts的长度,在138字节处取了一个小端序的int类型,然后从158字节开始截取出真实的ts
然后就可以使用标准aes解密出ts视频,python代码如下

    ts = requests.get(tsurl, headers=headers).content
    if ts == 138:
      key = ts
      ts = ts)]
      crypto = AES.new(key=key, mode=AES.MODE_CBC, iv=key)
      ts = unpad(crypto.decrypt(ts), AES.block_size)
      with open('1.ts', 'wb') as f:
            f.write(ts)

jinzhu160 发表于 2022-4-9 09:09

var AAA = Function;
Function = function (x) {
if (x != 'debugger') {
    return AAA(x);
}
return function () {};
};
执行这个函数以后,断点会自动消失哦。

ligxi 发表于 2022-8-18 16:20

gamma 发表于 2022-8-18 16:07
小白提问时间:
m3u8软件如何设定参数才能使其从某一位置开始读取呢?

其实没有一个软件可以满足各种需求的,设定参数读取某一个位置的,这种要么软件的作者修改后支持,要么就只能自己写了。
根据网页的逻辑代码,自己编写出对应的处理代码。逍遥的m3u8软件也是这样处理的,判断流媒体是否是正常的头信息,如果是正常的就不进行处理,如果不是就按网页的逻辑代码来处理。
如果感兴趣的话可以看看常见的文件头信息,就会懂如何判断了。:loveliness:

laobj 发表于 2022-5-25 14:07

感谢楼主分享原创

ligxi 发表于 2022-4-8 15:58

OVVO 发表于 2022-4-8 15:39
大佬您好,希望能分析下这篇
【震惊】发现一种极其罕见的m3u8魔改加密
https://www.52pojie.cn/thread-16 ...

这个ts加密位置好像和那个阿里是一样的,有一篇阿里的帖子就有提到,不过能不能看懂就得看你自己了。

chucklee 发表于 2022-4-8 15:33

学习一下,谢谢分享

gblw 发表于 2022-4-8 15:38

看得我热血沸腾!

OVVO 发表于 2022-4-8 15:39

大佬您好,希望能分析下这篇
【震惊】发现一种极其罕见的m3u8魔改加密
https://www.52pojie.cn/thread-1613157-1-1.html
(出处: 吾爱破解论坛)

李玉风我爱你 发表于 2022-4-8 15:41

本帖最后由 李玉风我爱你 于 2022-4-8 15:49 编辑

图片类型的可以在hex模式下搜索 49 45 4e 44(IEND)
49 45 4e 44是png的end标识符 之后的数据就是加密的m3u8地址

yang5355 发表于 2022-4-8 17:00

良心教程,非常好的逆向思路,授人以渔{:1_921:}

ligxi 发表于 2022-4-8 17:11

李玉风我爱你 发表于 2022-4-8 15:41
图片类型的可以在hex模式下搜索 49 45 4e 44(IEND)
49 45 4e 44是png的end标识符 之后的数据就是加密的m ...

我搜了一下似乎并没有这串代码,这应该是来自其他网站的类似代码吧?{:1_904:}不过可以做参考

李玉风我爱你 发表于 2022-4-8 17:18

ligxi 发表于 2022-4-8 17:11
我搜了一下似乎并没有这串代码,这应该是来自其他网站的类似代码吧?不过可以做参考

是其他网站的我之前逆向的 你可以参考下

又要取名字 发表于 2022-4-8 17:21

这是哔嘀影视吧
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 某视频网站流媒体初次分析全过程