prince_cool 发表于 2023-1-20 23:12

某音直播弹幕web端js逆向分析----protobuf实战及工具介绍

本帖最后由 prince_cool 于 2024-1-13 11:22 编辑

# 某音直播弹幕web端js逆向分析----protobuf实战及工具介绍

# 声明

**       本文章中所有内容仅供学习交流,相关链接做了脱敏处理,若有侵权,请联系我立即删除!**

# 一、前言

       前面的文章应该可以让大家掌握好protobuf吧,只要抓住关键词就行了。前面文章我也提到了某音直播弹幕也是采用protobuf格式传输的,那我们这篇文章就来看看,实战一下能不能获取到信息。同时我也会放出我写好的ast工具,对基本的protobuf格式应该都能做到处理,大佬们也可以在我这个基础上修改,或者推出更适合的工具。

# 二、前情提要

一般的直播弹幕都采用websocket协议传输数据,因为要实时传输,ws协议只用一次建立握手连接,就可持续实时通信。

![](https://s2.loli.net/2023/05/18/JbmZohUzeQV35IT.png#id=icSxp&originHeight=390&originWidth=898&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

基于这点,我们可以很容易的就找到我们要的地址,我们开始分析吧!

# 三、目标网站

任意直播间

# 四、开始分析

如果你不信我的前情提要,觉得太虚了,有没有可能走http协议呢,那我们可以打开f12,打开fiddler抓包工具(为后面分析ws连接做准备),然后输入我们的目标网站地址(只需https:// live.***.com/数字),抓包看看吧。

获取到弹幕的一瞬间,我们的fiddler就可以关了。我们看看浏览器的f12内容,进度条一直在跑,因为数据很多嘛.

![](https://s2.loli.net/2023/05/18/ANpmKHTg8desiS6.png#id=SU9Tu&originHeight=121&originWidth=1196&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

弹幕数据肯定是那些零星的,蓝色分散的点,我们括一小段看看

![](https://s2.loli.net/2023/05/18/fzlEugI6jHRLvUY.png#id=zulxQ&originHeight=668&originWidth=1206&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们看到数据类型大多是图片,不是我们需要的啊,唯独websocket类型数据是一直贯穿的,即使暂停了直播,弹幕还是会有,webscoket传输的数据还是没停下来。所以我们要找的就是websocket协议传输的数据。

![](https://s2.loli.net/2023/05/18/QMU2szYHJfmgNwK.png#id=QdSsL&originHeight=736&originWidth=1205&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

点到ws,然后看到唯一链接wss开头,这就是websocket协议的。

知识点:(**绿色表示客户端消息发给服务器,红色表示服务器下发数据给客户端**)

我们可以从堆栈入手,找到发包点。

![](https://s2.loli.net/2023/05/18/3joa8UXbHDyz7FP.png#id=BqsyQ&originHeight=844&originWidth=463&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

其实我们从名字也大概可以猜到可能是这两个,我一开始是直接看这两个的也能找到点,我们还是从最后一个发包栈(m)看起吧。

![](https://s2.loli.net/2023/05/18/TMyBUJ9P386fuYS.png#id=fDAMt&originHeight=384&originWidth=916&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们去搜索一下,学习一下这个是怎么一回事,t参数是什么意思。

来到mdn网站,强烈推荐:(https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket)

                WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
      
                                        使用 WebSocket() 构造函数来构造一个 WebSocket。
      
                                        WebSocket(url ,protocols(可选))
      
                                        返回一个 `WebSocket` 对象。

所以说这个t是一个url链接,应该就是我们看到的那个wss链接了,我们下个断点,来分析分析。

![](https://s2.loli.net/2023/05/18/vxl8zwaeQTBZKFc.png#id=tLFKG&originHeight=450&originWidth=1652&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

确实是。我们回到原来那个mdn网站,可以往下看,可以看到事件这个栏目。

![](https://s2.loli.net/2023/05/18/7nVjguLBNJqcU2O.png#id=msZBH&originHeight=627&originWidth=1019&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们断点下方不正是有这几个事件关键词吗,我们如果刷新界面,它刚开始连接时,如果成功,应该会触发open事件,我们都可以下个断点看看,主要先看看open,**这几个关键词要记住,解websocket协议的核心**。

我们可以看到它先走了onmessage,我们在mdn查一下 addEventListener:addEventListener(type,listener)其中 listener是对象,相当于监听它。我们跟过去看看这个对象,函数。

![](https://s2.loli.net/2023/05/18/nV1AmySExJuwocj.png#id=epMsn&originHeight=522&originWidth=933&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

相当于每个都是监听里面这一堆东西,各种判断处理,先给创建的websocket对象绑定好了各种方法,再来处理数据。

所以他绑定的方法我们要跟过去下断点,都绑定后后,我们发现,他来到了onopen,就是我们最开始提到的,连接成功要处理的事件。

![](https://s2.loli.net/2023/05/18/H7iZT4meX1vwEYy.png#id=a2toD&originHeight=187&originWidth=931&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

核心是这个 this.ping(),其他都是一些赋值操作,我们跟进ping函数

![](https://s2.loli.net/2023/05/18/gOBTj9JMi1UsEyR.png#id=llWr6&originHeight=247&originWidth=892&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

是不是看到了我们protobuf格式的老熟人关键词:**serializeBinary**,然后通过socket的send发给服务器。

代码分析:

```javascript
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一次嘛
```

![](https://s2.loli.net/2023/05/18/hvLbVnlwN5j7uoR.png#id=Fyj2S&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

可能是太久了,他发了两次,对吧,所以说这里就是一个点,我们要构造ping值,然后连接成功时候发一下,然后就能得到服务器返回的值了。

![](https://s2.loli.net/2023/05/18/WLfMmFSjq695BDU.png#id=oLM2I&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们回到这里,先搞定这里再分析onmessage的,构造这个proto文件吧。

![](https://s2.loli.net/2023/05/18/z7RbilvgAFkrdC9.png#id=EWZ9R&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

搜索 **proto.pushproto.PushFrame.deserializeBinary**

![](https://s2.loli.net/2023/05/18/rfoYEcVDk9R4GQK.png#id=JXOUS&originHeight=674&originWidth=937&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

当然可以一个个慢慢扣,这里比较少。

### ①js转proto文件工具分享(只可解标准protobuf):

这里我采用我自己写的ast工具进行处理,把js尽可能转成proto文件,这里是我想分享的一点。

因为之前不是用渔歌的ast脚本加个try,逻辑改一下就搞定某方了,我就想,我能不能自己写一个呢,然后小学了一下ast,用的最基础的语法,我直接放出来吧,供大家使用,改良;你们写的肯定比我好。

```javascript
//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;
}
if (process.argv.length > 3) {
    decode_file = process.argv;
}
//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 =
    {
      //TODOwrite 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 < path2.node.cases.length - 1; i++) {
                                        location = cases_list.test.value
                                        if (cases_list.consequent.length) {
                                          if (cases_list.consequent.type == 'VariableDeclaration') {
                                                //遇到new对象处理
                                                if (cases_list.consequent.declarations.init.type == 'NewExpression') {
                                                    //获得type
                                                    node_code = cases_list.consequent.declarations.init
                                                    // console.log(generator(node_code).code)
                                                    node_code_list = generator(node_code).code.split('.')
                                                    node_code_list.shift()
                                                    id_type = node_code_list.join('_').replace('()', '')
                                                    // console.log(id_type);
                                                    //获得name,这部分有点问题,大家可以自行修改
                                                    try {
                                                      id_name = cases_list.consequent.expression.callee.property.name

                                                    } catch (e) {
                                                      id_name = cases_list.consequent.expression.expressions.callee.property.name
                                                    }

                                                    // console.log(id_name);
                                                    if (id_name.startsWith("set")) {
                                                      id_st = "";
                                                    } else if (id_name.startsWith("add")) {
                                                      id_st = "repeated";
                                                    } else {
                                                      // map类型暂时没处理,可自行补充完善
                                                      continue
                                                    }
                                                    id_name = id_name.substring(3, id_name.length).toLowerCase();
                                                    // console.log(id_name)
                                                    text = text + id_st + ' ' + id_type + ' ' + id_name + '=' + location + ';' + '\n'


                                                }
                                                //其他情况处理
                                                else {
                                                    if (cases_list.consequent.declarations.init.callee)
                                                      id_type = cases_list.consequent.declarations.init.callee.property.name
                                                    if (cases_list.consequent.declarations.init.consequent || cases_list.consequent.declarations.init.alternate)
                                                      id_type = cases_list.consequent.declarations.init.consequent.callee.property.name
                                                    id_type = get_id_type(id_type)
                                                    // console.log(id_type);
                                                    if (cases_list.consequent.type == 'ForStatement')
                                                      id_name = cases_list.consequent.body.body.expression.callee.property.name
                                                    else if (cases_list.consequent.type == 'ExpressionStatement') {
                                                      if (cases_list.consequent.expression.expressions)
                                                            id_name = cases_list.consequent.expression.expressions.consequent.expression.expressions.length - 1].callee.property.name
                                                      else
                                                            id_name = cases_list.consequent.expression.callee.property.name
                                                    }

                                                    if (id_name.startsWith("set")) {
                                                      id_st = "";
                                                    } else if (id_name.startsWith("add")) {
                                                      id_st = "repeated";
                                                    } else {
                                                      // map类型暂时没处理,可自行补充完善
                                                      continue
                                                    }
                                                    id_name = id_name.substring(3, id_name.length);
                                                    // console.log(id_name)
                                                    if (id_type == 'readEnum') {
                                                      text = text + 'enum ' + titleCase(id_name) + ' {\n' + id_name + '_0=0;\n}\n' + id_st + ' ' + titleCase(id_name) + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'
                                                    } else if (id_type == 'readPackedEnum') {
                                                      id_st = 'repeated'
                                                      text = text + 'enum ' + titleCase(id_name) + ' {\n' + id_name + '_0=0;\n}\n' + id_st + ' ' + titleCase(id_name) + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'
                                                    } else
                                                      text = text + id_st + ' ' + id_type + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'

                                                }
                                          }
                                          if (cases_list.consequent.type == 'ExpressionStatement') {
                                                //一般处理
                                                if (cases_list.consequent.expression.right.type == 'CallExpression') {
                                                    id_type = cases_list.consequent.expression.right.callee.property.name
                                                    // console.log(id_type)
                                                    id_type = get_id_type(id_type)
                                                    // console.log(id_type);
                                                    if (cases_list.consequent.type == 'ForStatement')
                                                      id_name = cases_list.consequent.body.body.expression.callee.property.name
                                                    else if (cases_list.consequent.type == 'ExpressionStatement') {
                                                      if (cases_list.consequent.expression.expressions)
                                                            id_name = cases_list.consequent.expression.expressions.consequent.expression.expressions.length - 1].callee.property.name
                                                      else
                                                            id_name = cases_list.consequent.expression.callee.property.name
                                                    }
                                                    // console.log(id_name);
                                                    if (id_name.startsWith("set")) {
                                                      id_st = "";
                                                    } else if (id_name.startsWith("add")) {
                                                      id_st = "repeated";
                                                    } else {
                                                      // map类型暂时没处理,可自行补充完善
                                                      continue
                                                    }
                                                    id_name = id_name.substring(3, id_name.length).toLowerCase();
                                                    // console.log(id_name)
                                                    if (id_type == 'readEnum') {
                                                      text = text + 'enum ' + titleCase(id_name) + ' {\n' + id_name + '_0=0;\n}\n' + id_st + ' ' + titleCase(id_name) + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'
                                                    } else if (id_type == 'readPackedEnum') {
                                                      id_st = 'repeated'
                                                      text = text + 'enum ' + titleCase(id_name) + ' {\n' + id_name + '_0=0;\n}\n' + id_st + ' ' + titleCase(id_name) + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'
                                                    } else
                                                      text = text + id_st + ' ' + id_type + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'

                                                }
                                                //遇到new对象处理
                                                if (cases_list.consequent.expression.right.type == 'NewExpression') {
                                                    //转化成字符串对象处理
                                                    node_code = cases_list.consequent.expression.right.callee
                                                    node_code_list = generator(node_code).code.split('.')
                                                    node_code_list.shift()
                                                    id_type = node_code_list.join('_')
                                                    // console.log(id_type);

                                                    //获得name
                                                    id_name = cases_list.consequent.expression.expressions.callee.property.name
                                                    // console.log(id_name);
                                                    if (id_name.startsWith("set")) {
                                                      id_st = "";
                                                    } else if (id_name.startsWith("add")) {
                                                      id_st = "repeated";
                                                    } else {
                                                      // map类型暂时没处理,可自行补充完善
                                                      continue
                                                    }
                                                    id_name = id_name.substring(3, id_name.length).toLowerCase();
                                                    // console.log(id_name)
                                                    if (id_type == 'readEnum') {
                                                      text = text + 'enum ' + titleCase(id_name) + ' {\n' + id_name + '_0=0;\n}\n' + id_st + ' ' + titleCase(id_name) + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'
                                                    } else if (id_type == 'readPackedEnum') {
                                                      id_st = 'repeated'
                                                      text = text + 'enum ' + titleCase(id_name) + ' {\n' + id_name + '_0=0;\n}\n' + id_st + ' ' + titleCase(id_name) + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'
                                                    } else
                                                      text = text + id_st + ' ' + id_type + ' ' + id_name.toLowerCase() + '=' + location + ';' + '\n'


                                                }


                                          }

                                        }


                                    }

                              },
                              ////某音有的结构,遇到其他网站可以注释
                              IfStatement(path3) {
                                    location = path3.node.test.left.value
                                    if (path3.node.consequent.type === 'BlockStatement') {
                                        if (path3.node.consequent.body.type === 'VariableDeclaration') {
                                          // console.log(generator(path3.node).code)
                                          //遇到new对象处理
                                          if (path3.node.consequent.body.declarations.init.type == 'NewExpression') {
                                                //获得type
                                                node_code = path3.node.consequent.body.declarations.init
                                                // console.log(generator(node_code).code)
                                                node_code_list = generator(node_code).code.split('.')
                                                node_code_list.shift()
                                                id_type = node_code_list.join('_').replace('()', '')
                                                // console.log(id_type);
                                                //获得name,这部分有点问题,大家可以自行修改
                                                try {
                                                    id_name = path3.node.consequent.body.expression.callee.property.name

                                                } catch (e) {
                                                    id_name = path3.node.consequent.body.expression.expressions.callee.property.name
                                                }

                                                // console.log(id_name);
                                                if (id_name.startsWith("set")) {
                                                    id_st = "";
                                                } else if (id_name.startsWith("add")) {
                                                    id_st = "repeated";
                                                } else {
                                                    // map类型暂时没处理,可自行补充完善
                                                }
                                                id_name = id_name.substring(3, id_name.length).toLowerCase();
                                                // console.log(id_name)
                                                text = text + id_st + ' ' + id_type + ' ' + id_name + '=' + location + ';' + '\n'


                                          } else {
                                                id_type = path3.node.consequent.body.declarations.init.callee.property.name
                                                id_type = get_id_type(id_type)
                                                id_name = path3.node.consequent.body.expression.callee.property.name
                                                if (id_name.startsWith("set")) {
                                                    id_st = "";
                                                } else if (id_name.startsWith("add")) {
                                                    id_st = "repeated";
                                                } else {
                                                    // map类型暂时没处理,可自行补充完善
                                                }
                                                id_name = id_name.substring(3, id_name.length).toLowerCase();
                                                // console.log(id_name)
                                                text = text + id_st + ' ' + id_type + ' ' + id_name + '=' + location + ';' + '\n'
                                          }
                                        }
                                    }
                                    if (path3.node.consequent.type === 'ForStatement') {
                                        id_type = path3.node.consequent.init.declarations.init.consequent.callee.property.name
                                        id_type = get_id_type(id_type)
                                        id_name = path3.node.consequent.body.expression.callee.property.name
                                        // console.log(id_name);
                                        if (id_name.startsWith("set")) {
                                          id_st = "";
                                        } else if (id_name.startsWith("add")) {
                                          id_st = "repeated";
                                        } else {
                                          // map类型暂时没处理,可自行补充完善
                                        }
                                        id_name = id_name.substring(3, id_name.length).toLowerCase();
                                        // console.log(id_name)
                                        text = text + id_st + ' ' + id_type + ' ' + id_name + '=' + location + ';' + '\n'

                                    }


                              }
                            }
                        )

                  }
                  text = text + '}\n'
                }

            }


      }
    }


//处理enum对象插件
const visitor2 = {
    AssignmentExpression(path) {
      if (path.node.right.type === 'ObjectExpression' && path.node.left.type === 'MemberExpression' && path.node.right.properties.length) {
            left_node = path.node.left
            left_code = generator(left_node).code.split('.')
            word = left_code.shift()
            if (word === 'proto') {
                enum_name = left_code.join('_')
                // console.log(enum_name)
                right_object_list = path.node.right.properties
                right_str = ''
                for (i = 0; i < right_object_list.length; i++) {
                  // console.log(right_object_list.key.name)
                  objet_name = right_object_list.key.name
                  objet_value = right_object_list.value.value
                  right_str = right_str + enum_name + '_' + objet_name + '=' + objet_value + ';\n'
                }
                text = text + 'enum ' + enum_name + '{\n' + right_str + '}' + '\n'
                // console.log(right_code)

            }


      }
      // console.log(generator(path.node).code)
    }
}


//调用插件,处理源代码

// traverse(ast, visitor2)
// console.log('enum部分完成!!!')
// fs.writeFile('douyin_test_enum完成.proto', text, (err) => {});

traverse(ast, visitor);
console.log('message部分完成!!!')
// // fs.writeFile('douyin_test_message完成.proto', text, (err) => {});
//
//
// // console.log(text);
//
fs.writeFile(decode_file, text, (err) => {});
```

首先,为什么能写出这个插件,我基于的是之前文章讲过的:(**不要编辑它**)

![](https://s2.loli.net/2023/05/18/Bg3YoOZCEsaVSNT.png#id=JE2YJ&originHeight=700&originWidth=1773&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

所以我就看了一下原始创建的js和某方和某音的结构,发现有相同点:

![](https://s2.loli.net/2023/05/18/8XpVZIhbSNDkCy2.png#id=UFY9N&originHeight=631&originWidth=708&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

```javascript
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/)

![](https://s2.loli.net/2023/05/18/Ox2eqdHjPwKYt9y.png#id=Sms1r&originHeight=635&originWidth=866&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

发现都是 AssignmentExpression类型对象,然后有左:MemberExpression类型节点,它调用的方法名是**deserializeBinaryFromReader**

**定位**:我就是先以这个确定message对象:

![](https://s2.loli.net/2023/05/18/IyuCf6Tshpt95z2.png#id=oDYBe&originHeight=447&originWidth=911&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

**处理**:然后再处理细节,展开后:

![](https://s2.loli.net/2023/05/18/xDsFmAyPXk8Uip7.png#id=NPFQJ&originHeight=335&originWidth=817&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

先把message名字搞定:

用babel库的generator将左节点转成字符串源码,然后以"."分割去掉头(proto)尾(deserializeBinaryFromReader)

然后用"_"拼接,就搞定了message对象名字。我拿text变量来连接这些数据,下面就是处理右边的function了。

![](https://s2.loli.net/2023/05/18/MntZgOKHauyv7DE.png#id=YV4BY&originHeight=307&originWidth=845&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

直接通过babel库的traverse定位到switch语句块,里面又有好几种情况,大家可以自行分析,我们可以通过慢慢调试,发现缺少的就慢慢补充起来,我基本上把可能的情况都写上去了,可是有个地方需要大家帮忙修改或者采用更好的方法优化一下吧。



如果报错可以在pycharm,debugger下看到报错细节,然后再进行调整,我发的基本上解决了。

下面是一些特殊情况,因为没保存之前的截图,临时找了一下:

![](https://s2.loli.net/2023/05/18/Sx9HlzKimLGCfFN.png#id=YXFRW&originHeight=629&originWidth=920&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

其实下面也写了处理enum类型的模块,因为本身转化成js后是不可逆的,只能尽量还原,不可逆的点就是这个enum类型对象,你可以看回转化后的js,是没有一一对应关系的,所以无法关联到枚举类型变量,所以我就关了,没使用,想看枚举类型也能打开来看。

![](https://s2.loli.net/2023/05/18/AJn6aCS9FTrsQYK.png#id=yYy9s&originHeight=538&originWidth=919&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

对了,上面有封装了一个函数,是处理数据类型的,如果你遇到了我这工具没有的类型,你自己可以添加到case语句里面,一看就懂。

![](https://s2.loli.net/2023/05/18/GnldhkJifyYbTIj.png#id=sl9eD&originHeight=412&originWidth=560&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

代码大概介绍了一下,我们就拿上面拿出来的某方,某音的列子来试试效果。

![](https://s2.loli.net/2023/05/18/OEJd4NGqm2SrfRI.png#id=oUh4z&originHeight=396&originWidth=738&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

一下子就处理完成,效果蛮好的,红色报错之前文章也说过原因,也教过怎么找这个对象,可以看回之前的文章。

### ②回到某音ping分析的地方:

![](https://s2.loli.net/2023/05/18/nQ2Iho1vcMrkH7Z.png#id=I0wK6&originHeight=491&originWidth=968&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们跟进了那个方法的js文件,我们直接复制下来,丢到工具里面:

![](https://s2.loli.net/2023/05/18/Om5JwZhQDioCI6X.png#id=YE3U8&originHeight=688&originWidth=1415&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

发现我们要的其实就是我圈起来的这两个,我们可以单独拿出来。

```protobuf
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语句看回我补充篇)

![](https://s2.loli.net/2023/05/18/JTb239M4LEKroed.png#id=lf9f1&originHeight=663&originWidth=1144&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

可以看到我们发过去后,服务器给我们发送了一个数据,猜测应该会在onMessage收到,然后处理,放行,果然来了。

![](https://s2.loli.net/2023/05/18/JhkfFZtUVYHjiBS.png#id=FtNOs&originHeight=349&originWidth=1231&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们跟过去看看吧:

![](https://s2.loli.net/2023/05/18/TJefNt4FX1DVvSn.png#id=y0srd&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

又看到了我们的老朋友:反序列化的函数(deserializeBinary),所以我们可以采取构造proto文件处理,也可以使用python库blackboxprotobuf处理。因为我写了工具,就直接使用第一种方式,构造proto文件,也更直观。

我们先大概分析一下代码,再用工具去构造吧:

先看第一个语句:

```javascript
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 < 128 ? t.push(e) : e < 2048 ? (t.push(192 + (e >> 6)),
         t.push(128 + (63 & e))) : e < 65536 && (t.push(224 + (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函数处理展示值。

我们看看实际情况:

![](https://s2.loli.net/2023/05/18/i6gXWON5oB8uyQY.png#id=KcY3J&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

所以先通过n.ec函数处理,然后再来反序列化。

我们复制刚刚返回的,也能得到这个序列。

![](https://s2.loli.net/2023/05/18/TCzSxDjnkXgKqJs.png#id=R8c3r&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

![](https://s2.loli.net/2023/05/18/pztSkAN3X5ULwlx.png#id=RtWWW&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们进去n.ec函数,然后移到最上面,发现是webpack打包的,然后我们可以看到

![](https://s2.loli.net/2023/05/18/aqvMALj5JWKSgF1.png#id=so3fC&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

![](https://s2.loli.net/2023/05/18/AI2HPZb1ELp3vkB.png#id=clPzP&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

刚刚好Qe就是我们进来的位置,也就是这个函数,其实名字已经暴露了是解压,我们也可以把代码扣下来使用。

去掉开头结尾就好了:

```javascript
function i(t) {
    let e = t.length;
    for (; --e >= 0; )
      t = 0
}
const n = 256
, r = 286
, s = 30
, l = 15
, o = new Uint8Array()
, h = new Uint8Array()
, d = new Uint8Array()
, _ = new Uint8Array()
, 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 < 256 ? u : u
, z = (t,e)=>{
    t.pending_buf = 255 & e,
    t.pending_buf = e >>> 8 & 255
}
, A = (t,e,a)=>{
    t.bi_valid > 16 - a ? (t.bi_buf |= e << t.bi_valid & 65535,
    z(t, t.bi_buf),
    t.bi_buf = e >> 16 - t.bi_valid,
    t.bi_valid += a - 16) : (t.bi_buf |= e << t.bi_valid & 65535,
    t.bi_valid += a)
}
, E = (t,e,a)=>{
    A(t, a, a)
}
, R = (t,e)=>{
    let a = 0;
    do {
      a |= 1 & t,
      t >>>= 1,
      a <<= 1
    } while (--e > 0);
    return a >>> 1
}
, Z = (t,e,a)=>{
    const i = new Array(16);
    let n, r, s = 0;
    for (n = 1; n <= l; n++)
      i = s = s + a << 1;
    for (r = 0; r <= e; r++) {
      let e = t;
      0 !== e && (t = R(i++, e))
    }
}
, U = t=>{
    let e;
    for (e = 0; e < r; e++)
      t.dyn_ltree = 0;
    for (e = 0; e < s; e++)
      t.dyn_dtree = 0;
    for (e = 0; e < 19; e++)
      t.bl_tree = 0;
    t.dyn_ltree = 1,
    t.opt_len = t.static_len = 0,
    t.last_lit = t.matches = 0
}
, S = t=>{
    t.bi_valid > 8 ? z(t, t.bi_buf) : t.bi_valid > 0 && (t.pending_buf = 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 < t || t === t && i <= i
}
, O = (t,e,a)=>{
    const i = t.heap;
    let n = a << 1;
    for (; n <= t.heap_len && (n < t.heap_len && D(e, t.heap, t.heap, t.depth) && n++,
    !D(e, i, t.heap, t.depth)); )
      t.heap = t.heap,
      a = n,
      n <<= 1;
    t.heap = i
}
, T = (t,e,a)=>{
    let i, r, s, l, d = 0;
    if (0 !== t.last_lit)
      do {
            i = t.pending_buf << 8 | t.pending_buf,
            r = t.pending_buf,
            d++,
            0 === i ? E(t, r, e) : (s = w,
            E(t, s + n + 1, e),
            l = o,
            0 !== l && (r -= b,
            A(t, r, l)),
            i--,
            s = x(i),
            E(t, s, a),
            l = h,
            0 !== l && (i -= g,
            A(t, i, l)))
      } while (d < t.last_lit);
    E(t, 256, e)
}
, L = (t,e)=>{
    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 < r; s++)
      0 !== a ? (t.heap[++t.heap_len] = d = s,
      t.depth = 0) : a = 0;
    for (; t.heap_len < 2; )
      h = t.heap[++t.heap_len] = d < 2 ? ++d : 0,
      a = 1,
      t.depth = 0,
      t.opt_len--,
      n && (t.static_len -= i);
    for (e.max_code = d,
    s = t.heap_len >> 1; s >= 1; s--)
      O(t, a, s);
    h = r;
    do {
      s = t.heap,
      t.heap = t.heap,
      O(t, a, 1),
      o = t.heap,
      t.heap[--t.heap_max] = s,
      t.heap[--t.heap_max] = o,
      a = a + a,
      t.depth = (t.depth >= t.depth ? t.depth : t.depth) + 1,
      a = a = h,
      t.heap = h++,
      O(t, a, 1)
    } while (t.heap_len >= 2);
    t.heap[--t.heap_max] = t.heap,
    ((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 <= l; c++)
            t.bl_count = 0;
      for (a + 1] = 0,
      d = t.heap_max + 1; d < 573; d++)
            _ = t.heap,
            c = a + 1] + 1,
            c > h && (c = h,
            b++),
            a = c,
            _ > i || (t.bl_count++,
            u = 0,
            _ >= o && (u = s),
            w = a,
            t.opt_len += w * (c + u),
            r && (t.static_len += w * (n + u)));
      if (0 !== b) {
            do {
                for (c = h - 1; 0 === t.bl_count; )
                  c--;
                t.bl_count--,
                t.bl_count += 2,
                t.bl_count--,
                b -= 2
            } while (b > 0);
            for (c = h; 0 !== c; c--)
                for (_ = t.bl_count; 0 !== _; )
                  f = t.heap[--d],
                  f > i || (a !== c && (t.opt_len += (c - a) * a,
                  a = c),
                  _--)
      }
    }
    )(t, e),
    Z(a, d, t.bl_count)
}
, N = (t,e,a)=>{
    let i, n, r = -1, s = e, l = 0, o = 7, h = 4;
    for (0 === s && (o = 138,
    h = 3),
    e = 65535,
    i = 0; i <= a; i++)
      n = s,
      s = e,
      ++l < o && n === s || (l < h ? t.bl_tree += l : 0 !== n ? (n !== r && t.bl_tree++,
      t.bl_tree++) : l <= 10 ? t.bl_tree++ : t.bl_tree++,
      l = 0,
      r = n,
      0 === s ? (o = 138,
      h = 3) : n === s ? (o = 6,
      h = 3) : (o = 7,
      h = 4))
}
, F = (t,e,a)=>{
    let i, n, r = -1, s = e, l = 0, o = 7, h = 4;
    for (0 === s && (o = 138,
    h = 3),
    i = 0; i <= a; i++)
      if (n = s,
      s = e,
      !(++l < o && n === s)) {
            if (l < h)
                do {
                  E(t, n, t.bl_tree)
                } while (0 != --l);
            else
                0 !== n ? (n !== r && (E(t, n, t.bl_tree),
                l--),
                E(t, 16, t.bl_tree),
                A(t, l - 3, 2)) : l <= 10 ? (E(t, 17, t.bl_tree),
                A(t, l - 3, 3)) : (E(t, 18, t.bl_tree),
                A(t, l - 11, 7));
            l = 0,
            r = n,
            0 === s ? (o = 138,
            h = 3) : n === s ? (o = 6,
            h = 3) : (o = 7,
            h = 4)
      }
}
;
let B = !1;
const I = (t,e,a,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 <= 31; e++,
      a >>>= 1)
            if (1 & a && 0 !== t.dyn_ltree)
                return 0;
      if (0 !== t.dyn_ltree || 0 !== t.dyn_ltree || 0 !== t.dyn_ltree)
            return 1;
      for (e = 32; e < n; e++)
            if (0 !== t.dyn_ltree)
                return 1;
      return 0
    }
    )(t)),
    L(t, t.l_desc),
    L(t, t.d_desc),
    l = (t=>{
      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 + 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 <= r && (r = s)) : r = s = a + 5,
    a + 4 <= r && -1 !== e ? I(t, e, a, i) : 4 === t.strategy || s === r ? (A(t, 2 + (i ? 1 : 0), 3),
    T(t, f, c)) : (A(t, 4 + (i ? 1 : 0), 3),
    ((t,e,a,i)=>{
      let n;
      for (A(t, e - 257, 5),
      A(t, a - 1, 5),
      A(t, i - 4, 4),
      n = 0; n < i; n++)
            A(t, t.bl_tree + 1], 3);
      F(t, t.dyn_ltree, e - 1),
      F(t, t.dyn_dtree, a - 1)
    }
    )(t, t.l_desc.max_code + 1, t.d_desc.max_code + 1, l + 1),
    T(t, t.dyn_ltree, t.dyn_dtree)),
    U(t),
    i && S(t)
}
, H = {
    _tr_init: t=>{
      B || ((()=>{
            let t, e, a, i, n;
            const _ = new Array(16);
            for (a = 0,
            i = 0; i < 28; i++)
                for (b = a,
                t = 0; t < 1 << o; t++)
                  w = i;
            for (w = i,
            n = 0,
            i = 0; i < 16; i++)
                for (g = n,
                t = 0; t < 1 << h; t++)
                  u = i;
            for (n >>= 7; i < s; i++)
                for (g = n << 7,
                t = 0; t < 1 << h - 7; t++)
                  u = i;
            for (e = 0; e <= l; e++)
                _ = 0;
            for (t = 0; t <= 143; )
                f = 8,
                t++,
                _++;
            for (; t <= 255; )
                f = 9,
                t++,
                _++;
            for (; t <= 279; )
                f = 7,
                t++,
                _++;
            for (; t <= 287; )
                f = 8,
                t++,
                _++;
            for (Z(f, 287, _),
            t = 0; t < s; t++)
                c = 5,
                c = R(t, 5);
            p = new m(f,o,257,r,l),
            k = new m(c,h,0,s,l),
            v = new m(new Array(0),d,0,19,7)
      }
      )(),
      B = !0),
      t.l_desc = new y(t.dyn_ltree,p),
      t.d_desc = new y(t.dyn_dtree,k),
      t.bl_desc = new y(t.bl_tree,v),
      t.bi_buf = 0,
      t.bi_valid = 0,
      U(t)
    }
    ,
    _tr_stored_block: I,
    _tr_flush_block: C,
    _tr_tally: (t,e,a)=>(t.pending_buf = e >>> 8 & 255,
    t.pending_buf = 255 & e,
    t.pending_buf = 255 & a,
    t.last_lit++,
    0 === e ? t.dyn_ltree++ : (t.matches++,
    e--,
    t.dyn_ltree + n + 1)]++,
    t.dyn_dtree++),
    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 = 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 | 0,
            r = r + n | 0
      } while (--s);
      n %= 65521,
      r %= 65521
    }
    return n | r << 16 | 0
}
;
const K = new Uint32Array((()=>{
    let t, e = [];
    for (var a = 0; a < 256; a++) {
      t = a;
      for (var i = 0; i < 8; i++)
            t = 1 & t ? 3988292384 ^ t >>> 1 : t >>> 1;
      e = t
    }
    return e
}
)());
var j = (t,e,a,i)=>{
    const n = K
      , r = i + a;
    t ^= -1;
    for (let s = i; s < r; s++)
      t = t >>> 8 ^ n)];
    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)
, vt = t=>(t << 1) - (t > 4 ? 9 : 0)
, yt = t=>{
    let e = t.length;
    for (; --e >= 0; )
      t = 0
}
;
let xt = (t,e,a)=>(e << t.hash_shift ^ a) & t.hash_mask;
const zt = t=>{
    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 = e
}
, Rt = (t,e)=>{
    t.pending_buf = e >>> 8 & 255,
    t.pending_buf = 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
      , u = h;
    t.prev_length >= t.good_match && (n >>= 2),
    l > t.lookahead && (l = t.lookahead);
    do {
      if (a = e,
      h === u && h === c && h === h && h[++a] === h) {
            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 < f);
            if (i = wt - (f - r),
            r = f - wt,
            i > s) {
                if (t.match_start = e,
                s = i,
                i >= l)
                  break;
                c = h,
                u = h
            }
      }
    } while ((e = _) > o && 0 != --n);
    return s <= t.lookahead ? s : t.lookahead
}
, St = t=>{
    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 = n >= e ? n - e : 0
            } while (--i);
            i = e,
            a = i;
            do {
                n = t.prev[--a],
                t.prev = 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); t.insert && (t.ins_h = xt(t, t.ins_h, t.window),
            t.prev = t.head,
            t.head = s,
            s++,
            t.insert--,
            !(t.lookahead + t.insert < 3)); )
                ;
    } while (t.lookahead < bt && 0 !== t.strm.avail_in)
}
, Dt = (t,e)=>{
    let a, i;
    for (; ; ) {
      if (t.lookahead < bt) {
            if (St(t),
            t.lookahead < bt && e === Q)
                return 1;
            if (0 === t.lookahead)
                break
      }
      if (a = 0,
      t.lookahead >= 3 && (t.ins_h = xt(t, t.ins_h, t.window),
      a = t.prev = t.head,
      t.head = t.strstart),
      0 !== a && t.strstart - a <= t.w_size - bt && (t.match_length = Ut(t, a)),
      t.match_length >= 3)
            if (i = q(t, t.strstart - t.match_start, t.match_length - 3),
            t.lookahead -= t.match_length,
            t.match_length <= t.max_lazy_match && t.lookahead >= 3) {
                t.match_length--;
                do {
                  t.strstart++,
                  t.ins_h = xt(t, t.ins_h, t.window),
                  a = t.prev = t.head,
                  t.head = 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.ins_h = xt(t, t.ins_h, t.window);
      else
            i = q(t, 0, t.window),
            t.lookahead--,
            t.strstart++;
      if (i && (At(t, !1),
      0 === t.strm.avail_out))
            return 1
    }
    return t.insert = t.strstart < 2 ? t.strstart : 2,
    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
}
, Ot = (t,e)=>{
    let a, i, n;
    for (; ; ) {
      if (t.lookahead < bt) {
            if (St(t),
            t.lookahead < bt && e === Q)
                return 1;
            if (0 === t.lookahead)
                break
      }
      if (a = 0,
      t.lookahead >= 3 && (t.ins_h = xt(t, t.ins_h, t.window),
      a = t.prev = t.head,
      t.head = t.strstart),
      t.prev_length = t.match_length,
      t.prev_match = t.match_start,
      t.match_length = 2,
      0 !== a && t.prev_length < t.max_lazy_match && t.strstart - a <= t.w_size - bt && (t.match_length = Ut(t, a),
      t.match_length <= 5 && (t.strategy === ot || 3 === t.match_length && t.strstart - t.match_start > 4096) && (t.match_length = 2)),
      t.prev_length >= 3 && t.match_length <= t.prev_length) {
            n = t.strstart + t.lookahead - 3,
            i = q(t, t.strstart - 1 - t.prev_match, t.prev_length - 3),
            t.lookahead -= t.prev_length - 1,
            t.prev_length -= 2;
            do {
                ++t.strstart <= n && (t.ins_h = xt(t, t.ins_h, t.window),
                a = t.prev = t.head,
                t.head = t.strstart)
            } while (0 != --t.prev_length);
            if (t.match_available = 0,
            t.match_length = 2,
            t.strstart++,
            i && (At(t, !1),
            0 === t.strm.avail_out))
                return 1
      } else if (t.match_available) {
            if (i = q(t, 0, t.window),
            i && At(t, !1),
            t.strstart++,
            t.lookahead--,
            0 === t.strm.avail_out)
                return 1
      } else
            t.match_available = 1,
            t.strstart++,
            t.lookahead--
    }
    return t.match_available && (i = q(t, 0, t.window),
    t.match_available = 0),
    t.insert = t.strstart < 2 ? t.strstart : 2,
    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
}
;
function Tt(t, e, a, i, n) {
    this.good_length = t,
    this.max_lazy = e,
    this.nice_length = a,
    this.max_chain = i,
    this.func = n
}
const Lt = [new Tt(0,0,0,0,((t,e)=>{
    let a = 65535;
    for (a > t.pending_buf_size - 5 && (a = t.pending_buf_size - 5); ; ) {
      if (t.lookahead <= 1) {
            if (St(t),
            0 === t.lookahead && e === Q)
                return 1;
            if (0 === t.lookahead)
                break
      }
      t.strstart += t.lookahead,
      t.lookahead = 0;
      const i = t.block_start + a;
      if ((0 === t.strstart || t.strstart >= 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 < 0 && (e.wrap = -e.wrap),
    e.status = e.wrap ? 42 : mt,
    t.adler = 2 === e.wrap ? 0 : 1,
    e.last_flush = Q,
    G(e),
    at
}
, Bt = t=>{
    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.max_lazy,
    a.good_match = Lt.good_length,
    a.nice_match = Lt.nice_length,
    a.max_chain_length = Lt.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 < 0 ? (s = 0,
    i = -i) : i > 15 && (s = 2,
    i -= 16),
    n < 1 || n > 9 || a !== ut || i < 8 || i > 15 || e < 0 || e > 9 || r < 0 || 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 << l.w_bits,
    l.w_mask = l.w_size - 1,
    l.hash_bits = n + 7,
    l.hash_size = 1 << l.hash_bits,
    l.hash_mask = l.hash_size - 1,
    l.hash_shift = ~~((l.hash_bits + 3 - 1) / 3),
    l.window = new Uint8Array(2 * l.w_size),
    l.head = new Uint16Array(l.hash_size),
    l.prev = new Uint16Array(l.w_size),
    l.lit_bufsize = 1 << n + 6,
    l.pending_buf_size = 4 * l.lit_bufsize,
    l.pending_buf = new Uint8Array(l.pending_buf_size),
    l.d_buf = 1 * l.lit_bufsize,
    l.l_buf = 3 * l.lit_bufsize,
    l.level = e,
    l.strategy = r,
    l.method = a,
    Bt(t)
}
;
var Ct = {
    deflateInit: (t,e)=>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 < 0)
            return t ? kt(t, nt) : nt;
      const n = t.state;
      if (!t.output || !t.input && 0 !== t.avail_in || n.status === pt && e !== tt)
            return kt(t, 0 === t.avail_out ? st : nt);
      n.strm = t;
      const r = n.last_flush;
      if (n.last_flush = e,
      42 === n.status)
            if (2 === n.wrap)
                t.adler = 0,
                Et(n, 31),
                Et(n, 139),
                Et(n, 8),
                n.gzhead ? (Et(n, (n.gzhead.text ? 1 : 0) + (n.gzhead.hcrc ? 2 : 0) + (n.gzhead.extra ? 4 : 0) + (n.gzhead.name ? 8 : 0) + (n.gzhead.comment ? 16 : 0)),
                Et(n, 255 & n.gzhead.time),
                Et(n, n.gzhead.time >> 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 < 2 ? 4 : 0),
                Et(n, 255 & n.gzhead.os),
                n.gzhead.extra && n.gzhead.extra.length && (Et(n, 255 & n.gzhead.extra.length),
                Et(n, n.gzhead.extra.length >> 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 < 2 ? 4 : 0),
                Et(n, 3),
                n.status = mt);
            else {
                let e = ut + (n.w_bits - 8 << 4) << 8
                  , a = -1;
                a = n.strategy >= ht || n.level < 2 ? 0 : n.level < 6 ? 1 : 6 === n.level ? 2 : 3,
                e |= a << 6,
                0 !== n.strstart && (e |= 32),
                e += 31 - e % 31,
                n.status = mt,
                Rt(n, e),
                0 !== n.strstart && (Rt(n, t.adler >>> 16),
                Rt(n, 65535 & t.adler)),
                t.adler = 1
            }
      if (69 === n.status)
            if (n.gzhead.extra) {
                for (a = n.pending; n.gzindex < (65535 & n.gzhead.extra.length) && (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)); )
                  Et(n, 255 & n.gzhead.extra),
                  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 < n.gzhead.name.length ? 255 & n.gzhead.name.charCodeAt(n.gzindex++) : 0,
                  Et(n, i)
                } while (0 !== i);
                n.gzhead.hcrc && n.pending > 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 < n.gzhead.comment.length ? 255 & n.gzhead.comment.charCodeAt(n.gzindex++) : 0,
                  Et(n, i)
                } while (0 !== i);
                n.gzhead.hcrc && n.pending > 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 <= n.pending_buf_size && (Et(n, 255 & t.adler),
      Et(n, t.adler >> 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) <= vt(r) && e !== tt)
            return kt(t, st);
      if (n.status === pt && 0 !== t.avail_in)
            return kt(t, st);
      if (0 !== t.avail_in || 0 !== n.lookahead || e !== Q && n.status !== pt) {
            let a = n.strategy === ht ? ((t,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.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 <= wt) {
                        if (St(t),
                        t.lookahead <= wt && e === Q)
                            return 1;
                        if (0 === t.lookahead)
                            break
                  }
                  if (t.match_length = 0,
                  t.lookahead >= 3 && t.strstart > 0 && (n = t.strstart - 1,
                  i = s,
                  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 < r);
                        t.match_length = wt - (r - n),
                        t.match_length > 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.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.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 <= 0 ? it : (2 === n.wrap ? (Et(n, 255 & t.adler),
      Et(n, t.adler >> 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),
                i.prev = i.head,
                i.head = 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 = a)
      }
    }
    return t
}
, Kt = t=>{
    let e = 0;
    for (let i = 0, n = t.length; i < n; i++)
      e += t.length;
    const a = new Uint8Array(e);
    for (let i = 0, n = 0, r = t.length; i < r; i++) {
      let e = t;
      a.set(e, n),
      n += e.length
    }
    return a
}
;
let jt = !0;
try {
    String.fromCharCode.apply(null, new Uint8Array(1))
} catch (oa) {
    jt = !1
}
const Pt = new Uint8Array(256);
for (let ha = 0; ha < 256; ha++)
    Pt = ha >= 252 ? 6 : ha >= 248 ? 5 : ha >= 240 ? 4 : ha >= 224 ? 3 : ha >= 192 ? 2 : 1;
Pt = Pt = 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 < s; n++)
      a = t.charCodeAt(n),
      55296 == (64512 & a) && n + 1 < s && (i = t.charCodeAt(n + 1),
      56320 == (64512 & i) && (a = 65536 + (a - 55296 << 10) + (i - 56320),
      n++)),
      l += a < 128 ? 1 : a < 2048 ? 2 : a < 65536 ? 3 : 4;
    for (e = new Uint8Array(l),
    r = 0,
    n = 0; r < l; n++)
      a = t.charCodeAt(n),
      55296 == (64512 & a) && n + 1 < s && (i = t.charCodeAt(n + 1),
      56320 == (64512 & i) && (a = 65536 + (a - 55296 << 10) + (i - 56320),
      n++)),
      a < 128 ? e = a : a < 2048 ? (e = 192 | a >>> 6,
      e = 128 | 63 & a) : a < 65536 ? (e = 224 | a >>> 12,
      e = 128 | a >>> 6 & 63,
      e = 128 | 63 & a) : (e = 240 | a >>> 18,
      e = 128 | a >>> 12 & 63,
      e = 128 | a >>> 6 & 63,
      e = 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 < a; ) {
      let e = t;
      if (e < 128) {
            r = e;
            continue
      }
      let s = Pt;
      if (s > 4)
            r = 65533,
            i += s - 1;
      else {
            for (e &= 2 === s ? 31 : 3 === s ? 15 : 7; s > 1 && i < a; )
                e = e << 6 | 63 & t,
                s--;
            s > 1 ? r = 65533 : e < 65536 ? r = e : (e -= 65536,
            r = 55296 | e >> 10 & 1023,
            r = 56320 | 1023 & e)
      }
    }
    return ((t,e)=>{
      if (e < 65534 && t.subarray && jt)
            return String.fromCharCode.apply(null, t.length === e ? t : t.subarray(0, e));
      let a = "";
      for (let i = 0; i < e; i++)
            a += String.fromCharCode(t);
      return a
    }
    )(r, n)
}
, Xt = (t,e)=>{
    (e = e || t.length) > t.length && (e = t.length);
    let a = e - 1;
    for (; a >= 0 && 128 == (192 & t); )
      a--;
    return a < 0 || 0 === a ? e : a + Pt] > 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 < 16 && (e.windowBits += 16),
    this.err = 0,
    this.msg = "",
    this.ended = !1,
    this.chunks = [],
    this.strm = new Wt,
    this.strm.avail_out = 0;
    let a = Ct.deflateInit2(this.strm, e.level, e.method, e.windowBits, e.memLevel, e.strategy);
    if (a !== te)
      throw new Error(P);
    if (e.header && Ct.deflateSetHeader(this.strm, e.header),
    e.dictionary) {
      let t;
      if (t = "string" == typeof e.dictionary ? Yt(e.dictionary) : "" === qt.call(e.dictionary) ? new Uint8Array(e.dictionary) : e.dictionary,
      a = Ct.deflateSetDictionary(this.strm, t),
      a !== te)
            throw new Error(P);
      this._dict_set = !0
    }
}
function se(t, e) {
    const a = new re(e);
    if (a.push(t, !0),
    a.err)
      throw a.msg || P;
    return a.result
}
re.prototype.push = function(t, e) {
    const a = this.strm
      , i = this.options.chunkSize;
    let n, r;
    if (this.ended)
      return !1;
    for (r = e === ~~e ? e : !0 === e ? $t : Jt,
    "string" == typeof t ? a.input = Yt(t) : "" === qt.call(t) ? a.input = new Uint8Array(t) : a.input = t,
    a.next_in = 0,
    a.avail_in = a.input.length; ; )
      if (0 === a.avail_out && (a.output = new Uint8Array(i),
      a.next_out = 0,
      a.avail_out = i),
      (r === Qt || r === Vt) && a.avail_out <= 6)
            this.onData(a.output.subarray(0, a.next_out)),
            a.avail_out = 0;
      else {
            if (n = Ct.deflate(a, r),
            n === ee)
                return a.next_out > 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 << E.lenbits) - 1,
    g = (1 << E.distbits) - 1;
    t: do {
      c < 15 && (f += z << c,
      c += 8,
      f += z << c,
      c += 8),
      m = u;
      e: for (; ; ) {
            if (p = m >>> 24,
            f >>>= p,
            c -= p,
            p = m >>> 16 & 255,
            0 === p)
                A = 65535 & m;
            else {
                if (!(16 & p)) {
                  if (0 == (64 & p)) {
                        m = u[(65535 & m) + (f & (1 << p) - 1)];
                        continue e
                  }
                  if (32 & p) {
                        E.mode = 12;
                        break t
                  }
                  t.msg = "invalid literal/length code",
                  E.mode = 30;
                  break t
                }
                k = 65535 & m,
                p &= 15,
                p && (c < p && (f += z << c,
                c += 8),
                k += f & (1 << p) - 1,
                f >>>= p,
                c -= p),
                c < 15 && (f += z << c,
                c += 8,
                f += z << c,
                c += 8),
                m = w;
                a: for (; ; ) {
                  if (p = m >>> 24,
                  f >>>= p,
                  c -= p,
                  p = m >>> 16 & 255,
                  !(16 & p)) {
                        if (0 == (64 & p)) {
                            m = w[(65535 & m) + (f & (1 << p) - 1)];
                            continue a
                        }
                        t.msg = "invalid distance code",
                        E.mode = 30;
                        break t
                  }
                  if (v = 65535 & m,
                  p &= 15,
                  c < p && (f += z << c,
                  c += 8,
                  c < p && (f += z << c,
                  c += 8)),
                  v += f & (1 << p) - 1,
                  v > 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 < k) {
                              k -= p;
                              do {
                                    A = _
                              } while (--p);
                              y = n - v,
                              x = A
                            }
                        } else if (d < p) {
                            if (y += o + d - p,
                            p -= d,
                            p < k) {
                              k -= p;
                              do {
                                    A = _
                              } while (--p);
                              if (y = 0,
                              d < k) {
                                    p = d,
                                    k -= p;
                                    do {
                                        A = _
                                    } while (--p);
                                    y = n - v,
                                    x = A
                              }
                            }
                        } else if (y += d - p,
                        p < k) {
                            k -= p;
                            do {
                              A = _
                            } while (--p);
                            y = n - v,
                            x = A
                        }
                        for (; k > 2; )
                            A = x,
                            A = x,
                            A = x,
                            k -= 3;
                        k && (A = x,
                        k > 1 && (A = x))
                  } else {
                        y = n - v;
                        do {
                            A = A,
                            A = A,
                            A = A,
                            k -= 3
                        } while (k > 2);
                        k && (A = A,
                        k > 1 && (A = A))
                  }
                  break
                }
            }
            break
      }
    } while (a < i && n < s);
    k = c >> 3,
    a -= k,
    c -= k << 3,
    f &= (1 << c) - 1,
    t.next_in = a,
    t.next_out = n,
    t.avail_in = a < i ? i - a + 5 : 5 - (a - i),
    t.avail_out = n < s ? s - n + 257 : 257 - (n - s),
    E.hold = f,
    E.bits = c
};
const he = 15
, de = new Uint16Array()
, _e = new Uint8Array()
, fe = new Uint16Array()
, ce = new Uint8Array();
var ue = (t,e,a,i,n,r,s,l)=>{
    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 <= he; w++)
      R = 0;
    for (b = 0; b < i; b++)
      R]++;
    for (p = o,
    m = he; m >= 1 && 0 === R; m--)
      ;
    if (p > m && (p = m),
    0 === m)
      return n = 20971520,
      n = 20971520,
      l.bits = 1,
      0;
    for (g = 1; g < m && 0 === R; g++)
      ;
    for (p < g && (p = g),
    y = 1,
    w = 1; w <= he; w++)
      if (y <<= 1,
      y -= R,
      y < 0)
            return -1;
    if (y > 0 && (0 === t || 1 !== m))
      return -1;
    for (Z = 0,
    w = 1; w < he; w++)
      Z = Z + R;
    for (b = 0; b < i; b++)
      0 !== e && (s]++] = b);
    if (0 === t ? (A = O = s,
    u = 19) : 1 === t ? (A = de,
    E -= 257,
    O = _e,
    T -= 257,
    u = 256) : (A = fe,
    O = ce,
    u = -1),
    z = 0,
    b = 0,
    w = g,
    c = r,
    k = p,
    v = 0,
    _ = -1,
    x = 1 << p,
    f = x - 1,
    1 === t && x > 852 || 2 === t && x > 592)
      return 1;
    for (; ; ) {
      U = w - v,
      s < u ? (S = 0,
      D = s) : s > u ? (S = O],
      D = A]) : (S = 96,
      D = 0),
      h = 1 << w - v,
      d = 1 << k,
      g = d;
      do {
            d -= h,
            n = U << 24 | S << 16 | D | 0
      } while (0 !== d);
      for (h = 1 << w - 1; z & h; )
            h >>= 1;
      if (0 !== h ? (z &= h - 1,
      z += h) : z = 0,
      b++,
      0 == --R) {
            if (w === m)
                break;
            w = e]
      }
      if (w > p && (z & f) !== _) {
            for (0 === v && (v = p),
            c += g,
            k = w - v,
            y = 1 << k; k + v < m && (y -= R,
            !(y <= 0)); )
                k++,
                y <<= 1;
            if (x += 1 << k,
            1 === t && x > 852 || 2 === t && x > 592)
                return 1;
            _ = z & f,
            n = p << 24 | k << 16 | c - r | 0
      }
    }
    return 0 !== z && (n = w - v << 24 | 64 << 16 | 0),
    l.bits = p,
    0
}
;
const {Z_FINISH: we, Z_BLOCK: be, Z_TREES: ge, Z_OK: me, Z_STREAM_END: pe, Z_NEED_DICT: ke, Z_STREAM_ERROR: ve, Z_DATA_ERROR: ye, Z_MEM_ERROR: xe, Z_BUF_ERROR: ze, Z_DEFLATED: Ae} = Y
, Ee = 12
, Re = 30
, Ze = t=>(t >>> 24 & 255) + (t >>> 8 & 65280) + ((65280 & t) << 8) + ((255 & t) << 24);
function Ue() {
    this.mode = 0,
    this.last = !1,
    this.wrap = 0,
    this.havedict = !1,
    this.flags = 0,
    this.dmax = 0,
    this.check = 0,
    this.total = 0,
    this.head = null,
    this.wbits = 0,
    this.wsize = 0,
    this.whave = 0,
    this.wnext = 0,
    this.window = null,
    this.hold = 0,
    this.bits = 0,
    this.length = 0,
    this.offset = 0,
    this.extra = 0,
    this.lencode = null,
    this.distcode = null,
    this.lenbits = 0,
    this.distbits = 0,
    this.ncode = 0,
    this.nlen = 0,
    this.ndist = 0,
    this.have = 0,
    this.next = null,
    this.lens = new Uint16Array(320),
    this.work = new Uint16Array(288),
    this.lendyn = null,
    this.distdyn = null,
    this.sane = 0,
    this.back = 0,
    this.was = 0
}
const Se = 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 < 0 ? (a = 0,
    e = -e) : (a = 1 + (e >> 4),
    e < 48 && (e &= 15)),
    e && (e < 8 || 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 < 144; )
            t.lens = 8;
      for (; e < 256; )
            t.lens = 9;
      for (; e < 280; )
            t.lens = 7;
      for (; e < 288; )
            t.lens = 8;
      for (ue(1, t.lens, 0, 288, Le, 0, t.work, {
            bits: 9
      }),
      e = 0; e < 32; )
            t.lens = 5;
      ue(2, t.lens, 0, 32, Ne, 0, t.work, {
            bits: 5
      }),
      Fe = !1
    }
    t.lencode = Le,
    t.lenbits = 9,
    t.distcode = Ne,
    t.distbits = 5
}
, Ie = (t,e,a,i)=>{
    let n;
    const r = t.state;
    return null === r.window && (r.wsize = 1 << r.wbits,
    r.wnext = 0,
    r.whave = 0,
    r.window = new Uint8Array(r.wsize)),
    i >= 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 < r.wsize && (r.whave += n))),
    0
}
;
var Ce = {
    inflateReset: De,
    inflateReset2: Oe,
    inflateResetKeep: Se,
    inflateInit: t=>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();
      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 < 16; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                if (2 & a.wrap && 35615 === h) {
                  a.check = 0,
                  A = 255 & h,
                  A = h >>> 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) + (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 << a.wbits,
                t.adler = a.check = 1,
                a.mode = 512 & h ? 10 : Ee,
                h = 0,
                d = 0;
                break;
            case 2:
                for (; d < 16; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                if (a.flags = h,
                (255 & a.flags) !== Ae) {
                  t.msg = "unknown compression method",
                  a.mode = Re;
                  break
                }
                if (57344 & a.flags) {
                  t.msg = "unknown header flags set",
                  a.mode = Re;
                  break
                }
                a.head && (a.head.text = h >> 8 & 1),
                512 & a.flags && (A = 255 & h,
                A = h >>> 8 & 255,
                a.check = j(a.check, A, 2, 0)),
                h = 0,
                d = 0,
                a.mode = 3;
            case 3:
                for (; d < 32; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                a.head && (a.head.time = h),
                512 & a.flags && (A = 255 & h,
                A = h >>> 8 & 255,
                A = h >>> 16 & 255,
                A = h >>> 24 & 255,
                a.check = j(a.check, A, 4, 0)),
                h = 0,
                d = 0,
                a.mode = 4;
            case 4:
                for (; d < 16; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                a.head && (a.head.xflags = 255 & h,
                a.head.os = h >> 8),
                512 & a.flags && (A = 255 & h,
                A = 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 < 16; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  a.length = h,
                  a.head && (a.head.extra_len = h),
                  512 & a.flags && (A = 255 & h,
                  A = h >>> 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,
                        a.head && y && a.length < 65536 && (a.head.name += String.fromCharCode(y))
                  } while (y && c < l);
                  if (512 & a.flags && (a.check = j(a.check, i, c, r)),
                  l -= c,
                  r += c,
                  y)
                        break t
                } else
                  a.head && (a.head.name = null);
                a.length = 0,
                a.mode = 8;
            case 8:
                if (4096 & a.flags) {
                  if (0 === l)
                        break t;
                  c = 0;
                  do {
                        y = i,
                        a.head && y && a.length < 65536 && (a.head.comment += String.fromCharCode(y))
                  } while (y && c < l);
                  if (512 & a.flags && (a.check = j(a.check, i, c, r)),
                  l -= c,
                  r += c,
                  y)
                        break t
                } else
                  a.head && (a.head.comment = null);
                a.mode = 9;
            case 9:
                if (512 & a.flags) {
                  for (; d < 16; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  if (h !== (65535 & a.check)) {
                        t.msg = "header crc mismatch",
                        a.mode = Re;
                        break
                  }
                  h = 0,
                  d = 0
                }
                a.head && (a.head.hcrc = a.flags >> 9 & 1,
                a.head.done = !0),
                t.adler = a.check = 0,
                a.mode = Ee;
                break;
            case 10:
                for (; d < 32; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                t.adler = a.check = Ze(h),
                h = 0,
                d = 0,
                a.mode = 11;
            case 11:
                if (0 === a.havedict)
                  return t.next_out = s,
                  t.avail_out = o,
                  t.next_in = r,
                  t.avail_in = l,
                  a.hold = h,
                  a.bits = d,
                  ke;
                t.adler = a.check = 1,
                a.mode = Ee;
            case Ee:
                if (e === be || e === ge)
                  break t;
            case 13:
                if (a.last) {
                  h >>>= 7 & d,
                  d -= 7 & d,
                  a.mode = 27;
                  break
                }
                for (; d < 3; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                switch (a.last = 1 & h,
                h >>>= 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 < 32; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                if ((65535 & h) != (h >>> 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 < 14; ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                if (a.nlen = 257 + (31 & h),
                h >>>= 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 < a.ncode; ) {
                  for (; d < 3; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  a.lens] = 7 & h,
                  h >>>= 3,
                  d -= 3
                }
                for (; a.have < 19; )
                  a.lens] = 0;
                if (a.lencode = a.lendyn,
                a.lenbits = 7,
                E = {
                  bits: a.lenbits
                },
                x = ue(0, a.lens, 0, 19, a.lencode, 0, a.work, E),
                a.lenbits = E.bits,
                x) {
                  t.msg = "invalid code lengths set",
                  a.mode = Re;
                  break
                }
                a.have = 0,
                a.mode = 19;
            case 19:
                for (; a.have < a.nlen + a.ndist; ) {
                  for (; z = a.lencode,
                  b = z >>> 24,
                  g = z >>> 16 & 255,
                  m = 65535 & z,
                  !(b <= d); ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  if (m < 16)
                        h >>>= b,
                        d -= b,
                        a.lens = m;
                  else {
                        if (16 === m) {
                            for (R = b + 2; d < R; ) {
                              if (0 === l)
                                    break t;
                              l--,
                              h += i << d,
                              d += 8
                            }
                            if (h >>>= b,
                            d -= b,
                            0 === a.have) {
                              t.msg = "invalid bit length repeat",
                              a.mode = Re;
                              break
                            }
                            y = a.lens,
                            c = 3 + (3 & h),
                            h >>>= 2,
                            d -= 2
                        } else if (17 === m) {
                            for (R = b + 3; d < R; ) {
                              if (0 === l)
                                    break t;
                              l--,
                              h += i << d,
                              d += 8
                            }
                            h >>>= b,
                            d -= b,
                            y = 0,
                            c = 3 + (7 & h),
                            h >>>= 3,
                            d -= 3
                        } else {
                            for (R = b + 7; d < R; ) {
                              if (0 === l)
                                    break t;
                              l--,
                              h += i << d,
                              d += 8
                            }
                            h >>>= 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 = y
                  }
                }
                if (a.mode === Re)
                  break;
                if (0 === a.lens) {
                  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,
                b = z >>> 24,
                g = z >>> 16 & 255,
                m = 65535 & z,
                !(b <= d); ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                if (g && 0 == (240 & g)) {
                  for (p = b,
                  k = g,
                  v = m; z = a.lencode,
                  b = z >>> 24,
                  g = z >>> 16 & 255,
                  m = 65535 & z,
                  !(p + b <= d); ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  h >>>= 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 < R; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  a.length += h & (1 << a.extra) - 1,
                  h >>>= a.extra,
                  d -= a.extra,
                  a.back += a.extra
                }
                a.was = a.length,
                a.mode = 23;
            case 23:
                for (; z = a.distcode,
                b = z >>> 24,
                g = z >>> 16 & 255,
                m = 65535 & z,
                !(b <= d); ) {
                  if (0 === l)
                        break t;
                  l--,
                  h += i << d,
                  d += 8
                }
                if (0 == (240 & g)) {
                  for (p = b,
                  k = g,
                  v = m; z = a.distcode,
                  b = z >>> 24,
                  g = z >>> 16 & 255,
                  m = 65535 & z,
                  !(p + b <= d); ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  h >>>= 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 < R; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  a.offset += h & (1 << a.extra) - 1,
                  h >>>= 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 = w
                } while (--c);
                0 === a.length && (a.mode = 21);
                break;
            case 26:
                if (0 === o)
                  break t;
                n = a.length,
                o--,
                a.mode = 21;
                break;
            case 27:
                if (a.wrap) {
                  for (; d < 32; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h |= i << d,
                        d += 8
                  }
                  if (f -= o,
                  t.total_out += f,
                  a.total += f,
                  f && (t.adler = a.check = a.flags ? j(a.check, n, f, s - f) : M(a.check, n, f, s - f)),
                  f = o,
                  (a.flags ? h : Ze(h)) !== a.check) {
                        t.msg = "incorrect data check",
                        a.mode = Re;
                        break
                  }
                  h = 0,
                  d = 0
                }
                a.mode = 28;
            case 28:
                if (a.wrap && a.flags) {
                  for (; d < 32; ) {
                        if (0 === l)
                            break t;
                        l--,
                        h += i << d,
                        d += 8
                  }
                  if (h !== (4294967295 & a.total)) {
                        t.msg = "incorrect length check",
                        a.mode = Re;
                        break
                  }
                  h = 0,
                  d = 0
                }
                a.mode = 29;
            case 29:
                x = pe;
                break t;
            case Re:
                x = ye;
                break t;
            case 31:
                return xe;
            default:
                return ve
            }
      return t.next_out = s,
      t.avail_out = o,
      t.next_in = r,
      t.avail_in = l,
      a.hold = h,
      a.bits = d,
      (a.wsize || f !== t.avail_out && a.mode < Re && (a.mode < 27 || e !== we)) && Ie(t, t.output, t.next_out, f - t.avail_out),
      _ -= t.avail_in,
      f -= t.avail_out,
      t.total_in += _,
      t.total_out += f,
      a.total += f,
      a.wrap && f && (t.adler = a.check = a.flags ? j(a.check, n, f, t.next_out - f) : M(a.check, n, f, t.next_out - f)),
      t.data_type = a.bits + (a.last ? 64 : 0) + (a.mode === Ee ? 128 : 0) + (20 === a.mode || 15 === a.mode ? 256 : 0),
      (0 === _ && 0 === f || e === we) && x === me && (x = ze),
      x
    }
    ,
    inflateEnd: t=>{
      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 < 16 && (e.windowBits = -e.windowBits,
    0 === e.windowBits && (e.windowBits = -15)),
    !(e.windowBits >= 0 && e.windowBits < 16) || t && t.windowBits || (e.windowBits += 32),
    e.windowBits > 15 && e.windowBits < 48 && 0 == (15 & e.windowBits) && (e.windowBits |= 15),
    this.err = 0,
    this.msg = "",
    this.ended = !1,
    this.chunks = [],
    this.strm = new Wt,
    this.strm.avail_out = 0;
    let a = Ce.inflateInit2(this.strm, e.windowBits);
    if (a !== Pe)
      throw new Error(P);
    if (this.header = new He,
    Ce.inflateGetHeader(this.strm, this.header),
    e.dictionary && ("string" == typeof e.dictionary ? e.dictionary = Yt(e.dictionary) : "" === Me.call(e.dictionary) && (e.dictionary = new Uint8Array(e.dictionary)),
    e.raw && (a = Ce.inflateSetDictionary(this.strm, e.dictionary),
    a !== Pe)))
      throw new Error(P)
}
function Qe(t, e) {
    const a = new Je(e);
    if (a.push(t),
    a.err)
      throw a.msg || P;
    return a.result
}
Je.prototype.push = function(t, e) {
    const a = this.strm
      , i = this.options.chunkSize
      , n = this.options.dictionary;
    let r, s, l;
    if (this.ended)
      return !1;
    for (s = e === ~~e ? e : !0 === e ? je : Ke,
    "" === Me.call(t) ? a.input = new Uint8Array(t) : a.input = t,
    a.next_in = 0,
    a.avail_in = a.input.length; ; ) {
      for (0 === a.avail_out && (a.output = new Uint8Array(i),
      a.next_out = 0,
      a.avail_out = i),
      r = Ce.inflate(a, s),
      r === Ge && n && (r = Ce.inflateSetDictionary(a, n),
      r === Pe ? r = Ce.inflate(a, s) : r === We && (r = Ge)); a.avail_in > 0 && r === Ye && a.state.wrap > 0 && 0 !== t; )
            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然后再处理

![](https://s2.loli.net/2023/05/18/WuBb5fDMmAxUY24.png#id=xaJnA&originHeight=774&originWidth=1711&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

所以js这样处理

![](https://s2.loli.net/2023/05/18/ezXnAtIHjYLFa9p.png#id=W7ePM&originHeight=256&originWidth=686&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

python:

![](https://s2.loli.net/2023/05/18/8siUArKvwfOq7xo.png#id=AfvJG&originHeight=728&originWidth=1806&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

现在数据都处理好了,就剩r.Response这个proto文件了,就能解析了。

![](https://s2.loli.net/2023/05/18/oYPxilv1fJ69DyM.png#id=SHefD&originHeight=553&originWidth=901&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

跟进去,发现是一个地方。我们刚刚不是处理过吗,找一下文件里面的,拿出来:

```protobuf
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;
}
```

编译一下,看效果:

网站上:

![](https://s2.loli.net/2023/05/18/7cUuRtz4NdBaoQJ.png#id=WkhWb&originHeight=718&originWidth=1077&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

python:

![](https://s2.loli.net/2023/05/18/ySr4mq7MGQox3zC.png#id=aVfNf&originHeight=743&originWidth=1794&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

一致,本来第一项列表是有数据,可以看到里面消息类型的,刚刚不小心跳过断点了,然后直播结束了,没消息了....尴尬。但是暂时不影响分析,我们把ack也完成就换个直播间。

### ③ack处理:

因为这里needack是true,所以需要发一个包:

![](https://s2.loli.net/2023/05/18/8iexz5jtBT3R9Sv.png#id=uXQ5t&originHeight=760&originWidth=1631&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

可以看到,我们构造没问题。

代码:

```
#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函数:

![](https://s2.loli.net/2023/05/18/AeKc2rmR3aYFnwX.png#id=mFXE5&originHeight=708&originWidth=1078&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

![](https://s2.loli.net/2023/05/18/ozgf8LsthJY6C2d.png#id=j4pNU&originHeight=780&originWidth=1172&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

猜测应该是payload里面是弹幕信息,我们进到emit函数里面看看吧。

```javascript
            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列表第一个:

![](https://s2.loli.net/2023/05/18/cPsIoBaNDeK1jkW.png#id=JcReH&originHeight=166&originWidth=1134&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

我们可以看到,没错,是第一个WebcastMemberMessage,然后把这message列表第一个对象扔进了这个runAllEvents函数处理:

![](https://s2.loli.net/2023/05/18/ADh48erkHtVJ7jS.png#id=B8maS&originHeight=421&originWidth=779&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

进入runAllEvents函数,我们又看到了熟悉的关键词:

![](https://s2.loli.net/2023/05/18/B7S2DGsLvrXtkWV.png#id=SZunq&originHeight=411&originWidth=1050&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

大概猜测这里就能得到我们想要的数据了,下断点,运行到这看s值。

![](https://s2.loli.net/2023/05/18/FwSnzXIyGudtamY.png#id=J2FUo&originHeight=699&originWidth=1036&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

果然显示了用户名还有一些信息。

代码分析:

```javascript
            runAllEvents(e, t) {
                var a;
                //this.eventsMap.entries()里面包含所有事件模块
                for (const of this.eventsMap.entries()) {
                  const a = this.messageModules; //创建模块
                  //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打包很分散,我讲一下我的处理方法吧:

![](https://s2.loli.net/2023/05/18/6xdsTnuWLRI4cNr.png#id=Di8mK&originHeight=733&originWidth=1173&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

相似报错可以直接同一值替换(可能不太严谨,但是跟了很多都是同一个),我们可以把这个直接全部复制到一个大proto文件中,比如说我们最开始那个,折叠起来,然后慢慢把报错解决。

我演示Common的处理,其他类似就好:

![](https://s2.loli.net/2023/05/18/6wrDymh9vBp1dkC.png#id=DHAu6&originHeight=542&originWidth=945&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

跟进去,直接定位Common,因为p对象,所以代码无法体现是谁,所以处理会报错。

跟进去后直接复制js到工具(记得创建自己要处理的文件js),直接处理。

![](https://s2.loli.net/2023/05/18/kFX1ENtYZ592lTO.png#id=SfdZW&originHeight=551&originWidth=745&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

将补充的补充到下面,标注一下,说不定其他模块也要用,反正我们都复制下来。

我这里采用nodepad++全体Common替换成 webcast_im_Common

![](https://s2.loli.net/2023/05/18/PG3gzunhIRMBDvj.png#id=pzADB&originHeight=887&originWidth=1859&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

这里就解决了

![](https://s2.loli.net/2023/05/18/9RUVd1fI6qyO4sT.png#id=YCobk&originHeight=896&originWidth=1843&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

然后后面的也接着一样操作来,直到补全完整的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

![](https://s2.loli.net/2023/05/18/JPw4QFd7mv5L2Vb.png#id=w1YeF&originHeight=860&originWidth=1020&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

补到后面会发现3个map类型的数据,因为工具没处理(大佬可以帮忙添加一下处理map类型的方法),我们注释掉里面的内容就好

![](https://s2.loli.net/2023/05/18/rtuZPvRsheXDy7V.png#id=T5slf&originHeight=91&originWidth=619&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

20分钟左右补完了,经历了3个webpack,都是上w行代码的js。

![](https://s2.loli.net/2023/05/18/Br1ASPIqj3U4DVL.png#id=QSi7e&originHeight=771&originWidth=1239&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

很清楚的就得到刚刚那个WebcastMemberMessage类型数据的结构了,其他message类型(应该有6种)也是一样步骤,慢慢补充,就可以得到完整的proto文件,就能使用了。

![](https://s2.loli.net/2023/05/18/ULQrXdaZwvxGiNP.png#id=Zr6Gk&originHeight=328&originWidth=722&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

最后这个不常见,刚刚好关播给我抓到了。

# 六、python连接websocket一些注意点

上面我们已经分析了数据处理方面的东西,现在我们来用python连接websocket地址,然后实现open,onmessage事件等功能,然后获取我们想要的数据,我这里讲一些注意点吧。

### ①连接需要cookie

在fd搜索:

![](https://s2.loli.net/2023/05/18/oRCJvMzy2feHZVI.png#id=Avr7W&originHeight=426&originWidth=482&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

发现原始请求头我们要什么数据:

![](https://s2.loli.net/2023/05/18/h4OnDgJpUHGqLK3.png#id=fDorf&originHeight=287&originWidth=635&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

需要一个cookie,搜索ttwid就能看到cookie是怎么生成的,发现是刚开网页服务器发过来的。

### ②网址signature分析

![](https://s2.loli.net/2023/05/18/s9znMcRpmvDAFTW.png#id=hEkRV&originHeight=317&originWidth=965&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

从最开始,我们知道,网址是这里的t,然后进入u方法。

![](https://s2.loli.net/2023/05/18/mtMsAa9cC7DJZLl.png#id=QLWmt&originHeight=669&originWidth=970&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

这里就是我们要的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 : ""
   }
```

里面全扣下来,然后补环境可以解决,有轻微混淆吧。

![](https://s2.loli.net/2023/05/18/HF4xCzahejBpXWG.png#id=DuP0q&originHeight=330&originWidth=1655&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

![](https://s2.loli.net/2023/05/18/focDU5sKlNEFhZq.png#id=jtoOl&originHeight=760&originWidth=1642&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

只是可能js代码和node版本问题,警告了,不过没什么事。

其实这个参数目前还不会校验,删掉不携带也可以获得响应,不排除以后校验的可能。

# 七、效果展示

![](https://s2.loli.net/2023/05/18/fRBwbHm4y62WA3v.png#id=MFBGI&originHeight=518&originWidth=1112&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)

因为有些数据可能敏感,就放这张效果图了。

# 八、代码及总结

代码地址:(https://github.com/Prince-cool/dy_protobuf)

希望大家能给个star支持一下,哈哈哈哈哈,写的真的好辛苦啊!!!!

总结:这几天过节,也到处去玩,所以都是抽空慢慢补齐内容的,可能会有点跳跃,请见谅,因为如果要讲太细,可能篇幅太长了,文字也说不清楚,一共60多张图片。如果有机会,想要我录视频的,可以留言,我找机会录制一下,或者开个腾讯会议一起交流。希望这篇文章对你有益,感谢,也祝大家新年快乐,心想事成!

prince_cool 发表于 2023-2-15 11:19

视频地址:https://www.bilibili.com/video/BV1hM411w7L3/?vd_source=a09025848c538f7705d991cf872fe16e

QingYi. 发表于 2023-1-21 08:30

忘记手机打卡 发表于 2023-1-21 00:10
楼主能不能把整个过程简要概括一下,就想知道个原理和大概~

付钱开小灶

shiyier 发表于 2023-2-17 14:41

报错:Handshake status 200 OK
我用的是websocket-client 版本是1.5.1
代码没有改动

这是什么原因,

正己 发表于 2023-1-20 23:22

坐等大佬视频教学{:301_997:}

sunxing_1 发表于 2023-1-20 23:41

看了帖子大佬太强了

lewislihao 发表于 2023-1-20 23:47

期待大神的视频

Arcticlyc 发表于 2023-1-20 23:54

大佬,学习了。期待教学视频

忘记手机打卡 发表于 2023-1-21 00:10

楼主能不能把整个过程简要概括一下,就想知道个原理和大概~

bennyt 发表于 2023-1-21 02:32

好详细的教程,学习一下。

SVIP9大会员 发表于 2023-1-21 02:54

除夕快乐,大神 膜拜了,思路清晰缜密啊!

xixicoco 发表于 2023-1-21 03:21

大佬神级别作品,真的佩服,多谢分析
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 某音直播弹幕web端js逆向分析----protobuf实战及工具介绍