type
Post
status
Published
date
May 16, 2023
slug
tech-m3u8
summary
分析一个m3u8链接的解析过程
tags
Python
逆向
category
技术分享
icon
password
URL
m1907视频接口js解密分析
问题描述
在使用谷歌开发者工具进行跟踪分析的时候,我们发现请求的一个接口带有z和slig这两个请求参数,但搜索的时候并没有找到这两个值的设置位置,且这两个值会每天更新,如果z值不更新,就会导致返回的响应错误
问题原因与初步分析
在网络请求面板中跟踪链接及时序,可以看到之后又加载了一个css文件和两个js文件,之后的请求就会出现z和slig这两个参数,那么,问题应该就出在js文件上,里面存在设置参数的函数,同时js文件采用了webpack方式加密,且每个文件都有几千行代码,对于阅读和查找分析带来了很大的麻烦,怎么定位和调试成了解决问题的关键。
图1:

我的初步分析方法是查找"z ="或者"z="字符串,因为这里是设置z参数的代码,此方法可以大致定位到代码段,接下来在可疑代码位置下断点,看看能否断下来,图2是我找到的断点位置,刷新网页就可以断下来,单步调试可以跟踪变量变化,当调试到p = (p = Et()(String(p))).substring(0, 10),这里时,发现p的值是37,经过多次刷新页面后观察该值没有发生变化,单步调试进入该语句,发现代码跳转到图3,那么这里应该就是对p值进行加密的代码段了,经过step over调试后,代码重新返回图2界面,此时p值更新为一个hash值,但这个值与最终的z参数不一致,这里通过z参数的长度初步尝试使用常用的加解密算法来还原,使用md5在线解密,发现解密出来的字符串与通过调试得到的p值的前十位是一致的,至此,整个解密流程也就梳理完毕了。
图2:

图3:

问题解决
我们的最终目标是拿到z值,可以按照以下步骤来操作
- 计算p值,该p值与日期和时间相关
- 对p值进行加密,算法过于复杂,不采用模拟方式计算
- 对加密后的p值进行md5加密,得到z请求参数
鉴于js文件过于复杂,我们采用pyexecjs模拟的方式来进行解密步骤,方法如下:
- 安装nodejs免安装版,并设置环境变量,node -v显示版本说明安装成功,这里用的是nodejs8的版本
- 安装pyexecjs模块
原生的js文件直接加载后调用会出现找不到函数的错误,这里我们可以把需要的js代码段抽出来保存到一个本地js文件,使用标准函数形式即可,见代码块1,最终的python模拟分析见代码块2
代码块1:
function y(e, t) { return function (e, t) { var n, r, o = p(e), i = [], l = []; for (i[15] = l[15] = void 0, o.length > 16 && (o = f(o, 8 * e.length)), n = 0; n < 16; n += 1) i[n] = 909522486 ^ o[n], l[n] = 1549556828 ^ o[n]; return r = f(i.concat(p(t)), 512 + 8 * t.length), d(f(l.concat(r), 640)) }(h(e), h(t)) } function g(e, t, n) { return t ? n ? y(t, e) : m(y(t, e)) : n ? v(e) : m(v(e)) } function z() { c = new Date, l = c.getTime(), u = 6e4 * c.getTimezoneOffset(), d = l + u + 36e5 * 8, m = new Date(d), p = (p = m).getDate() + 9 + 9 ^ 10; return p; } function y(e, t) { return function (e, t) { var n, r, o = p(e), i = [], l = []; for (i[15] = l[15] = void 0, o.length > 16 && (o = f(o, 8 * e.length)), n = 0; n < 16; n += 1) i[n] = 909522486 ^ o[n], l[n] = 1549556828 ^ o[n]; return r = f(i.concat(p(t)), 512 + 8 * t.length), d(f(l.concat(r), 640)) }(h(e), h(t)) } function g(e, t, n) { return t ? n ? y(t, e) : m(y(t, e)) : n ? v(e) : m(v(e)) } function z() { c = new Date, l = c.getTime(), u = 6e4 * c.getTimezoneOffset(), d = l + u + 36e5 * 8, m = new Date(d), p = (p = m).getDate() + 9 + 9 ^ 10; return p; }
代码块2:
import hashlib import execjs print(execjs.get().name) with open('prj/m3u8/js/m1907.js') as f: ctx = execjs.compile(f.read()) z = ctx.call('z') print(z) ret = ctx.call('g', z) print(ret) m = hashlib.md5(ret[:10].encode(encoding='utf-8')).hexdigest() print(m) import hashlib import execjs print(execjs.get().name) with open('prj/m3u8/js/m1907.js') as f: ctx = execjs.compile(f.read()) z = ctx.call('z') print(z) ret = ctx.call('g', z) print(ret) m = hashlib.md5(ret[:10].encode(encoding='utf-8')).hexdigest() print(m)
延伸思考
关于代码定位的问题,可以通过特征字符串精准定位,比如"api/v/?z=",见图4
图4:

关于python模拟解析js的问题,由于原生js过于复杂,且直接加载后无法调用,因此通过python来计算的方式并不可取,最好的方式就是抽离关键代码,保存到本地供python调用
- 作者:紫电穿云
- 链接:https://zidy.eu.org/article/tech-m3u8
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。



