首页
论坛
课程
招聘
[原创]Protobuf协议解析文档-某音弹幕
2020-9-21 18:27 13637

[原创]Protobuf协议解析文档-某音弹幕

2020-9-21 18:27
13637

0x00 : 前言

   近期因为工作原因着手分析某音协议,在做到直播间这步的时候通过抓包发现其直播间内弹幕数据为protobuf协议,之前用xposed做wx的时候虽然接触过,但在下从来都是只要结果的人,直接调用wx内部函数转成对象即可,谁理你什么格式,但现在做的既然是协议,那自然是无端可用,什么东西都要自己处理,这里分享一下记录的分析流程.

0x01 : 准备工作

   在开始分析之前需要准备好:
      1.protobuf(安装方法百度即可);
      2.直播间弹幕抓包;
      3.通过protoc工具测试:将数据包里返回的弹幕内容复制到文本,通过指令
      protoc --decode_raw < xxx.bin 查看解析后的效果:

 

可以被工具正常解析,到这里准备工作就完成了, 接下来本文档以图中数据为例,进入分析流程;

0x02 : 分析流程

   上面通过protoc解析出了个大概,那么里面的数据究竟是什么意思呢? 1 2 3, 1: 2:这些都是什么呢? 网上所搜一番得知:解析出来的数据中开头的1 2 3..或 1: 2: 3:的均为protobuf的tag, 是解析数据的关键值,带":"号的是字段,没":"号的是 repeated 集合.
   那么知道了这些,要怎么还原数据呢,逆向中是没有原始.proto文件的,对于protobuf的解析就需要依靠分析源码,自己生成一份.proto文件,以上图为例,在源码中定位到解析代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private ProtoApiResult a(b bVar) throws Exception {
        PatchProxyResult proxy = PatchProxy.proxy(new Object[]{bVar}, this, a, false, 22172);
        if (proxy.isSupported) {
            return (ProtoApiResult) proxy.result;
        }
        com.bytedance.android.tools.pbadapter.a.b protoDecoder = ((INetworkService) d.a(INetworkService.class)).getProtoDecoder(com.bytedance.android.livesdkapi.message.f.class);
        if (protoDecoder != null) {
            com.bytedance.android.livesdkapi.message.f fVar = (com.bytedance.android.livesdkapi.message.f) protoDecoder.decode(this.s.a(bVar));
            ProtoApiResult protoApiResult = new ProtoApiResult();
            protoApiResult.cursor = fVar.b;
            protoApiResult.fetchInterval = fVar.c;
            protoApiResult.now = fVar.d;
            protoApiResult.messages = new LinkedList();
            this.m = fVar.e;
            long currentTimeMillis = System.currentTimeMillis();
            this.n = currentTimeMillis - this.k;
            ag.b = (fVar.d + ((currentTimeMillis - this.k) / 2)) - currentTimeMillis;
            if (Lists.isEmpty(fVar.a)) {
                return protoApiResult;
            }
            //省略......

其中参数(b bVar)权当作http请求后的body即可,无关紧要,重点是com.bytedance.android.livesdkapi.message.f 这个类,进去看一下类里的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.bytedance.android.livesdkapi.message;
public class f {
    @SerializedName("messages")
    public List<a> a;
    @SerializedName("cursor")
    public String b;
    @SerializedName("fetch_interval")
    public long c;
    @SerializedName("now")
    public long d;
    @SerializedName("internal_ext")
    public String e;
    public static final class a {
        @SerializedName("method")   //这个实际上是类名,根据这个字段获取对应解析的类.
        public String a;
        @SerializedName("payload"//解析的数据内容,同样是protobuf.
        public byte[] b;
    }
}

对比一下解析出来的数据图,发现对应tag:2,3,4,5

 

 

tag1是个数组,类型是内部类a, 内部类中剩下的2个字段是用于动态定位接下来解析的类:
method => 解析剩下数据包中的类名, payload => 数据内容,method 也就是这部分:

 

 

分析到此数据结构有了大致了解,com.bytedance.android.livesdkapi.message.f是某音弹幕数据的最外层,用来保存直播间弹幕所有的消息类型,从格式上不难理解,会有多种method,这里先以"WebcastRoomMessage"为例查找对应关系:
在代码里搜索一下很快找到了 com.bytedance.android.livesdkapi.depend.g.a:

 

 

找到对这个枚举的引用,定位到 com.bytedance.android.livesdk.chatroom.bl.a:

 

 

对应关系找到,说明剩下byte[]是在 cl.class这个类中解析,看一下里面的结构:

1
2
3
4
5
6
7
8
9
public class cl extends d {             //注意继承关系
    public static ChangeQuickRedirect a;
    @SerializedName("content")
    public String b;
    @SerializedName("supprot_landscape")
    public boolean c;
    public cl() {
        this.type = com.bytedance.android.livesdkapi.depend.g.a.ROOM;   <-枚举类型
    }

"WebcastRoomMessage"中只有2个字段,"content"和"supprot_landscape",可是根据工具解析出来的数据可以看到,还有一大坨呢,剩下的数据在哪,并且这2个的tag排列关系又是什么? 那只能继续查找cl这个类是怎么解析的,经过一番查找,找到了解析对应关系,com.bytedance.android.live.base.model.proto.d ,在这里可以看到负责解析cl.class的类是 gw.class:

 

进入gw.class:

 

 

在这里知道了"content"和"supprot_landscape"分别对应tag 2和3,还有第三个名为"baseMessage"的字段要解析,这个是cl.class的基类成员,通过多个数据包验证得知,所有method中,都继承同一个基类:

 

 

之前没解析出来的数据也在其中,看下这个名为baseMessage成员的内部结构,并找一下"tag表":

 

 

分析到此,弹幕结构渐渐清晰了, 如下图:

 

 

现在可以根据分析结果开始写proto文件了.

0x03 : proto文件编写

   关于proto文件语法网上资料很多,这里先说几个关键的,proto语法有"proto2"和"proto3",我没看出来对于咱们逆向人员选择"proto3"有什么用处,并且3的语法是不需要字段修饰符的,这只会让我们在写proto文件的时候更加迷糊,所以我们选择"proto2"来写.
   或许你已经发现,基类(baseMessage)有很多字段,怎么解析出来的就"method","msg_id","room_id","create_time"这么几个,其实这也是很正常的情况,并不是每一个字段都是必选的.proto有3个字段修饰符required(必选),optional(可选),repeated(重复),说白了就是咱们的proto文件不使用required,只用optional,因为咱们也不知道哪个字段什么时候才有值,如果是数组一律用repeated修饰.
   注意proto的数据类型,以java为例.String => string ; int => int32; long => uint64;...更多类型可自行查找资料.
开始吧,先写一份 ProtoApiResult.proto,对应上面的
com.bytedance.android.livesdkapi.message.f ,用来保存弹幕整体数据:

 

 

外层有了,现在写"WebcastRoomMessage"对应的类:

 

 

接下来最后一个"CommonMessageData":

 

 

注意这里的display_text字段,这还是一个对象,里面又是N多字段,这里我已经解析过了, 大家自己在分析过程中如果不想解析那么多,可以删除不需要的字段,或者将类型定义为bytes即可.只要tag不乱就行.
   OK,将.proto打包成java文件: protoc --java_out=out .*.proto
之后在out目录就生成了可以使用的java文件, 导入到项目中即可,最后看下解码效果:

 

 

   嗯,费了一大堆事最后只解码一个系统提示...,这也没办法,某音一次请求动辄数万字节的返回数据,用那些做例子的话,我这图都没法截,迫不得已找了个最小的,根据上述流程其实其他类型的数据解析都是一样的,没什么区别,最后上个最终演示效果:

 

 

 

首次发帖,多多关照.


看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

收藏
点赞4
打赏
分享
最新回复 (20)
雪    币: 6175
活跃值: 活跃值 (3270)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 活跃值 4 2020-9-21 18:50
2
1
赞,还有个后台,
雪    币: 260
活跃值: 活跃值 (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小豆芽 活跃值 2020-9-21 19:42
3
0
赞,还有个后台,
雪    币: 2649
活跃值: 活跃值 (1287)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Vn小帆 活跃值 2020-9-22 10:28
4
0
弹幕也是websocket?
雪    币: 482
活跃值: 活跃值 (806)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
天荒怨未泯 活跃值 2020-9-22 11:39
5
0
Vn小帆 弹幕也是websocket?
http,1秒一次
雪    币: 5440
活跃值: 活跃值 (2883)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
尐进 活跃值 2020-9-22 15:14
6
1

request请求中将protobuf改为json有奇效哦

最后于 2020-9-22 15:16 被尐进编辑 ,原因:
雪    币: 482
活跃值: 活跃值 (806)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
天荒怨未泯 活跃值 2020-9-22 17:32
7
0
尐进 request请求中将protobuf改为json有奇效哦
666...我是真没往这块想过
雪    币: 601
活跃值: 活跃值 (748)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Lateautumn4 活跃值 2020-9-22 17:55
8
0
尐进 request请求中将protobuf改为json有奇效哦
我记得弹幕请求有个字段是说明是json还是protobuf,可能我指定了json内容就为空了,哈哈,不知道是不是姿势不对
雪    币: 2357
活跃值: 活跃值 (1436)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dreamhake 活跃值 2020-9-22 18:08
9
0
大佬实在什么样的公司啊,还有这种需求
雪    币: 482
活跃值: 活跃值 (806)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
天荒怨未泯 活跃值 2020-9-22 18:13
10
0
dreamhake 大佬实在什么样的公司啊,还有这种需求
一个濒临散伙前 疯狂试探其他路子的公司
雪    币: 238
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wkys 活跃值 2020-9-22 20:05
11
0
大佬 你这是哪个版本呀,想学习,能给个联系方式不
雪    币: 238
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wkys 活跃值 2020-9-23 10:22
12
0
大佬 我试了好几个版本 没找到com.bytedance.android.livesdkapi.message.f这个类呀
雪    币: 2357
活跃值: 活跃值 (1436)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dreamhake 活跃值 2020-9-23 12:16
13
0
天荒怨未泯 一个濒临散伙前 疯狂试探其他路子的公司[em_78]
厉害了,哈哈
雪    币: 30
活跃值: 活跃值 (324)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
紫川 活跃值 2020-9-24 08:32
14
0
雪    币: 973
活跃值: 活跃值 (234)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lylxd 活跃值 2020-9-24 19:29
15
0
看着像求职帖
雪    币: 238
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wkys 活跃值 2020-9-26 20:21
16
0



大佬我按你写的proto文件,这样使用出现了这个错误,这个咋办呀

雪    币: 1332
活跃值: 活跃值 (968)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
影………… 活跃值 1 2020-12-15 02:05
17
0
wkys 大佬我按你写的proto文件,这样使用出现了这个错误,这个咋办呀
https://bbs.pediy.com/thread-264034.htm 看这一篇。会得到你想要的。
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
中单李云龙 活跃值 2021-1-22 11:30
18
0
请问你用的哪个版本的dy?
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
罗伟龙 活跃值 2021-11-25 19:01
19
0
大佬 请问能给个联系方式吗?????
雪    币: 20
能力值: (RANK:0 )
在线值:
发帖
回帖
粉丝
寻求付费帮助 活跃值 2021-11-27 19:04
20
0
楼主联系我项目合作
 V18233706300
雪    币: 21
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
万里星河 活跃值 2021-11-27 19:53
21
0
还有个后台 讲究
游客
登录 | 注册 方可回帖
返回