关于python:JS-逆向百例吾爱破解2022春节解题领红包之番外篇-Web-中级题解

1次阅读

共计 6101 个字符,预计需要花费 16 分钟才能阅读完成。

关注微信公众号:K 哥爬虫,继续分享爬虫进阶、JS/ 安卓逆向等技术干货!

逆向指标

本次逆向的指标来源于吾爱破解 2022 春节解题领红包之番外篇 Web 中级题,吾爱破解每年都会有派送红包流动(送吾爱币),须要大家使出看家逆向本事来剖析内容取得口令红包,往年一共有五个题,一个送分题,两个 Windows 题、一个 Android 题和一个 Web 题,本文剖析的正是 Web 题,吾爱有规定流动完结前不要外泄口令、探讨分享剖析过程,所以本文在流动完结后才收回来。

此 Web 题题目是:小 D 最爱看的视频网站最近关站了,关站前他用 Fiddler 和 Web Archive 保留了一位主播的视频,但他发现存下来的文件无奈播放。你能帮小 D 找回他的回顾吗?(.saz 与 .wacz 任选其一即可解题)

为避免吾爱前期敞开解题通道,K 哥将 .saz 和 .wacz 文件保留了一份,可在公众号后盾回复 吾爱破解 获取!

  • 流动地址:https://www.52pojie.cn/thread…
  • Web 题地址:https://www.52pojie.cn/home.p…

HLS 流媒体传输协定

本题波及到 HLS 流媒体传输协定,先简略介绍一下,理解的同志可间接跳过。

HLS 全称 HTTP Live Streaming,即基于 HTTP 的自适应码率流媒体传输协定,是苹果研发的动态码率自适应技术,它包含一个 M3U(8) 的索引文件,若干 TS 视频流文件,如果视频流文件是加密的,那就还会存在一个 key 加密串文件。

M3U8 文件是 M3U 的一种,只不过文件中存储的文本应用 UTF-8 字符编码,在极少数状况下,M3U8 文件可能会以 M3UP 扩展名保留。M3U8 文件是各种音频和视频播放程序应用的播放列表文件,它蕴含了媒体文件或媒体文件夹的门路或 URL,以及无关播放列表的相干信息。

TS 全称为 MPEG2-TS,TS 即 Transport Stream 传输流,又称 MPEG-TS、MTS、TP,这种格局的特点就是从视频流的任一片段开始都是能够独立解码的。

针对 TS 格局的文件,如果是未加密的,个别的播放器就可能间接播放,也能够应用 FFmpeg 等工具转换为其余格局,FFmpeg 也能够间接解决 M3U8 文件,主动解密合并转换 TS 文件,当然也有其余大佬写好的小工具,拖入 M3U8 文件就间接给你解决好了。

M3U8 文件内容的大抵格局示例如下:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-KEY:METHOD=AES-128,URI="https://www.example.com/m3u8.key"
#EXT-X-TARGETDURATION:5
#EXTINF:4.200000,
https://www.example.com/hls/live_00000.ts
#EXTINF:4.166667,
https://www.example.com/hls/live_00001.ts
#EXTINF:3.600000,
https://www.example.com/hls/live_00002.ts
#EXTINF:2.516667,
https://www.example.com/hls/live_00003.ts
#EXTINF:4.166667,
https://www.example.com/hls/live_00004.ts
#EXTINF:4.166667,
https://www.example.com/hls/live_00005.ts
#EXTINF:4.166667,
https://www.example.com/hls/live_00006.ts
#EXTINF:1.716667,
https://www.example.com/hls/live_00007.ts
#EXT-X-ENDLIST

各标签含意如下:

  • #EXTM3U:m3u 文件头,必须放在第一行,起标示作用;
  • #EXT-X-VERSION:播放列表文件的兼容版本。若不存在此标记,则默认为协定的第一个版本;
  • #EXT-X-MEDIA-SEQUENCE:播放列表中的每个媒体 URI 都有一个惟一的整数序列号。URI 的序列号等于它之前的 URI 的序列号加一;
  • #EXT-X-ALLOW-CACHE:批示客户端是否能够缓存下载的媒体片段以供当前重播;
  • #EXT-X-KEY:TS 片段能够被加密,该标签指定加密形式(METHOD)、密钥的 URI 以及偏移量 IV 等信息,没有此标签示意未加密;
  • #EXT-X-TARGETDURATION:每一份 TS 媒体文件的最大持续时间,以秒为单位;
  • #EXTINF:每一份媒体文件的详细信息,包含媒体持续时间、媒体 URL 地址等;
  • #EXT-X-ENDLIST:示意不再将媒体片段增加到播放列表文件中,个别位于文件结尾。

残缺格局、规范标签可参考 HLS 标准协议中,对 Playlist file 的介绍:https://datatracker.ietf.org/…

SAZ 剖析

在 Fiddler 软件中,应用 SAZ 格局用来保留和读取 HTTP/HTTPS 申请信息,关上该文件能够留神到一些重要的申请:script.bundle.js、live.m3u8、drm 以及八个 ts 视频流文件。

先来看看 m3u8 文件,能够看到是 AES-128 加密,加密的 key 文件地址为 key://live,如下图所示:

个别状况下,要想解密 ts,必然会去申请 key 的地址,拿到 key 后再解密 ts,很显然此题的 key 地址不是一个非法的 URL 地址,当然此题的抓包记录可能是出题人伪造的,因为这个 Host 是 52tube.mmxxii,也不是一个非法的域名,最次要的是,抓包记录里没有 key://live 这条申请,那么很大概率实在的地址暗藏在 JS 里,从另一个方面来思考,如果这是残缺的抓包记录,不论实在的 key 地址是啥,必然会在记录里呈现!

有教训的敌人应该一眼就能看进去 drm 这条申请最有可能是拿 key 的操作了,第一是 drm 这个关键词在 ts 解密里常常会呈现,搞得多的敌人应该见过不少,第二 ping 申请返回的 success,通过其名称和返回值来看也不像 key,剩下就只有 drm 了,查看返回值是乱码的,查看 Hex 值,32 位 16 进制数据,而失常的 key 应该是 16 位 16 进制数据,所以你如果间接拿这个数据当作 key 去解密,必定也是失败的。

到这里咱们应该有如下猜测:drm 返回的数据,通过了 script.bundle.js 二次解决就能失去正确的 key。

JS 逆向

咱们把抓包记录的 script.bundle.js,右键,save – response – response body,保留到本地。

格式化之后有 15000+ 行代码,又不能动静调试,从哪里找加密入口呢?能够大胆尝试一下:

  • JS 里可能会检测到 m3u8 里存在 key 的 URI 之后,发送 /api/drm/ 这个申请,能够间接搜寻 /api/drm/ 或者 key://live 定位;
  • drm 是一个 post 申请,带有 h 和 id 两个参数,能够间接搜寻 postidh 定位到大抵地位。

通过搜寻能够发现如下可疑代码片段:

将要害代码提炼一下:

function n(t) {return [...new Uint8Array(t)].map((t => t.toString(16).padStart(2, "0"))).join("")
}

function s(t, e) {let r = new Uint8Array(t.length);
    for (let i = 0; i < t.length; i++) r[i] = t[i] ^ e[i];
    return r
}

let e = "/api/ping/",
    i = "/api/drm/";

class a extends t.DefaultConfig.loader {let e = await async function() {let t = new Uint8Array(16);
        crypto.getRandomValues(t);
        let e = n(t.buffer) + Date.now() + Math.random();
        return new Uint8Array((await async function(t) {const e = (new TextEncoder).encode(t);
            return await crypto.subtle.digest("SHA-256", e)
        } (e)).slice(0, 16))
    }();
    var r = new URLSearchParams;
    r.append("h", n(e.buffer)),
        r.append("id", t);
    var a = {
        method: "POST",
        headers: {"Content-Type": "application/x-www-form-urlencoded"},
        body: r
    };
    let o = await fetch(i, a),
        l = await o.arrayBuffer();
    if (32 !== l.byteLength) throw new Error("Invalid response");
    let u = new Uint8Array(l.slice(0, 16)),
        c = new Uint8Array(l.slice(16, 32));
    return s(s(u, e), c)
}

能够看到事实上在发送 /api/drm/ 申请拿到后果后,先后取前后 16 位数据,而后通过了 s 办法的解决,最初返回的 s(s(u, e), c) 应该才是正确的 key,这里的重点在于 e 的值,下面有个办法,取了以后工夫 + 随机值,通过 SHA-256 加密,再取前 16 位。

这里能够思考一下,这个 e 的值是不固定的,那么最初的 key 应该也是不固定的,同一个 TS 对应有无数个 key,我反正是没见过,不信的话尝试就用那个办法生成 e,你会发现最终的 key 是谬误的。

认真看一下,发送 post 申请对 h 值赋值的中央:r.append("h", n(e.buffer)),n 办法是转 16 进制,那么咱们间接将 h 值倒推,从 16 进制转为 10 进制,这才是正确的 e 的值!而后 l 的值是 /api/drm/ 申请返回的 32 位 16 进制数据转为 10 进制,剩下的就好说了,间接改写一下 JS 代码拿到正确的 key:

function s(t, e) {let r = new Uint8Array(t.length);
    for (let i = 0; i < t.length; i++)
        r[i] = t[i] ^ e[i];
    return r
}

function getKey(){
    // /api/drm/ 申请表单的 h 值,16 进制数据
    const h = ["7b", "10", "31", "1e", "6e", "31", "0f", "0d", "f0", "68", "d9", "ed", "e1", "04", "75", "a8"];
    // /api/drm/ 申请返回的 32 位 16 进制数据
    const drm = ["08", "A5", "E6", "C2", "C2", "61", "A8", "AC", "B4", "D7", "9C", "49", "AF", "16", "0A", "3A", "DA", "4E", "5C", "EA", "E1", "6F", "ED", "46", "EB", "6F", "49", "8C", "9B", "63", "D5", "3B"]
    // 转换为 10 进制数据,为 e 和 l 赋值
    const e = [];
    const l = [];
    for (let i=0; i<h.length; i++)
    {e.push(parseInt(h[i],16))
    }
    for (let i=0; i<drm.length; i++)
    {l.push(parseInt(drm[i],16))
    }

    const u = new Uint8Array(l.slice(0, 16));
    const c = new Uint8Array(l.slice(16, 32));

    const keyArray = s(s(u, e), c);
    const keyHex = new Buffer.from(keyArray).toString('hex');
    const keyBase64 = new Buffer.from(keyArray).toString('base64');

    console.log("keyArray:", keyArray)
    console.log("keyHex:", keyHex)
    console.log("keyBase64:", keyBase64)
}

getKey()

// 输入
// keyArray:  Uint8Array(16) [
//   169, 251, 139,  54,  77,
//    63,  74, 231, 175, 208,
//    12,  40, 213, 113, 170,
//   169
// ]
// keyHex:  a9fb8b364d3f4ae7afd00c28d571aaa9
// keyBase64:  qfuLNk0/Suev0Awo1XGqqQ==

TS 解密合并转换

通过 JS 逆向咱们拿到了 16 进制和 base64 模式的 key,不论什么模式都能够拿来解密,这里介绍两种对 TS 媒体流解密、合并、转换的办法。

第一种办法是应用 FFmpeg 工具,FFmpeg 是一套能够用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。官网地址:https://ffmpeg.org/,下载编译好的程序,将 bin 目录增加到环境变量即可,该工具也能够间接在 K 哥爬虫公众号后盾回复 M3U8 获取。

首先咱们要把 m3u8 文件和 ts 媒体流保留到同一个文件夹,因为是虚伪的 Host,所以不能间接浏览器拜访保留,能够间接在 Fiddler 里,右键,save – response – response body,保留到本地,如下图所示:

而后就是保留密钥文件,这里要求密钥文件必须是 16 进制的数据,如果你间接将 key 以字符串模式保留的话,解密也是失败的,编辑 16 进制文件有专门的工具,比方 HxD、010 editor、winhex 等,以 HxD 为例,新建文件,写入咱们后面通过 JS 逆向失去的 key 的 16 进制数据,存为 .key 文件,如下图所示:

而后批改 m3u8 文件里 key 的地址、名称,倡议将 key、m3u8、ts 文件都放同一个文件夹,这样 m3u8 文件里就不必增加资源门路了,不容易出错。

而后在以后文件夹,关上命令行输出命令:ffmpeg -allowed_extensions ALL -i live.m3u8 -c copy live.mp4,即可主动解密 ts,并合并转换为 .mp4 格局:

第二种办法就是应用大佬写的第三方小工具,这里举荐吾爱大佬逍遥一仙写的 M3U8 批量下载器,下载地址、应用办法见原贴:https://www.52pojie.cn/thread…,也能够在 K 哥爬虫公众号后盾回复 M3U8 获取。

咱们能够间接拖入解决好的 M3U8 文件,主动解决:

也能够抉择其余 – 工具 – 合并助手,增加所有 TS 文件,输出 key 后主动解决:

处理完毕后的 mp4 文件默认在软件目录的 output 文件夹外面,解密后是一段动画,往后看会找到 flag:flag{like_sub_52tube} 为正确答案。


正文完
 0