声明
本文章中所有内容仅供学习交流,相关链接做了脱敏处理,若有侵权,请联系我立即删除!
一、前言
前面的文章应该可以让大家掌握好protobuf吧,只要抓住关键词就行了。前面文章我也提到了某音直播弹幕也是采用protobuf格式传输的,那我们这篇文章就来看看,实战一下能不能获取到信息。同时我也会放出我写好的ast工具,对基本的protobuf格式应该都能做到处理,大佬们也可以在我这个基础上修改,或者推出更适合的工具。
二、前情提要
一般的直播弹幕都采用websocket协议传输数据,因为要实时传输,ws协议只用一次建立握手连接,就可持续实时通信。
基于这点,我们可以很容易的就找到我们要的地址,我们开始分析吧!
三、目标网站
任意直播间
四、开始分析
如果你不信我的前情提要,觉得太虚了,有没有可能走http协议呢,那我们可以打开f12,打开fiddler抓包工具(为后面分析ws连接做准备),然后输入我们的目标网站地址(只需https:// live.***.com/数字),抓包看看吧。
获取到弹幕的一瞬间,我们的fiddler就可以关了。我们看看浏览器的f12内容,进度条一直在跑,因为数据很多嘛.
弹幕数据肯定是那些零星的,蓝色分散的点,我们括一小段看看
我们看到数据类型大多是图片,不是我们需要的啊,唯独websocket类型数据是一直贯穿的,即使暂停了直播,弹幕还是会有,webscoket传输的数据还是没停下来。所以我们要找的就是websocket协议传输的数据。
点到ws,然后看到唯一链接wss开头,这就是websocket协议的。
知识点:(绿色表示客户端消息发给服务器,红色表示服务器下发数据给客户端)
我们可以从堆栈入手,找到发包点。
其实我们从名字也大概可以猜到可能是这两个,我一开始是直接看这两个的也能找到点,我们还是从最后一个发包栈(m)看起吧。
我们去搜索一下,学习一下这个是怎么一回事,t参数是什么意思。
来到mdn网站,强烈推荐:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
使用 WebSocket() 构造函数来构造一个 WebSocket。
WebSocket(url ,protocols(可选))
返回一个 `WebSocket` 对象。
所以说这个t是一个url链接,应该就是我们看到的那个wss链接了,我们下个断点,来分析分析。
确实是。我们回到原来那个mdn网站,可以往下看,可以看到事件这个栏目。
我们断点下方不正是有这几个事件关键词吗,我们如果刷新界面,它刚开始连接时,如果成功,应该会触发open事件,我们都可以下个断点看看,主要先看看open,这几个关键词要记住,解websocket协议的核心。
我们可以看到它先走了onmessage,我们在mdn查一下 addEventListener:addEventListener(type,listener)其中 listener是对象,相当于监听它。我们跟过去看看这个对象,函数。
相当于每个都是监听里面这一堆东西,各种判断处理,先给创建的websocket对象绑定好了各种方法,再来处理数据。
所以他绑定的方法我们要跟过去下断点,都绑定后后,我们发现,他来到了onopen,就是我们最开始提到的,连接成功要处理的事件。
核心是这个 this.ping(),其他都是一些赋值操作,我们跟进ping函数
是不是看到了我们protobuf格式的老熟人关键词:serializeBinary,然后通过socket的send发给服务器。
代码分析:
const e = Math.max(1e4, Number(this.heartbeatDuration)); 得到e是10000。
if (this.client && 1 === this.client.socket.readyState) { //readyState准备状态,一开始是1嘛,可以理解,成立
const e = new p.PushFrame; //实例化proto对象
e.setPayloadType("hb"), //设置属性值
this.client.socket.send(e.serializeBinary()) //序列化后发送
}
this.pingTimer = window.setTimeout((()=>{
this.pingStarted && this.ping()
}
), e) //setTimeout:超时就执行ping,毫秒。相当于这里就是10秒ping一次嘛
可能是太久了,他发了两次,对吧,所以说这里就是一个点,我们要构造ping值,然后连接成功时候发一下,然后就能得到服务器返回的值了。
我们回到这里,先搞定这里再分析onmessage的,构造这个proto文件吧。
搜索 proto.pushproto.PushFrame.deserializeBinary
当然可以一个个慢慢扣,这里比较少。
①js转proto文件工具分享(只可解标准protobuf):
这里我采用我自己写的ast工具进行处理,把js尽可能转成proto文件,这里是我想分享的一点。
因为之前不是用渔歌的ast脚本加个try,逻辑改一下就搞定某方了,我就想,我能不能自己写一个呢,然后小学了一下ast,用的最基础的语法,我直接放出来吧,供大家使用,改良;你们写的肯定比我好。
//babel库及文件模块导入
const fs = require('fs');
//babel库相关,解析,转换,构建,生产
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");
const generator = require("@babel/generator").default;
//读取文件
let encode_file = "./test.js", decode_file = "./decode_result.proto"; //自己可以调整路径
if (process.argv.length > 2) {
encode_file = process.argv[2];
}
if (process.argv.length > 3) {
decode_file = process.argv[3];
}
//some function code
//判断类型
function get_id_type(id_type) {
switch (id_type) {
case "readString":
id_type = "string";
break;
case "readDouble":
id_type = "double";
break;
case "readInt32":
id_type = "int32";
break;
case "readInt64":
id_type = "int64";
break;
case "readFloat":
id_type = "float";
break;
case "readBool":
id_type = "bool";
break;
case "readPackedInt32":
id_type = "int32";
break;
case "readBytes":
id_type = "bytes";
break;
case "readEnum":
id_type = "readEnum";
break;
case "readPackedEnum":
id_type = "readPackedEnum";
break;
case "readUint64String":
id_type = "uint64";
break;
case "readInt64String":
id_type = "int64";
break;
case "readUint32":
id_type = "uint32";
break;
case "readUint64":
id_type = "uint64";
break;
}
return id_type
}
//首字母大写,处理
function titleCase(str) {
newStr = str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase();
return newStr;
}
//读取文件
let jscode = fs.readFileSync(encode_file, {encoding: "utf-8"});
//转换为ast树
let ast = parser.parse(jscode);
// console.log(ast)
text = 'syntax = "proto3";\n'
//处理message插件
const visitor =
{
//TODO write your code here!
AssignmentExpression(path, scope) {
// 外面大模型是没问题了,主要是里面细节
if (path.node.left.property) {
if (path.node.left.type == 'MemberExpression' &&
path.node.left.property.type == 'Identifier' &&
path.node.left.property.name == 'deserializeBinaryFromReader') {
//处理message名字
object_name = path.node.left.object.property.name
left_node = path.node.left
codename_list = generator(left_node).code.split('.');
codename_list.shift()
codename_list.pop()
object_name = codename_list.join('_');
// console.log(object_name);
text = text + 'message ' + object_name + '{\n'
//处理switch语句
if (path.node.right.type == 'FunctionExpression') {
path.traverse({
SwitchStatement(path2) {
//case语句
cases_list = path2.node.cases
// console.log(generator(path2.node).code);
// try {
for (i = 0; i {});
traverse(ast, visitor);
console.log('message部分完成!!!')
// // fs.writeFile('douyin_test_message完成.proto', text, (err) => {});
//
//
// // console.log(text);
//
fs.writeFile(decode_file, text, (err) => {});
首先,为什么能写出这个插件,我基于的是之前文章讲过的:(不要编辑它)
所以我就看了一下原始创建的js和某方和某音的结构,发现有相同点:
proto.SearchService.SearchResponse.deserializeBinaryFromReader = function(e, t) {
for (; t.nextField() && !t.isEndGroup(); ) {
switch (t.getFieldNumber()) {
case 1:
var r = t.readBool();
e.setStatus(r);
break;
case 2:
r = t.readString();
e.setMessage(r);
break;
case 3:
r = t.readInt64();
e.setCount(r);
break;
case 4:
r = new s.Resource;
t.readMessage(r, s.Resource.deserializeBinaryFromReader),
e.addResources(r);
break;
default:
t.skipField()
}
}
return e
}
proto.pushproto.PushHeader.deserializeBinaryFromReader = function(e, t) {
for (; t.nextField() && !t.isEndGroup(); ) {
switch (t.getFieldNumber()) {
case 1:
var i = t.readString();
e.setKey(i);
break;
case 2:
i = t.readString();
e.setValue(i);
break;
default:
t.skipField()
}
}
return e
}
我们放到网站上看看他们的结构:https://astexplorer.net/
发现都是 AssignmentExpression类型对象,然后有左:MemberExpression类型节点,它调用的方法名是deserializeBinaryFromReader
定位:我就是先以这个确定message对象:
处理:然后再处理细节,展开后:
先把message名字搞定:
用babel库的generator将左节点转成字符串源码,然后以"."分割去掉头(proto)尾(deserializeBinaryFromReader)
然后用"_"拼接,就搞定了message对象名字。我拿text变量来连接这些数据,下面就是处理右边的function了。
直接通过babel库的traverse定位到switch语句块,里面又有好几种情况,大家可以自行分析,我们可以通过慢慢调试,发现缺少的就慢慢补充起来,我基本上把可能的情况都写上去了,可是有个地方需要大家帮忙修改或者采用更好的方法优化一下吧。
如果报错可以在pycharm,debugger下看到报错细节,然后再进行调整,我发的基本上解决了。
下面是一些特殊情况,因为没保存之前的截图,临时找了一下:
其实下面也写了处理enum类型的模块,因为本身转化成js后是不可逆的,只能尽量还原,不可逆的点就是这个enum类型对象,你可以看回转化后的js,是没有一一对应关系的,所以无法关联到枚举类型变量,所以我就关了,没使用,想看枚举类型也能打开来看。
对了,上面有封装了一个函数,是处理数据类型的,如果你遇到了我这工具没有的类型,你自己可以添加到case语句里面,一看就懂。
代码大概介绍了一下,我们就拿上面拿出来的某方,某音的列子来试试效果。
一下子就处理完成,效果蛮好的,红色报错之前文章也说过原因,也教过怎么找这个对象,可以看回之前的文章。
②回到某音ping分析的地方:
我们跟进了那个方法的js文件,我们直接复制下来,丢到工具里面:
发现我们要的其实就是我圈起来的这两个,我们可以单独拿出来。
syntax = "proto3";
message pushproto_PushHeader{
string key=1;
string value=2;
}
message pushproto_PushFrame{
uint64 seqid=1;
uint64 logid=2;
uint64 service=3;
uint64 method=4;
repeated pushproto_PushHeader headers=5;
string payloadencoding=6;
string payloadtype=7;
bytes payload=8;
}
然后试试能不能得到想要的结果。(python语句看回我补充篇)
可以看到我们发过去后,服务器给我们发送了一个数据,猜测应该会在onMessage收到,然后处理,放行,果然来了。
我们跟过去看看吧:
又看到了我们的老朋友:反序列化的函数(deserializeBinary),所以我们可以采取构造proto文件处理,也可以使用python库blackboxprotobuf处理。因为我写了工具,就直接使用第一种方式,构造proto文件,也更直观。
我们先大概分析一下代码,再用工具去构造吧:
先看第一个语句:
const i = p.PushFrame.deserializeBinary(e.data) //反序列化得到数组对象
, o = r.Response.deserializeBinary(
//自执行
function(e) {
for (const t of Object.values(e.getHeadersList()))
//判断是否有compress_type和gzip值在i里面
if ("compress_type" === t.getKey() && "gzip" === t.getValue())
return !0; //返回真
return !1 //返回假
}(i)
//选择表达式,如果真就走第一个,假就走后面那个,得到的值再反序列化得到o,这里应该都是压缩过的,所以都会走第一个
? (0,n.ec)(i.getPayload()) : i.getPayload_asU8());
第二段:
//如果需要ack做的事情 自行百度什么是websocket的ack
if (o.getNeedAck()) {
let e = o.getInternalExt()
, t = o.getCursor();
(i.getHeadersList() || []).forEach((i=>{
"im-internal_ext" === (null == i ? void 0 : i.getKey()) && (e = null == i ? void 0 : i.getValue()),
"im-cursor" === (null == i ? void 0 : i.getKey()) && (t = null == i ? void 0 : i.getValue())
}
)),
this.internalExt = e,
this.cursor = t;
//下面才是核心
const a = new p.PushFrame;
a.setPayloadType("ack"),
a.setPayload(
//自执行:相当于把internalext传进去处理然后当成是payload的值
function(e) {
const t = [];
for (const i of e) {
const e = i.charCodeAt(0);
e > 6)),
t.push(128 + (63 & e))) : e > 12)),
t.push(128 + (e >> 6 & 63)),
t.push(128 + (63 & e)))
}
return Uint8Array.from(t)
}(e)
),
a.setLogid(i.getLogid()),
this.client.socket.send(a.serializeBinary())
}
最后
if ("msg" === i.getPayloadType() && (this.info("fetchSocketServer socket response: ", (()=>o.toObject())),
this.emit(o)),//这里就是把值展示出来了,里面处理值
"close" === i.getPayloadType())
return t(new Error("close by payloadtype"))
}
大概理清楚了onmessage的思路,先反序列化得到我们要的数据给后面展示,o就是我们要的数据对象,然后如果需要ack就发,最后通过emit函数处理展示值。
我们看看实际情况:
所以先通过n.ec函数处理,然后再来反序列化。
我们复制刚刚返回的,也能得到这个序列。
我们进去n.ec函数,然后移到最上面,发现是webpack打包的,然后我们可以看到
刚刚好Qe就是我们进来的位置,也就是这个函数,其实名字已经暴露了是解压,我们也可以把代码扣下来使用。
去掉开头结尾就好了:
function i(t) {
let e = t.length;
for (; --e >= 0; )
t[e] = 0
}
const n = 256
, r = 286
, s = 30
, l = 15
, o = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0])
, h = new Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13])
, d = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7])
, _ = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15])
, f = new Array(576);
i(f);
const c = new Array(60);
i(c);
const u = new Array(512);
i(u);
const w = new Array(256);
i(w);
const b = new Array(29);
i(b);
const g = new Array(s);
function m(t, e, a, i, n) {
this.static_tree = t,
this.extra_bits = e,
this.extra_base = a,
this.elems = i,
this.max_length = n,
this.has_stree = t && t.length
}
let p, k, v;
function y(t, e) {
this.dyn_tree = t,
this.max_code = 0,
this.stat_desc = e
}
i(g);
const x = t=>t >> 7)]
, z = (t,e)=>{
t.pending_buf[t.pending++] = 255 & e,
t.pending_buf[t.pending++] = e >>> 8 & 255
}
, A = (t,e,a)=>{
t.bi_valid > 16 - a ? (t.bi_buf |= e > 16 - t.bi_valid,
t.bi_valid += a - 16) : (t.bi_buf |= e {
A(t, a[2 * e], a[2 * e + 1])
}
, R = (t,e)=>{
let a = 0;
do {
a |= 1 & t,
t >>>= 1,
a 0);
return a >>> 1
}
, Z = (t,e,a)=>{
const i = new Array(16);
let n, r, s = 0;
for (n = 1; n {
let e;
for (e = 0; e {
t.bi_valid > 8 ? z(t, t.bi_buf) : t.bi_valid > 0 && (t.pending_buf[t.pending++] = t.bi_buf),
t.bi_buf = 0,
t.bi_valid = 0
}
, D = (t,e,a,i)=>{
const n = 2 * e
, r = 2 * a;
return t[n] {
const i = t.heap[a];
let n = a {
let i, r, s, l, d = 0;
if (0 !== t.last_lit)
do {
i = t.pending_buf[t.d_buf + 2 * d] {
const a = e.dyn_tree
, i = e.stat_desc.static_tree
, n = e.stat_desc.has_stree
, r = e.stat_desc.elems;
let s, o, h, d = -1;
for (t.heap_len = 0,
t.heap_max = 573,
s = 0; s > 1; s >= 1; s--)
O(t, a, s);
h = r;
do {
s = t.heap[1],
t.heap[1] = t.heap[t.heap_len--],
O(t, a, 1),
o = t.heap[1],
t.heap[--t.heap_max] = s,
t.heap[--t.heap_max] = o,
a[2 * h] = a[2 * s] + a[2 * o],
t.depth[h] = (t.depth
a[2 * s + 1] = a[2 * o + 1] = h,
t.heap[1] = h++,
O(t, a, 1)
} while (t.heap_len >= 2);
t.heap[--t.heap_max] = t.heap[1],
((t,e)=>{
const a = e.dyn_tree
, i = e.max_code
, n = e.stat_desc.static_tree
, r = e.stat_desc.has_stree
, s = e.stat_desc.extra_bits
, o = e.stat_desc.extra_base
, h = e.stat_desc.max_length;
let d, _, f, c, u, w, b = 0;
for (c = 0; c h && (c = h,
b++),
a[2 * _ + 1] = c,
_ > i || (t.bl_count[c]++,
u = 0,
_ >= o && (u = s[_ - o]),
w = a[2 * _],
t.opt_len += w * (c + u),
r && (t.static_len += w * (n[2 * _ + 1] + u)));
if (0 !== b) {
do {
for (c = h - 1; 0 === t.bl_count[c]; )
c--;
t.bl_count[c]--,
t.bl_count[c + 1] += 2,
t.bl_count[h]--,
b -= 2
} while (b > 0);
for (c = h; 0 !== c; c--)
for (_ = t.bl_count[c]; 0 !== _; )
f = t.heap[--d],
f > i || (a[2 * f + 1] !== c && (t.opt_len += (c - a[2 * f + 1]) * a[2 * f],
a[2 * f + 1] = c),
_--)
}
}
)(t, e),
Z(a, d, t.bl_count)
}
, N = (t,e,a)=>{
let i, n, r = -1, s = e[1], l = 0, o = 7, h = 4;
for (0 === s && (o = 138,
h = 3),
e[2 * (a + 1) + 1] = 65535,
i = 0; i {
let i, n, r = -1, s = e[1], l = 0, o = 7, h = 4;
for (0 === s && (o = 138,
h = 3),
i = 0; i {
A(t, 0 + (i ? 1 : 0), 3),
((t,e,a,i)=>{
S(t),
i && (z(t, a),
z(t, ~a)),
t.pending_buf.set(t.window.subarray(e, e + a), t.pending),
t.pending += a
}
)(t, e, a, !0)
}
;
var C = (t,e,a,i)=>{
let r, s, l = 0;
t.level > 0 ? (2 === t.strm.data_type && (t.strm.data_type = (t=>{
let e, a = 4093624447;
for (e = 0; e >>= 1)
if (1 & a && 0 !== t.dyn_ltree[2 * e])
return 0;
if (0 !== t.dyn_ltree[18] || 0 !== t.dyn_ltree[20] || 0 !== t.dyn_ltree[26])
return 1;
for (e = 32; e {
let e;
for (N(t, t.dyn_ltree, t.l_desc.max_code),
N(t, t.dyn_dtree, t.d_desc.max_code),
L(t, t.bl_desc),
e = 18; e >= 3 && 0 === t.bl_tree[2 * _[e] + 1]; e--)
;
return t.opt_len += 3 * (e + 1) + 5 + 5 + 4,
e
}
)(t),
r = t.opt_len + 3 + 7 >>> 3,
s = t.static_len + 3 + 7 >>> 3,
s {
let n;
for (A(t, e - 257, 5),
A(t, a - 1, 5),
A(t, i - 4, 4),
n = 0; n {
B || ((()=>{
let t, e, a, i, n;
const _ = new Array(16);
for (a = 0,
i = 0; i >= 7; i (t.pending_buf[t.d_buf + 2 * t.last_lit] = e >>> 8 & 255,
t.pending_buf[t.d_buf + 2 * t.last_lit + 1] = 255 & e,
t.pending_buf[t.l_buf + t.last_lit] = 255 & a,
t.last_lit++,
0 === e ? t.dyn_ltree[2 * a]++ : (t.matches++,
e--,
t.dyn_ltree[2 * (w[a] + n + 1)]++,
t.dyn_dtree[2 * x(e)]++),
t.last_lit === t.lit_bufsize - 1),
_tr_align: t=>{
A(t, 2, 3),
E(t, 256, f),
(t=>{
16 === t.bi_valid ? (z(t, t.bi_buf),
t.bi_buf = 0,
t.bi_valid = 0) : t.bi_valid >= 8 && (t.pending_buf[t.pending++] = 255 & t.bi_buf,
t.bi_buf >>= 8,
t.bi_valid -= 8)
}
)(t)
}
};
var M = (t,e,a,i)=>{
let n = 65535 & t | 0
, r = t >>> 16 & 65535 | 0
, s = 0;
for (; 0 !== a; ) {
s = a > 2e3 ? 2e3 : a,
a -= s;
do {
n = n + e[i++] | 0,
r = r + n | 0
} while (--s);
n %= 65521,
r %= 65521
}
return n | r {
let t, e = [];
for (var a = 0; a >> 1 : t >>> 1;
e[a] = t
}
return e
}
)());
var j = (t,e,a,i)=>{
const n = K
, r = i + a;
t ^= -1;
for (let s = i; s >> 8 ^ n[255 & (t ^ e
return -1 ^ t
}
, P = {
2: "need dictionary",
1: "stream end",
0: "",
"-1": "file error",
"-2": "stream error",
"-3": "data error",
"-4": "insufficient memory",
"-5": "buffer error",
"-6": "incompatible version"
}
, Y = {
Z_NO_FLUSH: 0,
Z_PARTIAL_FLUSH: 1,
Z_SYNC_FLUSH: 2,
Z_FULL_FLUSH: 3,
Z_FINISH: 4,
Z_BLOCK: 5,
Z_TREES: 6,
Z_OK: 0,
Z_STREAM_END: 1,
Z_NEED_DICT: 2,
Z_ERRNO: -1,
Z_STREAM_ERROR: -2,
Z_DATA_ERROR: -3,
Z_MEM_ERROR: -4,
Z_BUF_ERROR: -5,
Z_NO_COMPRESSION: 0,
Z_BEST_SPEED: 1,
Z_BEST_COMPRESSION: 9,
Z_DEFAULT_COMPRESSION: -1,
Z_FILTERED: 1,
Z_HUFFMAN_ONLY: 2,
Z_RLE: 3,
Z_FIXED: 4,
Z_DEFAULT_STRATEGY: 0,
Z_BINARY: 0,
Z_TEXT: 1,
Z_UNKNOWN: 2,
Z_DEFLATED: 8
};
const {_tr_init: G, _tr_stored_block: X, _tr_flush_block: W, _tr_tally: q, _tr_align: J} = H
, {Z_NO_FLUSH: Q, Z_PARTIAL_FLUSH: V, Z_FULL_FLUSH: $, Z_FINISH: tt, Z_BLOCK: et, Z_OK: at, Z_STREAM_END: it, Z_STREAM_ERROR: nt, Z_DATA_ERROR: rt, Z_BUF_ERROR: st, Z_DEFAULT_COMPRESSION: lt, Z_FILTERED: ot, Z_HUFFMAN_ONLY: ht, Z_RLE: dt, Z_FIXED: _t, Z_DEFAULT_STRATEGY: ft, Z_UNKNOWN: ct, Z_DEFLATED: ut} = Y
, wt = 258
, bt = 262
, gt = 103
, mt = 113
, pt = 666
, kt = (t,e)=>(t.msg = P[e],
e)
, vt = t=>(t 4 ? 9 : 0)
, yt = t=>{
let e = t.length;
for (; --e >= 0; )
t[e] = 0
}
;
let xt = (t,e,a)=>(e {
const e = t.state;
let a = e.pending;
a > t.avail_out && (a = t.avail_out),
0 !== a && (t.output.set(e.pending_buf.subarray(e.pending_out, e.pending_out + a), t.next_out),
t.next_out += a,
e.pending_out += a,
t.total_out += a,
t.avail_out -= a,
e.pending -= a,
0 === e.pending && (e.pending_out = 0))
}
, At = (t,e)=>{
W(t, t.block_start >= 0 ? t.block_start : -1, t.strstart - t.block_start, e),
t.block_start = t.strstart,
zt(t.strm)
}
, Et = (t,e)=>{
t.pending_buf[t.pending++] = e
}
, Rt = (t,e)=>{
t.pending_buf[t.pending++] = e >>> 8 & 255,
t.pending_buf[t.pending++] = 255 & e
}
, Zt = (t,e,a,i)=>{
let n = t.avail_in;
return n > i && (n = i),
0 === n ? 0 : (t.avail_in -= n,
e.set(t.input.subarray(t.next_in, t.next_in + n), a),
1 === t.state.wrap ? t.adler = M(t.adler, e, n, a) : 2 === t.state.wrap && (t.adler = j(t.adler, e, n, a)),
t.next_in += n,
t.total_in += n,
n)
}
, Ut = (t,e)=>{
let a, i, n = t.max_chain_length, r = t.strstart, s = t.prev_length, l = t.nice_match;
const o = t.strstart > t.w_size - bt ? t.strstart - (t.w_size - bt) : 0
, h = t.window
, d = t.w_mask
, _ = t.prev
, f = t.strstart + wt;
let c = h[r + s - 1]
, u = h[r + s];
t.prev_length >= t.good_match && (n >>= 2),
l > t.lookahead && (l = t.lookahead);
do {
if (a = e,
h[a + s] === u && h[a + s - 1] === c && h[a] === h[r] && h[++a] === h[r + 1]) {
r += 2,
a++;
do {} while (h[++r] === h[++a] && h[++r] === h[++a] && h[++r] === h[++a] && h[++r] === h[++a] && h[++r] === h[++a] && h[++r] === h[++a] && h[++r] === h[++a] && h[++r] === h[++a] && r s) {
if (t.match_start = e,
s = i,
i >= l)
break;
c = h[r + s - 1],
u = h[r + s]
}
}
} while ((e = _[e & d]) > o && 0 != --n);
return s {
const e = t.w_size;
let a, i, n, r, s;
do {
if (r = t.window_size - t.lookahead - t.strstart,
t.strstart >= e + (e - bt)) {
t.window.set(t.window.subarray(e, e + e), 0),
t.match_start -= e,
t.strstart -= e,
t.block_start -= e,
i = t.hash_size,
a = i;
do {
n = t.head[--a],
t.head[a] = n >= e ? n - e : 0
} while (--i);
i = e,
a = i;
do {
n = t.prev[--a],
t.prev[a] = n >= e ? n - e : 0
} while (--i);
r += e
}
if (0 === t.strm.avail_in)
break;
if (i = Zt(t.strm, t.window, t.strstart + t.lookahead, r),
t.lookahead += i,
t.lookahead + t.insert >= 3)
for (s = t.strstart - t.insert,
t.ins_h = t.window
t.ins_h = xt(t, t.ins_h, t.window[s + 1]); t.insert && (t.ins_h = xt(t, t.ins_h, t.window[s + 3 - 1]),
t.prev[s & t.w_mask] = t.head[t.ins_h],
t.head[t.ins_h] = s,
s++,
t.insert--,
!(t.lookahead + t.insert {
let a, i;
for (; ; ) {
if (t.lookahead = 3 && (t.ins_h = xt(t, t.ins_h, t.window[t.strstart + 3 - 1]),
a = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h],
t.head[t.ins_h] = t.strstart),
0 !== a && t.strstart - a = 3)
if (i = q(t, t.strstart - t.match_start, t.match_length - 3),
t.lookahead -= t.match_length,
t.match_length = 3) {
t.match_length--;
do {
t.strstart++,
t.ins_h = xt(t, t.ins_h, t.window[t.strstart + 3 - 1]),
a = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h],
t.head[t.ins_h] = t.strstart
} while (0 != --t.match_length);
t.strstart++
} else
t.strstart += t.match_length,
t.match_length = 0,
t.ins_h = t.window[t.strstart],
t.ins_h = xt(t, t.ins_h, t.window[t.strstart + 1]);
else
i = q(t, 0, t.window[t.strstart]),
t.lookahead--,
t.strstart++;
if (i && (At(t, !1),
0 === t.strm.avail_out))
return 1
}
return t.insert = t.strstart {
let a, i, n;
for (; ; ) {
if (t.lookahead = 3 && (t.ins_h = xt(t, t.ins_h, t.window[t.strstart + 3 - 1]),
a = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h],
t.head[t.ins_h] = t.strstart),
t.prev_length = t.match_length,
t.prev_match = t.match_start,
t.match_length = 2,
0 !== a && t.prev_length 4096) && (t.match_length = 2)),
t.prev_length >= 3 && t.match_length {
let a = 65535;
for (a > t.pending_buf_size - 5 && (a = t.pending_buf_size - 5); ; ) {
if (t.lookahead = i) && (t.lookahead = t.strstart - i,
t.strstart = i,
At(t, !1),
0 === t.strm.avail_out))
return 1;
if (t.strstart - t.block_start >= t.w_size - bt && (At(t, !1),
0 === t.strm.avail_out))
return 1
}
return t.insert = 0,
e === tt ? (At(t, !0),
0 === t.strm.avail_out ? 3 : 4) : (t.strstart > t.block_start && (At(t, !1),
t.strm.avail_out),
1)
}
)), new Tt(4,4,8,4,Dt), new Tt(4,5,16,8,Dt), new Tt(4,6,32,32,Dt), new Tt(4,4,16,16,Ot), new Tt(8,16,32,32,Ot), new Tt(8,16,128,128,Ot), new Tt(8,32,128,256,Ot), new Tt(32,128,258,1024,Ot), new Tt(32,258,258,4096,Ot)];
function Nt() {
this.strm = null,
this.status = 0,
this.pending_buf = null,
this.pending_buf_size = 0,
this.pending_out = 0,
this.pending = 0,
this.wrap = 0,
this.gzhead = null,
this.gzindex = 0,
this.method = ut,
this.last_flush = -1,
this.w_size = 0,
this.w_bits = 0,
this.w_mask = 0,
this.window = null,
this.window_size = 0,
this.prev = null,
this.head = null,
this.ins_h = 0,
this.hash_size = 0,
this.hash_bits = 0,
this.hash_mask = 0,
this.hash_shift = 0,
this.block_start = 0,
this.match_length = 0,
this.prev_match = 0,
this.match_available = 0,
this.strstart = 0,
this.match_start = 0,
this.lookahead = 0,
this.prev_length = 0,
this.max_chain_length = 0,
this.max_lazy_match = 0,
this.level = 0,
this.strategy = 0,
this.good_match = 0,
this.nice_match = 0,
this.dyn_ltree = new Uint16Array(1146),
this.dyn_dtree = new Uint16Array(122),
this.bl_tree = new Uint16Array(78),
yt(this.dyn_ltree),
yt(this.dyn_dtree),
yt(this.bl_tree),
this.l_desc = null,
this.d_desc = null,
this.bl_desc = null,
this.bl_count = new Uint16Array(16),
this.heap = new Uint16Array(573),
yt(this.heap),
this.heap_len = 0,
this.heap_max = 0,
this.depth = new Uint16Array(573),
yt(this.depth),
this.l_buf = 0,
this.lit_bufsize = 0,
this.last_lit = 0,
this.d_buf = 0,
this.opt_len = 0,
this.static_len = 0,
this.matches = 0,
this.insert = 0,
this.bi_buf = 0,
this.bi_valid = 0
}
const Ft = t=>{
if (!t || !t.state)
return kt(t, nt);
t.total_in = t.total_out = 0,
t.data_type = ct;
const e = t.state;
return e.pending = 0,
e.pending_out = 0,
e.wrap {
const e = Ft(t);
var a;
return e === at && ((a = t.state).window_size = 2 * a.w_size,
yt(a.head),
a.max_lazy_match = Lt[a.level].max_lazy,
a.good_match = Lt[a.level].good_length,
a.nice_match = Lt[a.level].nice_length,
a.max_chain_length = Lt[a.level].max_chain,
a.strstart = 0,
a.block_start = 0,
a.lookahead = 0,
a.insert = 0,
a.match_length = a.prev_length = 2,
a.match_available = 0,
a.ins_h = 0),
e
}
, It = (t,e,a,i,n,r)=>{
if (!t)
return nt;
let s = 1;
if (e === lt && (e = 6),
i 15 && (s = 2,
i -= 16),
n 9 || a !== ut || i 15 || e 9 || r _t)
return kt(t, nt);
8 === i && (i = 9);
const l = new Nt;
return t.state = l,
l.strm = t,
l.wrap = s,
l.gzhead = null,
l.w_bits = i,
l.w_size = 1 It(t, e, ut, 15, 8, ft),
deflateInit2: It,
deflateReset: Bt,
deflateResetKeep: Ft,
deflateSetHeader: (t,e)=>t && t.state ? 2 !== t.state.wrap ? nt : (t.state.gzhead = e,
at) : nt,
deflate: (t,e)=>{
let a, i;
if (!t || !t.state || e > et || e > 8 & 255),
Et(n, n.gzhead.time >> 16 & 255),
Et(n, n.gzhead.time >> 24 & 255),
Et(n, 9 === n.level ? 2 : n.strategy >= ht || n.level > 8 & 255)),
n.gzhead.hcrc && (t.adler = j(t.adler, n.pending_buf, n.pending, 0)),
n.gzindex = 0,
n.status = 69) : (Et(n, 0),
Et(n, 0),
Et(n, 0),
Et(n, 0),
Et(n, 0),
Et(n, 9 === n.level ? 2 : n.strategy >= ht || n.level = ht || n.level >> 16),
Rt(n, 65535 & t.adler)),
t.adler = 1
}
if (69 === n.status)
if (n.gzhead.extra) {
for (a = n.pending; n.gzindex a && (t.adler = j(t.adler, n.pending_buf, n.pending - a, a)),
zt(t),
a = n.pending,
n.pending !== n.pending_buf_size)); )
Et(n, 255 & n.gzhead.extra[n.gzindex]),
n.gzindex++;
n.gzhead.hcrc && n.pending > a && (t.adler = j(t.adler, n.pending_buf, n.pending - a, a)),
n.gzindex === n.gzhead.extra.length && (n.gzindex = 0,
n.status = 73)
} else
n.status = 73;
if (73 === n.status)
if (n.gzhead.name) {
a = n.pending;
do {
if (n.pending === n.pending_buf_size && (n.gzhead.hcrc && n.pending > a && (t.adler = j(t.adler, n.pending_buf, n.pending - a, a)),
zt(t),
a = n.pending,
n.pending === n.pending_buf_size)) {
i = 1;
break
}
i = n.gzindex a && (t.adler = j(t.adler, n.pending_buf, n.pending - a, a)),
0 === i && (n.gzindex = 0,
n.status = 91)
} else
n.status = 91;
if (91 === n.status)
if (n.gzhead.comment) {
a = n.pending;
do {
if (n.pending === n.pending_buf_size && (n.gzhead.hcrc && n.pending > a && (t.adler = j(t.adler, n.pending_buf, n.pending - a, a)),
zt(t),
a = n.pending,
n.pending === n.pending_buf_size)) {
i = 1;
break
}
i = n.gzindex a && (t.adler = j(t.adler, n.pending_buf, n.pending - a, a)),
0 === i && (n.status = gt)
} else
n.status = gt;
if (n.status === gt && (n.gzhead.hcrc ? (n.pending + 2 > n.pending_buf_size && zt(t),
n.pending + 2 > 8 & 255),
t.adler = 0,
n.status = mt)) : n.status = mt),
0 !== n.pending) {
if (zt(t),
0 === t.avail_out)
return n.last_flush = -1,
at
} else if (0 === t.avail_in && vt(e) {
let a;
for (; ; ) {
if (0 === t.lookahead && (St(t),
0 === t.lookahead)) {
if (e === Q)
return 1;
break
}
if (t.match_length = 0,
a = q(t, 0, t.window[t.strstart]),
t.lookahead--,
t.strstart++,
a && (At(t, !1),
0 === t.strm.avail_out))
return 1
}
return t.insert = 0,
e === tt ? (At(t, !0),
0 === t.strm.avail_out ? 3 : 4) : t.last_lit && (At(t, !1),
0 === t.strm.avail_out) ? 1 : 2
}
)(n, e) : n.strategy === dt ? ((t,e)=>{
let a, i, n, r;
const s = t.window;
for (; ; ) {
if (t.lookahead = 3 && t.strstart > 0 && (n = t.strstart - 1,
i = s[n],
i === s[++n] && i === s[++n] && i === s[++n])) {
r = t.strstart + wt;
do {} while (i === s[++n] && i === s[++n] && i === s[++n] && i === s[++n] && i === s[++n] && i === s[++n] && i === s[++n] && i === s[++n] && n t.lookahead && (t.match_length = t.lookahead)
}
if (t.match_length >= 3 ? (a = q(t, 1, t.match_length - 3),
t.lookahead -= t.match_length,
t.strstart += t.match_length,
t.match_length = 0) : (a = q(t, 0, t.window[t.strstart]),
t.lookahead--,
t.strstart++),
a && (At(t, !1),
0 === t.strm.avail_out))
return 1
}
return t.insert = 0,
e === tt ? (At(t, !0),
0 === t.strm.avail_out ? 3 : 4) : t.last_lit && (At(t, !1),
0 === t.strm.avail_out) ? 1 : 2
}
)(n, e) : Lt[n.level].func(n, e);
if (3 !== a && 4 !== a || (n.status = pt),
1 === a || 3 === a)
return 0 === t.avail_out && (n.last_flush = -1),
at;
if (2 === a && (e === V ? J(n) : e !== et && (X(n, 0, 0, !1),
e === $ && (yt(n.head),
0 === n.lookahead && (n.strstart = 0,
n.block_start = 0,
n.insert = 0))),
zt(t),
0 === t.avail_out))
return n.last_flush = -1,
at
}
return e !== tt ? at : n.wrap > 8 & 255),
Et(n, t.adler >> 16 & 255),
Et(n, t.adler >> 24 & 255),
Et(n, 255 & t.total_in),
Et(n, t.total_in >> 8 & 255),
Et(n, t.total_in >> 16 & 255),
Et(n, t.total_in >> 24 & 255)) : (Rt(n, t.adler >>> 16),
Rt(n, 65535 & t.adler)),
zt(t),
n.wrap > 0 && (n.wrap = -n.wrap),
0 !== n.pending ? at : it)
}
,
deflateEnd: t=>{
if (!t || !t.state)
return nt;
const e = t.state.status;
return 42 !== e && 69 !== e && 73 !== e && 91 !== e && e !== gt && e !== mt && e !== pt ? kt(t, nt) : (t.state = null,
e === mt ? kt(t, rt) : at)
}
,
deflateSetDictionary: (t,e)=>{
let a = e.length;
if (!t || !t.state)
return nt;
const i = t.state
, n = i.wrap;
if (2 === n || 1 === n && 42 !== i.status || i.lookahead)
return nt;
if (1 === n && (t.adler = M(t.adler, e, a, 0)),
i.wrap = 0,
a >= i.w_size) {
0 === n && (yt(i.head),
i.strstart = 0,
i.block_start = 0,
i.insert = 0);
let t = new Uint8Array(i.w_size);
t.set(e.subarray(a - i.w_size, a), 0),
e = t,
a = i.w_size
}
const r = t.avail_in
, s = t.next_in
, l = t.input;
for (t.avail_in = a,
t.next_in = 0,
t.input = e,
St(i); i.lookahead >= 3; ) {
let t = i.strstart
, e = i.lookahead - 2;
do {
i.ins_h = xt(i, i.ins_h, i.window[t + 3 - 1]),
i.prev[t & i.w_mask] = i.head[i.ins_h],
i.head[i.ins_h] = t,
t++
} while (--e);
i.strstart = t,
i.lookahead = 2,
St(i)
}
return i.strstart += i.lookahead,
i.block_start = i.strstart,
i.insert = i.lookahead,
i.lookahead = 0,
i.match_length = i.prev_length = 2,
i.match_available = 0,
t.next_in = s,
t.input = l,
t.avail_in = r,
i.wrap = n,
at
}
,
deflateInfo: "pako deflate (from Nodeca project)"
};
const Ht = (t,e)=>Object.prototype.hasOwnProperty.call(t, e);
var Mt = function(t) {
const e = Array.prototype.slice.call(arguments, 1);
for (; e.length; ) {
const a = e.shift();
if (a) {
if ("object" != typeof a)
throw new TypeError(a + "must be non-object");
for (const e in a)
Ht(a, e) && (t[e] = a[e])
}
}
return t
}
, Kt = t=>{
let e = 0;
for (let i = 0, n = t.length; i = 252 ? 6 : ha >= 248 ? 5 : ha >= 240 ? 4 : ha >= 224 ? 3 : ha >= 192 ? 2 : 1;
Pt[254] = Pt[254] = 1;
var Yt = t=>{
if ("function" == typeof TextEncoder && TextEncoder.prototype.encode)
return (new TextEncoder).encode(t);
let e, a, i, n, r, s = t.length, l = 0;
for (n = 0; n >> 6,
e[r++] = 128 | 63 & a) : a >> 12,
e[r++] = 128 | a >>> 6 & 63,
e[r++] = 128 | 63 & a) : (e[r++] = 240 | a >>> 18,
e[r++] = 128 | a >>> 12 & 63,
e[r++] = 128 | a >>> 6 & 63,
e[r++] = 128 | 63 & a);
return e
}
, Gt = (t,e)=>{
const a = e || t.length;
if ("function" == typeof TextDecoder && TextDecoder.prototype.decode)
return (new TextDecoder).decode(t.subarray(0, e));
let i, n;
const r = new Array(2 * a);
for (n = 0,
i = 0; i 4)
r[n++] = 65533,
i += s - 1;
else {
for (e &= 2 === s ? 31 : 3 === s ? 15 : 7; s > 1 && i 1 ? r[n++] = 65533 : e > 10 & 1023,
r[n++] = 56320 | 1023 & e)
}
}
return ((t,e)=>{
if (e {
(e = e || t.length) > t.length && (e = t.length);
let a = e - 1;
for (; a >= 0 && 128 == (192 & t[a]); )
a--;
return a e ? a : e
}
;
var Wt = function() {
this.input = null,
this.next_in = 0,
this.avail_in = 0,
this.total_in = 0,
this.output = null,
this.next_out = 0,
this.avail_out = 0,
this.total_out = 0,
this.msg = "",
this.state = null,
this.data_type = 2,
this.adler = 0
};
const qt = Object.prototype.toString
, {Z_NO_FLUSH: Jt, Z_SYNC_FLUSH: Qt, Z_FULL_FLUSH: Vt, Z_FINISH: $t, Z_OK: te, Z_STREAM_END: ee, Z_DEFAULT_COMPRESSION: ae, Z_DEFAULT_STRATEGY: ie, Z_DEFLATED: ne} = Y;
function re(t) {
this.options = Mt({
level: ae,
method: ne,
chunkSize: 16384,
windowBits: 15,
memLevel: 8,
strategy: ie
}, t || {});
let e = this.options;
e.raw && e.windowBits > 0 ? e.windowBits = -e.windowBits : e.gzip && e.windowBits > 0 && e.windowBits 0 && this.onData(a.output.subarray(0, a.next_out)),
n = Ct.deflateEnd(this.strm),
this.onEnd(n),
this.ended = !0,
n === te;
if (0 !== a.avail_out) {
if (r > 0 && a.next_out > 0)
this.onData(a.output.subarray(0, a.next_out)),
a.avail_out = 0;
else if (0 === a.avail_in)
break
} else
this.onData(a.output)
}
return !0
}
,
re.prototype.onData = function(t) {
this.chunks.push(t)
}
,
re.prototype.onEnd = function(t) {
t === te && (this.result = Kt(this.chunks)),
this.chunks = [],
this.err = t,
this.msg = this.strm.msg
}
;
var le = {
Deflate: re,
deflate: se,
deflateRaw: function(t, e) {
return (e = e || {}).raw = !0,
se(t, e)
},
gzip: function(t, e) {
return (e = e || {}).gzip = !0,
se(t, e)
},
constants: Y
};
var oe = function(t, e) {
let a, i, n, r, s, l, o, h, d, _, f, c, u, w, b, g, m, p, k, v, y, x, z, A;
const E = t.state;
a = t.next_in,
z = t.input,
i = a + (t.avail_in - 5),
n = t.next_out,
A = t.output,
r = n - (e - t.avail_out),
s = n + (t.avail_out - 257),
l = E.dmax,
o = E.wsize,
h = E.whave,
d = E.wnext,
_ = E.window,
f = E.hold,
c = E.bits,
u = E.lencode,
w = E.distcode,
b = (1 >> 24,
f >>>= p,
c -= p,
p = m >>> 16 & 255,
0 === p)
A[n++] = 65535 & m;
else {
if (!(16 & p)) {
if (0 == (64 & p)) {
m = u[(65535 & m) + (f & (1 >>= p,
c -= p),
c >> 24,
f >>>= p,
c -= p,
p = m >>> 16 & 255,
!(16 & p)) {
if (0 == (64 & p)) {
m = w[(65535 & m) + (f & (1 l) {
t.msg = "invalid distance too far back",
E.mode = 30;
break t
}
if (f >>>= p,
c -= p,
p = n - r,
v > p) {
if (p = v - p,
p > h && E.sane) {
t.msg = "invalid distance too far back",
E.mode = 30;
break t
}
if (y = 0,
x = _,
0 === d) {
if (y += o - p,
p 2; )
A[n++] = x[y++],
A[n++] = x[y++],
A[n++] = x[y++],
k -= 3;
k && (A[n++] = x[y++],
k > 1 && (A[n++] = x[y++]))
} else {
y = n - v;
do {
A[n++] = A[y++],
A[n++] = A[y++],
A[n++] = A[y++],
k -= 3
} while (k > 2);
k && (A[n++] = A[y++],
k > 1 && (A[n++] = A[y++]))
}
break
}
}
break
}
} while (a > 3,
a -= k,
c -= k {
const o = l.bits;
let h, d, _, f, c, u, w = 0, b = 0, g = 0, m = 0, p = 0, k = 0, v = 0, y = 0, x = 0, z = 0, A = null, E = 0;
const R = new Uint16Array(16)
, Z = new Uint16Array(16);
let U, S, D, O = null, T = 0;
for (w = 0; w = 1 && 0 === R[m]; m--)
;
if (p > m && (p = m),
0 === m)
return n[r++] = 20971520,
n[r++] = 20971520,
l.bits = 1,
0;
for (g = 1; g 0 && (0 === t || 1 !== m))
return -1;
for (Z[1] = 0,
w = 1; w 852 || 2 === t && x > 592)
return 1;
for (; ; ) {
U = w - v,
s u ? (S = O[T + s],
D = A[E + s]) : (S = 96,
D = 0),
h = 1 > v) + d] = U >= 1;
if (0 !== h ? (z &= h - 1,
z += h) : z = 0,
b++,
0 == --R[w]) {
if (w === m)
break;
w = e[a + s]
}
if (w > p && (z & f) !== _) {
for (0 === v && (v = p),
c += g,
k = w - v,
y = 1 852 || 2 === t && x > 592)
return 1;
_ = z & f,
n[_] = p (t >>> 24 & 255) + (t >>> 8 & 65280) + ((65280 & t) {
if (!t || !t.state)
return ve;
const e = t.state;
return t.total_in = t.total_out = e.total = 0,
t.msg = "",
e.wrap && (t.adler = 1 & e.wrap),
e.mode = 1,
e.last = 0,
e.havedict = 0,
e.dmax = 32768,
e.head = null,
e.hold = 0,
e.bits = 0,
e.lencode = e.lendyn = new Int32Array(852),
e.distcode = e.distdyn = new Int32Array(592),
e.sane = 1,
e.back = -1,
me
}
, De = t=>{
if (!t || !t.state)
return ve;
const e = t.state;
return e.wsize = 0,
e.whave = 0,
e.wnext = 0,
Se(t)
}
, Oe = (t,e)=>{
let a;
if (!t || !t.state)
return ve;
const i = t.state;
return e > 4),
e 15) ? ve : (null !== i.window && i.wbits !== e && (i.window = null),
i.wrap = a,
i.wbits = e,
De(t))
}
, Te = (t,e)=>{
if (!t)
return ve;
const a = new Ue;
t.state = a,
a.window = null;
const i = Oe(t, e);
return i !== me && (t.state = null),
i
}
;
let Le, Ne, Fe = !0;
const Be = t=>{
if (Fe) {
Le = new Int32Array(512),
Ne = new Int32Array(32);
let e = 0;
for (; e {
let n;
const r = t.state;
return null === r.window && (r.wsize = 1 = r.wsize ? (r.window.set(e.subarray(a - r.wsize, a), 0),
r.wnext = 0,
r.whave = r.wsize) : (n = r.wsize - r.wnext,
n > i && (n = i),
r.window.set(e.subarray(a - i, a - i + n), r.wnext),
(i -= n) ? (r.window.set(e.subarray(a - i, a), 0),
r.wnext = i,
r.whave = r.wsize) : (r.wnext += n,
r.wnext === r.wsize && (r.wnext = 0),
r.whave Te(t, 15),
inflateInit2: Te,
inflate: (t,e)=>{
let a, i, n, r, s, l, o, h, d, _, f, c, u, w, b, g, m, p, k, v, y, x, z = 0;
const A = new Uint8Array(4);
let E, R;
const Z = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
if (!t || !t.state || !t.output || !t.input && 0 !== t.avail_in)
return ve;
a = t.state,
a.mode === Ee && (a.mode = 13),
s = t.next_out,
n = t.output,
o = t.avail_out,
r = t.next_in,
i = t.input,
l = t.avail_in,
h = a.hold,
d = a.bits,
_ = l,
f = o,
x = me;
t: for (; ; )
switch (a.mode) {
case 1:
if (0 === a.wrap) {
a.mode = 13;
break
}
for (; d >> 8 & 255,
a.check = j(a.check, A, 2, 0),
h = 0,
d = 0,
a.mode = 2;
break
}
if (a.flags = 0,
a.head && (a.head.done = !1),
!(1 & a.wrap) || (((255 & h) > 8)) % 31) {
t.msg = "incorrect header check",
a.mode = Re;
break
}
if ((15 & h) !== Ae) {
t.msg = "unknown compression method",
a.mode = Re;
break
}
if (h >>>= 4,
d -= 4,
y = 8 + (15 & h),
0 === a.wbits)
a.wbits = y;
else if (y > a.wbits) {
t.msg = "invalid window size",
a.mode = Re;
break
}
a.dmax = 1 > 8 & 1),
512 & a.flags && (A[0] = 255 & h,
A[1] = h >>> 8 & 255,
a.check = j(a.check, A, 2, 0)),
h = 0,
d = 0,
a.mode = 3;
case 3:
for (; d >> 8 & 255,
A[2] = h >>> 16 & 255,
A[3] = h >>> 24 & 255,
a.check = j(a.check, A, 4, 0)),
h = 0,
d = 0,
a.mode = 4;
case 4:
for (; d > 8),
512 & a.flags && (A[0] = 255 & h,
A[1] = h >>> 8 & 255,
a.check = j(a.check, A, 2, 0)),
h = 0,
d = 0,
a.mode = 5;
case 5:
if (1024 & a.flags) {
for (; d >> 8 & 255,
a.check = j(a.check, A, 2, 0)),
h = 0,
d = 0
} else
a.head && (a.head.extra = null);
a.mode = 6;
case 6:
if (1024 & a.flags && (c = a.length,
c > l && (c = l),
c && (a.head && (y = a.head.extra_len - a.length,
a.head.extra || (a.head.extra = new Uint8Array(a.head.extra_len)),
a.head.extra.set(i.subarray(r, r + c), y)),
512 & a.flags && (a.check = j(a.check, i, c, r)),
l -= c,
r += c,
a.length -= c),
a.length))
break t;
a.length = 0,
a.mode = 7;
case 7:
if (2048 & a.flags) {
if (0 === l)
break t;
c = 0;
do {
y = i[r + c++],
a.head && y && a.length > 9 & 1,
a.head.done = !0),
t.adler = a.check = 0,
a.mode = Ee;
break;
case 10:
for (; d >>= 7 & d,
d -= 7 & d,
a.mode = 27;
break
}
for (; d >>= 1,
d -= 1,
3 & h) {
case 0:
a.mode = 14;
break;
case 1:
if (Be(a),
a.mode = 20,
e === ge) {
h >>>= 2,
d -= 2;
break t
}
break;
case 2:
a.mode = 17;
break;
case 3:
t.msg = "invalid block type",
a.mode = Re
}
h >>>= 2,
d -= 2;
break;
case 14:
for (h >>>= 7 & d,
d -= 7 & d; d >> 16 ^ 65535)) {
t.msg = "invalid stored block lengths",
a.mode = Re;
break
}
if (a.length = 65535 & h,
h = 0,
d = 0,
a.mode = 15,
e === ge)
break t;
case 15:
a.mode = 16;
case 16:
if (c = a.length,
c) {
if (c > l && (c = l),
c > o && (c = o),
0 === c)
break t;
n.set(i.subarray(r, r + c), s),
l -= c,
r += c,
o -= c,
s += c,
a.length -= c;
break
}
a.mode = Ee;
break;
case 17:
for (; d >>= 5,
d -= 5,
a.ndist = 1 + (31 & h),
h >>>= 5,
d -= 5,
a.ncode = 4 + (15 & h),
h >>>= 4,
d -= 4,
a.nlen > 286 || a.ndist > 30) {
t.msg = "too many length or distance symbols",
a.mode = Re;
break
}
a.have = 0,
a.mode = 18;
case 18:
for (; a.have >>= 3,
d -= 3
}
for (; a.have >> 24,
g = z >>> 16 & 255,
m = 65535 & z,
!(b >>= b,
d -= b,
a.lens[a.have++] = m;
else {
if (16 === m) {
for (R = b + 2; d >>= b,
d -= b,
0 === a.have) {
t.msg = "invalid bit length repeat",
a.mode = Re;
break
}
y = a.lens[a.have - 1],
c = 3 + (3 & h),
h >>>= 2,
d -= 2
} else if (17 === m) {
for (R = b + 3; d >>= b,
d -= b,
y = 0,
c = 3 + (7 & h),
h >>>= 3,
d -= 3
} else {
for (R = b + 7; d >>= b,
d -= b,
y = 0,
c = 11 + (127 & h),
h >>>= 7,
d -= 7
}
if (a.have + c > a.nlen + a.ndist) {
t.msg = "invalid bit length repeat",
a.mode = Re;
break
}
for (; c--; )
a.lens[a.have++] = y
}
}
if (a.mode === Re)
break;
if (0 === a.lens[256]) {
t.msg = "invalid code -- missing end-of-block",
a.mode = Re;
break
}
if (a.lenbits = 9,
E = {
bits: a.lenbits
},
x = ue(1, a.lens, 0, a.nlen, a.lencode, 0, a.work, E),
a.lenbits = E.bits,
x) {
t.msg = "invalid literal/lengths set",
a.mode = Re;
break
}
if (a.distbits = 6,
a.distcode = a.distdyn,
E = {
bits: a.distbits
},
x = ue(2, a.lens, a.nlen, a.ndist, a.distcode, 0, a.work, E),
a.distbits = E.bits,
x) {
t.msg = "invalid distances set",
a.mode = Re;
break
}
if (a.mode = 20,
e === ge)
break t;
case 20:
a.mode = 21;
case 21:
if (l >= 6 && o >= 258) {
t.next_out = s,
t.avail_out = o,
t.next_in = r,
t.avail_in = l,
a.hold = h,
a.bits = d,
oe(t, f),
s = t.next_out,
n = t.output,
o = t.avail_out,
r = t.next_in,
i = t.input,
l = t.avail_in,
h = a.hold,
d = a.bits,
a.mode === Ee && (a.back = -1);
break
}
for (a.back = 0; z = a.lencode[h & (1 >> 24,
g = z >>> 16 & 255,
m = 65535 & z,
!(b > p)],
b = z >>> 24,
g = z >>> 16 & 255,
m = 65535 & z,
!(p + b >>= p,
d -= p,
a.back += p
}
if (h >>>= b,
d -= b,
a.back += b,
a.length = m,
0 === g) {
a.mode = 26;
break
}
if (32 & g) {
a.back = -1,
a.mode = Ee;
break
}
if (64 & g) {
t.msg = "invalid literal/length code",
a.mode = Re;
break
}
a.extra = 15 & g,
a.mode = 22;
case 22:
if (a.extra) {
for (R = a.extra; d >>= a.extra,
d -= a.extra,
a.back += a.extra
}
a.was = a.length,
a.mode = 23;
case 23:
for (; z = a.distcode[h & (1 >> 24,
g = z >>> 16 & 255,
m = 65535 & z,
!(b > p)],
b = z >>> 24,
g = z >>> 16 & 255,
m = 65535 & z,
!(p + b >>= p,
d -= p,
a.back += p
}
if (h >>>= b,
d -= b,
a.back += b,
64 & g) {
t.msg = "invalid distance code",
a.mode = Re;
break
}
a.offset = m,
a.extra = 15 & g,
a.mode = 24;
case 24:
if (a.extra) {
for (R = a.extra; d >>= a.extra,
d -= a.extra,
a.back += a.extra
}
if (a.offset > a.dmax) {
t.msg = "invalid distance too far back",
a.mode = Re;
break
}
a.mode = 25;
case 25:
if (0 === o)
break t;
if (c = f - o,
a.offset > c) {
if (c = a.offset - c,
c > a.whave && a.sane) {
t.msg = "invalid distance too far back",
a.mode = Re;
break
}
c > a.wnext ? (c -= a.wnext,
u = a.wsize - c) : u = a.wnext - c,
c > a.length && (c = a.length),
w = a.window
} else
w = n,
u = s - a.offset,
c = a.length;
c > o && (c = o),
o -= c,
a.length -= c;
do {
n[s++] = w[u++]
} while (--c);
0 === a.length && (a.mode = 21);
break;
case 26:
if (0 === o)
break t;
n[s++] = a.length,
o--,
a.mode = 21;
break;
case 27:
if (a.wrap) {
for (; d {
if (!t || !t.state)
return ve;
let e = t.state;
return e.window && (e.window = null),
t.state = null,
me
}
,
inflateGetHeader: (t,e)=>{
if (!t || !t.state)
return ve;
const a = t.state;
return 0 == (2 & a.wrap) ? ve : (a.head = e,
e.done = !1,
me)
}
,
inflateSetDictionary: (t,e)=>{
const a = e.length;
let i, n, r;
return t && t.state ? (i = t.state,
0 !== i.wrap && 11 !== i.mode ? ve : 11 === i.mode && (n = 1,
n = M(n, e, a, 0),
n !== i.check) ? ye : (r = Ie(t, e, a, a),
r ? (i.mode = 31,
xe) : (i.havedict = 1,
me))) : ve
}
,
inflateInfo: "pako inflate (from Nodeca project)"
};
var He = function() {
this.text = 0,
this.time = 0,
this.xflags = 0,
this.os = 0,
this.extra = null,
this.extra_len = 0,
this.name = "",
this.comment = "",
this.hcrc = 0,
this.done = !1
};
const Me = Object.prototype.toString
, {Z_NO_FLUSH: Ke, Z_FINISH: je, Z_OK: Pe, Z_STREAM_END: Ye, Z_NEED_DICT: Ge, Z_STREAM_ERROR: Xe, Z_DATA_ERROR: We, Z_MEM_ERROR: qe} = Y;
function Je(t) {
this.options = Mt({
chunkSize: 65536,
windowBits: 15,
to: ""
}, t || {});
const e = this.options;
e.raw && e.windowBits >= 0 && e.windowBits = 0 && e.windowBits 15 && e.windowBits 0 && r === Ye && a.state.wrap > 0 && 0 !== t[a.next_in]; )
Ce.inflateReset(a),
r = Ce.inflate(a, s);
switch (r) {
case Xe:
case We:
case Ge:
case qe:
return this.onEnd(r),
this.ended = !0,
!1
}
if (l = a.avail_out,
a.next_out && (0 === a.avail_out || r === Ye))
if ("string" === this.options.to) {
let t = Xt(a.output, a.next_out)
, e = a.next_out - t
, n = Gt(a.output, t);
a.next_out = e,
a.avail_out = i - e,
e && a.output.set(a.output.subarray(t, t + e), 0),
this.onData(n)
} else
this.onData(a.output.length === a.next_out ? a.output : a.output.subarray(0, a.next_out));
if (r !== Pe || 0 !== l) {
if (r === Ye)
return r = Ce.inflateEnd(this.strm),
this.onEnd(r),
this.ended = !0,
!0;
if (0 === a.avail_in)
break
}
}
return !0
}
,
Je.prototype.onData = function(t) {
this.chunks.push(t)
}
,
Je.prototype.onEnd = function(t) {
t === Pe && ("string" === this.options.to ? this.result = this.chunks.join("") : this.result = Kt(this.chunks)),
this.chunks = [],
this.err = t,
this.msg = this.strm.msg
}
;
var Ve = {
Inflate: Je,
inflate: Qe,
inflateRaw: function(t, e) {
return (e = e || {}).raw = !0,
Qe(t, e)
},
ungzip: Qe,
constants: Y
};
里面写了几种算法,扣下来可以学习,备着以后用。看大站,可以学习很多。
因为使用python,我这里做了点处理,先把byte转base64然后再处理
所以js这样处理
python:
现在数据都处理好了,就剩r.Response这个proto文件了,就能解析了。
跟进去,发现是一个地方。我们刚刚不是处理过吗,找一下文件里面的,拿出来:
message webcast_im_Response{
repeated webcast_im_Message messages=1;
string cursor=2;
int64 fetchinterval=3;
int64 now=4;
string internalext=5;
int32 fetchtype=6;
int64 heartbeatduration=8;
bool needack=9;
string pushserver=10;
string livecursor=11;
bool historynomore=12;
}
message webcast_im_Message{
string method=1;
bytes payload=2;
int64 msgid=3;
int32 msgtype=4;
int64 offset=5;
bool needwrdsstore=6;
int64 wrdsversion=7;
string wrdssubkey=8;
}
编译一下,看效果:
网站上:
python:
一致,本来第一项列表是有数据,可以看到里面消息类型的,刚刚不小心跳过断点了,然后直播结束了,没消息了....尴尬。但是暂时不影响分析,我们把ack也完成就换个直播间。
③ack处理:
因为这里needack是true,所以需要发一个包:
可以看到,我们构造没问题。
代码:
#ack处理
internalext=webcast_im_Response.internalext
payload_base64=ctx.call('get_ackpayload',internalext)
payload_byte=base64.b64decode(payload_base64)
ack_pushproto_PushFrame=new_pb2.pushproto_PushFrame()
ack_pushproto_PushFrame.payloadtype='ack'
ack_pushproto_PushFrame.payload=payload_byte
ack_pushproto_PushFrame.logid=pushproto_PushFrame.logid
ack_payload=ack_pushproto_PushFrame.SerializeToString()
print(base64.b64encode(ack_payload).decode())
我们到现在还是没看到弹幕数据,因为他在下面emit函数处处理了,我们先别急,我换一个直播间再继续分析。
④最后数据处理emit函数:
猜测应该是payload里面是弹幕信息,我们进到emit函数里面看看吧。
emit(e) {
const t = e.getMessagesList(); //得到刚刚的message列表
//forEach() 方法对数组的每个元素执行一次给定的函数。
//这里相当于遍历每一个message,然后做处理。
t.length && t.forEach((e=>{
const t = e.getMethod()
, a = "RoomMessage" === t ? t : e.getMsgId();
this.messageIdsForDistinct.has(a) || (this.messageIdsForDistinct.add(a),
//
this.runAllEvents(t, e))
}
))
}
我们下个断点,断下时,看看值,这里应该是message列表第一个:
我们可以看到,没错,是第一个WebcastMemberMessage,然后把这message列表第一个对象扔进了这个runAllEvents函数处理:
进入runAllEvents函数,我们又看到了熟悉的关键词:
大概猜测这里就能得到我们想要的数据了,下断点,运行到这看s值。
果然显示了用户名还有一些信息。
代码分析:
runAllEvents(e, t) {
var a;
//this.eventsMap.entries()里面包含所有事件模块
for (const [o,i] of this.eventsMap.entries()) {
const a = this.messageModules[o]; //创建模块
//if判断传进来的名字是否匹配模块
if (i && a && this.isCorrectEventName(o, e)) {
//匹配就执行相应代码
const r = t.getPayload_asU8()
, s = a.deserializeBinary(r); //可以在这里下断点找到a对象对应的反序列化对象,模块
return this.info(`emit Message Type: ${e} ${o}`),
this.info("emit Message Payload:", (()=>s.toObject())),
void i.forEach((e=>{
e(s, t, r)
}
))
}
}
const r = null !== (a = this.messageNotUseCache.get(e)) && void 0 !== a ? a : [];
r.length > o.ej && r.shift(),
r.push(t),
this.messageNotUseCache.set(e, r)
}
至此,基本上分析完了全部逻辑,一共解析了3次payload,他是遍历第二次得到的payload数据中的message列表,根据不同message类型,找到不同类型对应的proto文件对象,然后再解析。
五、用工具,得到基本proto文件,遇到proto文件报错解决方法
跟前面得到的proto文件类似,进入到解析对象的js里面,然后复制js到工具处理。
因为很多,而且webpack打包很分散,我讲一下我的处理方法吧:
相似报错可以直接同一值替换(可能不太严谨,但是跟了很多都是同一个),我们可以把这个直接全部复制到一个大proto文件中,比如说我们最开始那个,折叠起来,然后慢慢把报错解决。
我演示Common的处理,其他类似就好:
跟进去,直接定位Common,因为p对象,所以代码无法体现是谁,所以处理会报错。
跟进去后直接复制js到工具(记得创建自己要处理的文件js),直接处理。
将补充的补充到下面,标注一下,说不定其他模块也要用,反正我们都复制下来。
我这里采用nodepad++全体Common替换成 webcast_im_Common
这里就解决了
然后后面的也接着一样操作来,直到补全完整的proto文件,基本上就出来了,很多要有耐心,可能一开始错会很多,但是补到后面,你补其他message类型的时候就很轻松了。
把大的Image,User,common,Text补完,基本上就剩一点点了。
Text =====> webcast_data_Text
Image =====> webcast_data_Image
User =====> webcast_data_User
Common =====> webcast_im_Common
Room =====> webcast_data_Room
补到后面会发现3个map类型的数据,因为工具没处理(大佬可以帮忙添加一下处理map类型的方法),我们注释掉里面的内容就好
20分钟左右补完了,经历了3个webpack,都是上w行代码的js。
很清楚的就得到刚刚那个WebcastMemberMessage类型数据的结构了,其他message类型(应该有6种)也是一样步骤,慢慢补充,就可以得到完整的proto文件,就能使用了。
最后这个不常见,刚刚好关播给我抓到了。
六、python连接websocket一些注意点
上面我们已经分析了数据处理方面的东西,现在我们来用python连接websocket地址,然后实现open,onmessage事件等功能,然后获取我们想要的数据,我这里讲一些注意点吧。
①连接需要cookie
在fd搜索:
发现原始请求头我们要什么数据:
需要一个cookie,搜索ttwid就能看到cookie是怎么生成的,发现是刚开网页服务器发过来的。
②网址signature分析
从最开始,我们知道,网址是这里的t,然后进入u方法。
这里就是我们要的signature,可以看出是从p来的。
const s = {
"X-MS-STUB": g()(n.substring(1))
};
let p = {};
return
//这一串是问号表达式(三元表达式),":"前面是false就走":"后面的,否则走前面的
window.byted_acrawler && (p = null ===
(o = null === window || void 0 === window ?
void 0 : window.byted_acrawler
)
|| void 0 === o
? void 0 : o.frontierSign(s)),
//会进这个o.frontierSign(s)方法里面,大家可以自己调试
{
signature: null !== (a = p["X-Bogus"]) && void 0 !== a ? a : ""
}
里面全扣下来,然后补环境可以解决,有轻微混淆吧。
只是可能js代码和node版本问题,警告了,不过没什么事。
其实这个参数目前还不会校验,删掉不携带也可以获得响应,不排除以后校验的可能。
七、效果展示
因为有些数据可能敏感,就放这张效果图了。
八、代码及总结
代码地址:https://github.com/Prince-cool/dy_protobuf
希望大家能给个star支持一下,哈哈哈哈哈,写的真的好辛苦啊!!!!
总结:这几天过节,也到处去玩,所以都是抽空慢慢补齐内容的,可能会有点跳跃,请见谅,因为如果要讲太细,可能篇幅太长了,文字也说不清楚,一共60多张图片。如果有机会,想要我录视频的,可以留言,我找机会录制一下,或者开个腾讯会议一起交流。
希望这篇文章对你有益,感谢,也祝大家新年快乐,心想事成!