//分析的apk版本: 哔哩哔哩动画 android6.11.0
//逆向所用工具frida+objection、gda+jadx
//手机环境android8.1.0_r1+magisk+wifiadb
1.通过抓包获得的请求头中的关键字定位到z1.c.v.p.a.d.b.f.a关键类
用objection观察此类,点击查看视频时的方法调用顺序为:
(agent) [1v8ainsl9xp] Called z1.c.v.p.a.d.b.f.a.b(z1.c.v.p.a.d.b.f.a, io.grpc.l0)
(agent) [1v8ainsl9xp] Called z1.c.v.p.a.d.b.f.a.c(io.grpc.l0) (agent) [1v8ainsl9xp] Called z1.c.v.p.a.d.b.f.a.a(io.grpc.MethodDescriptor, io.grpc.d, io.grpc.e)

  1. 观察z1.c.v.p.a.d.b.f.a.c方法:

private final void a.c(l0 p0) //method@00a986
{
   p0.m(this.a, HeadersKt.k());
   String si = HeadersKt.i();
   if ((k.m1(si)^0x01)) {     
      p0.m(this.b, si);
   }  
   p0.m(this.c, HeadersKt.h());
   p0.m(this.d, HeadersKt.l());
   p0.m(this.e, HeadersKt.m());
   p0.m(this.f, HeadersKt.j());
   return;
}

跟进到io.grpc.l0.m方法,原型:public void l0.m(l0$g p0,Object p1),源码:


public void l0.m(l0$g p0,Object p1)	//method@00a07a
{
   i.o(p0, "key");
   i.o(p1, "value");
   this.i();
   this.k(this.b, p0.a());
   this.o(this.b, p0.i(p1));
   this.b = this.b+1;
}

通过简单分析,hook当前类的k和o方法,点击视频,打印出:
int1: 0
byte1 to String: x-bili-fawkes-req-bin
int2: 0
byte2 to String: androidprod
int1: 1
byte1 to String: x-bili-metadata-bin
int2: 1
byte2 to String: android ���bili2%XYB2DD7B401DE6C86298B604005D003FDB102:android
int1: 2
byte1 to String: x-bili-device-bin
int2: 2
byte2 to String:���%XYB2DD7B401DE6C86298B604005D003FDB102"android
android:biliBAndroidJAOSP on msm8996R8.1.0
int1: 3
byte1 to String: x-bili-network-bin
int2: 3
byte2 to String:
int1: 4
byte1 to String: x-bili-restriction-bin
int2: 4
byte2 to String:
int1: 5
byte1 to String: x-bili-locale-bin
int2: 5
byte2 to String:

zhHansCN
zhHansCN
int1: 6
byte1 to String: grpc-encoding
int2: 6
byte2 to String: gzip
int1: 7
byte1 to String: grpc-accept-encoding
int2: 7
byte2 to String: identity,gzip
int1: 8
byte1 to String: grpc-timeout
int2: 8
byte2 to String: 16294679u

  1. hook io.grpc.l0.m方法打印出的并不是真正意义上的原始数据

public void l0.m(l0$g p0,Object p1)	//method@00a07a
{
   i.o(p0, "key");
   i.o(p1, "value");
   this.i();
   this.k(this.b, p0.a());
   this.o(this.b, p0.i(p1));
   this.b = this.b+1;
}

m方法将传入的第一个参数设为key,第二个参数设为value。在调用栈的角度,io.grpc.l0.m调用栈的倒数第二个方法,也就是z1.c.v.p.a.d.b.f.a.c方法调用了io.grpc.l0.m方法,传入的第二个参数为com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的方法的返回结果。

  1. 接下来用objection对com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的所有方法进行hook,点击视频,打印调用情况如下:
    (agent) [r205bm66lxf] Called com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.k()
    (agent) [r205bm66lxf] Called com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.l()
    (其实此类中其他很多方法都被调用,但objection却没回显)

5.接着选择一个com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的方法,如com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.l方法进行查看源码(在z1.c.v.p.a.d.b.f.a.c方法中可以知道com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.l方法的返回结果与x-bili-network-bin建成一对键值对):


public final static byte[] HeadersKt.l()	//method@008515
{
   byte[] bArray = Network.newBuilder().setType(a.a(e.e.q())).setTf(e.e.y()).setOid(e.e.r()).build().toByteArray();
   w.h(bArray, "Network.newBuilder\(\)\n   …\(\)\).build\(\).toByteArray\(\)");
   return bArray;
}

可以看到此方法关键调用了toByteArray函数,并将此函数返回结果作为返回结果。
toByteArray在com.google.protobuf.AbstractMessageLite类中:


public byte[] AbstractMessageLite.toByteArray()	//method@00916c
{
   try{	
      byte[] obyteArray = new byte[this.getSerializedSize()];
      CodedOutputStream cInstance = CodedOutputStream.newInstance(obyteArray);
      this.writeTo(cInstance);
      cInstance.checkNoSpaceLeft();
      return obyteArray;
   }catch(java.io.IOException e0){	
      throw new RuntimeException(v3.getSerializingExceptionMessage("byte array"), e0);
   }	
}

(百度CodedOutputStream得到:用谷歌protocolbuffers的CodedOutputStream,手写序列化。先计算序列化以后需要多少空间,然后new出这个byte[],然后往里填。这是protoc生成的代码所采取的体式格局)
很明显writeTo函数就是写入到CodedOutputStream中。writeTo函数为com.google.protobuf.MessageLite接口的一个抽象函数,此方法在此类中是多态的,很明显com.google.protobuf.AbstractMessageLite.toByteArray中调用的writeTo的原型在com.google.protobuf.MessageLite是:void writeTo(CodedOutputStream codedOutputStream) throws IOException;
com.google.protobuf.AbstractMessageLite类实现了com.google.protobuf.MessageLite接口,但是没实现原型为void writeTo(CodedOutputStream codedOutputStream) throws IOException;的writeTo函数。
jadx中搜索extends AbstractMessageLite,定位到com.google.protobuf.GeneratedMessageLite类,但是com.google.protobuf.GeneratedMessageLite类中并没有实现原型为void writeTo(CodedOutputStream codedOutputStream) throws IOException;的writeTo函数。
继续jadx搜索extends GeneratedMessageLite,此时搜索到的结果有1000+,先暂时放下搜索的结果。通过上面的com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.l方法的源码可以知道,需要调用com.google.protobuf.AbstractMessageLite.toByteArray进行转换的对象类是com.bapis.bilibili.metadata.network.Network,jadx中进入此类查看发现此类继承了com.google.protobuf.GeneratedMessageLite类,并且实现了原型为void writeTo(CodedOutputStream codedOutputStream) throws IOException;的writeTo函数。这时,开启objection,对此函数进行hook并打印调用栈,验证猜测:


(agent) [10xurkzysbhj] Backtrace:
        com.bapis.bilibili.metadata.network.Network.writeTo(Native Method)
        com.google.protobuf.AbstractMessageLite.toByteArray(BL:3)
        com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.l(BL:4)
        z1.c.v.p.a.d.b.f.a.c(BL:6)
        z1.c.v.p.a.d.b.f.a.b(BL:1)
        z1.c.v.p.a.d.b.f.a$a.e(BL:1)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.e.a$a.e(BL:3)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.f.b$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.c.a$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.b.b$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.d.a$a.e(BL:5)
        io.grpc.stub.ClientCalls.n(BL:1)
        io.grpc.stub.ClientCalls.f(BL:1)
        io.grpc.stub.ClientCalls.l(BL:2)
        io.grpc.stub.ClientCalls.i(BL:3)
        com.bilibili.lib.moss.internal.impl.failover.FailoverEngine.c(BL:5)
        com.bilibili.lib.moss.api.MossService.blockingUnaryCall(BL:1)
        com.bapis.bilibili.app.playurl.v1.PlayURLMoss.playView(BL:2)
        com.bilibili.lib.media.resolver.resolve.d.k.a(BL:15)
        com.bilibili.lib.media.resolver.resolve.d.h.d(BL:14)
        com.bilibili.lib.media.resolver.resolve.d.h.resolveMediaResource(BL:2)
        com.bilibili.lib.media.resolver.resolve.a.d(BL:10)
        com.bilibili.lib.media.resolver.resolve.MediaResolveProvider.l(BL:4)
        com.bilibili.lib.media.resolver.resolve.MediaResolveProvider.call(BL:6)
        android.content.ContentProvider$Transport.call(ContentProvider.java:401)
        android.content.ContentResolver.call(ContentResolver.java:1708)
        com.bilibili.lib.media.resolver.resolve.MediaResolveProvider.k(BL:3)
        com.bilibili.lib.media.resolver.resolve.a.e(BL:4)
        com.bilibili.lib.media.c.c.c.b.a(BL:3)
        com.bilibili.lib.media.c.b.e.a.a(BL:1)
        com.bilibili.lib.media.c.b.e.a.call(BL:1)
        java.util.concurrent.FutureTask.run(FutureTask.java:266)
        com.bilibili.lib.media.c.b.b.c$c.call(BL:1)
        java.util.concurrent.FutureTask.run(FutureTask.java:266)
        java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        java.lang.Thread.run(Thread.java:764)

很明显,com.google.protobuf.AbstractMessageLite.toByteArray中调用的writeTo确实在com.bapis.bilibili.metadata.network.Network类中。com.bapis.bilibili.metadata.network.Network.writeTo源码:


public void Network.writeTo(CodedOutputStream p0)	//method@00c82d
{
   if (this.type_ != NetworkType.NT_UNKNOWN.getNumber()) {	
      p0.writeEnum(1, this.type_);
   }	
   if (this.tf_ != TFType.TF_UNKNOWN.getNumber()) {	
      p0.writeEnum(2, this.tf_);
   }	
   if (!this.oid_.isEmpty()) {	
      p0.writeString(3, this.getOid());
   }	
   return;
}

源码中调用的writeEnum函数和writeString函数,
在com.google.protobuf.CodedOutputStream类中,
writeEnum函数原型是:public abstract void writeInt32(int i, int i2) throws IOException;
writeString函数原型是:public abstract void writeString(int i, String str) throws IOException;
最后会将构建x-bili-network-bin的原始数据写入到CodedOutputStream,原始数据在当前类(com.bapis.bilibili.metadata.network.Network)的三个变量中:oid_,tf_,type_ 。只需要frida hook com.bapis.bilibili.metadata.network.Network的writeTo函数(hook 打印数据时,runtime需要设置为v8),原始数据就是当前实例中的oid_,tf_,type_变量,直接打印就好了

至于com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类中的其他获取请求头x-bili-*-bin数据的分析方式跟上面大同小异:
(1)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.f –> x-bili-fawkes-req-bin
jadx中的java源码:


public final static byte[] HeadersKt.f()	//method@00850f
{
   byte[] bArray = e.e.i().toByteArray();
   w.h(bArray, "RuntimeHelper.fawkesReq\(\).toByteArray\(\)");
   return bArray;
}
//需要hook writeTo函数的类:com.bapis.bilibili.metadata.fawkes.FawkesReq

(2)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.h –> x-bili-device-bin (frida需要以spawn开启app,才能获取到x-bili-device-bin的原始数据)
jadx中的java源码:


public final static byte[] HeadersKt.h()	//method@008511
{
   return HeadersKt.b.getValue();
}
//需要hook writeTo函数的类:com.bapis.bilibili.metadata.device.Device

(3)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.j –> x-bili-locale-bin
jadx中的java源码:


public final static byte[] HeadersKt.j()	//method@008513
{
   byte[] bArray = e.e.n().toByteArray();
   w.h(bArray, "RuntimeHelper.locale\(\).toByteArray\(\)");
   return bArray;
}
//需要hook writeTo函数的类:com.bapis.bilibili.metadata.locale.Locale

(4) com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.k –> x-bili-metadata-bin
jadx中的java源码:


public final static byte[] HeadersKt.k()	//method@008514
{
   Metadata$Builder mBuilder = Metadata.newBuilder();
   String sa = e.e.a();
   String str = "";
   if (sa) {	
   }else {	
      sa = str;
   }	
   mBuilder = mBuilder.setAccessKey(sa).setMobiApp(e.e.o()).setDevice(e.e.h()).setBuild(e.e.c()).setChannel(e.e.e());
   if ((sa = e.e.d())) {	
      str = sa;
   }	
   byte[] bArray = mBuilder.setBuvid(str).setPlatform("android").build().toByteArray();
   w.h(bArray, "Metadata.newBuilder\(\)\n  …   .build\(\).toByteArray\(\)");
   return bArray;
}
//需要hook writeTo函数的类:com.bapis.bilibili.metadata.Metadata

(5) com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.l –> x-bili-network-bin (上面已做分析)
(6) com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.m –> x-bili-restriction-bin
jadx中的java源码:


public final static byte[] HeadersKt.m()	//method@008516
{
   byte[] bArray = e.e.v().toByteArray();
   w.h(bArray, "RuntimeHelper.restriction\(\).toByteArray\(\)");
   return bArray;
}
//需要hook writeTo函数的类:com.bapis.bilibili.metadata.restriction.Restriction
  1. com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的中其他方法:
    (1)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.a –> 返回x-bili-fawkes-req-bin加密后的字符串(与抓包的相关字段内容相同)

public final static String HeadersKt.a()	//method@00850a
{
   return d.a.b(HeadersKt.f());
}

(2)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.b –> 返回x-bili-locale-bin加密后的字符串(与抓包的相关字段内容相同)


public final static String HeadersKt.b()	//method@00850b
{
   return d.a.b(HeadersKt.j());
}

(3)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.c –> 返回x-bili-metadata-bin加密后的字符串(与抓包的相关字段内容相同)


public final static String HeadersKt.c()	//method@00850c
{
   return d.a.b(HeadersKt.k());
}

(4)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.d –> 返回x-bili-network-bin加密后的字符串(与抓包的相关字段内容相同)


public final static String HeadersKt.d()	//method@00850d
{
   return d.a.b(HeadersKt.l());
}

(5)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.e –> 返回x-bili-restriction-bin加密后的字符串(与抓包的相关字段内容相同)


public final static String HeadersKt.e()	//method@00850e
{
   return d.a.b(HeadersKt.m());
}

(6)com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.g –> 返回x-bili-device-bin加密后的字符串(与抓包的相关字段内容相同)


public final static String HeadersKt.g()	//method@008510
{
   return HeadersKt.c.getValue();
}

8.请求头中的authorization字段内容获取:
com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.i是获取请求头中authorization字段的数据的,前面在分析z1.c.v.p.a.d.b.f.a.c方法时可以看到是调用了com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.i方法的,但是用objection直接hook,发现没有(回显信息)调用。
此方法在jadx的java源码如下:


public final static String HeadersKt.i()	//method@008512
{
   String sa;
   if (!(sa = e.e.a()) || !(sa = new StringBuilder()+"identify_v1 "+sa)) {	
      sa = "";
   }	
   return sa;
}

阅读源码,当追到z1.c.v.p.b.e.a方法时,发现不知道此函数的返回调用了什么函数,
z1.c.v.p.b.e.a方法在jadx中java源码:


public final String e.a()	//method@00fe02
{
   e$a a;
   if (!(a = e.a)) {	
      w.O("delegate");
   }	
   return a.b();
}

z1.c.v.p.b.e类中有一个z1.c.v.p.b.ea接口的变量a。z1.c.v.p.b.ea接口的变量a。 z1.c.v.p.b.ea.b方法是一个抽象方法,无论在jadx中搜索还是在gda中搜索,实现了z1.c.v.p.b.e$a接口的类都很多。
这时可以打开objection,点击视频后,用wallbreaker寻找内存中z1.c.v.p.b.e类的实例,选择其中一个进行objectdump:


package z1.c.v.p.b

class e {
                                                                                                                 
        /* static fields */                                                                                      
        static e$a _a; => [0x2f72]: tv.danmaku.bili.utils.l0$a@6b6f09e
        static e _b; => [0x2f9a]: z1.c.v.p.b.e@bb7899a

        /* instance fields */

        /* constructor methods */
        void e();

        /* static methods */

        /* instance methods */
        String a();
        int b();
        int c();
        String d();
        String e();
        boolean f();
        boolean g();
        String h();
        FawkesReq i();
        e$a j();
        Map k();
        boolean l(String);
        String m(String);
        Locale n();
        String o();
        HttpDns p();
        int q();
        String r();
        void s(FawkesReply);
        Object t(String, Class);
        boolean u();
        Restriction v();
        g w(String, String);
        void x(e$a);
        TFType y();
        String z();
}

可以看到z1.c.v.p.b.e类中有一个z1.c.v.p.b.ea接口的变量a指向的是一个tv.danmaku.bili.utils.l0a接口的变量a 指向的是一个tv.danmaku.bili.utils.l0a类。
打开tv.danmaku.bili.utils.l0a类,找到实现了z1.c.v.p.b.ea类,找到实现了z1.c.v.p.b.ea接口的b方法:


public String l0$a.b()	//method@00f938
{
   e ei = e.i(this.applicatioa);
   w.h(ei, "BiliAccount.get\(app\)");
   return ei.j();
}

tv.danmaku.bili.utils.l0$a.b方法最终又调用了com.bilibili.lib.account.e类的j方法:


public String e.j()	//method@007d5a
{
   return this.c.r();
}

com.bilibili.lib.account.e类的j方法又调用了com.bilibili.lib.passport.c类的r方法:


public String c.r()	//method@0087c8
{
   a ap;
   String str = (!(ap = this.p()))? null : ap.strc;	
   return str;
}

当用账号登录bilibili的apk时,返回的就是com.bilibili.lib.passport.a类的string类型的c变量,这时可以打开objection,hook com.bilibili.lib.passport.c类的r方法,点击视频后:


(agent) [2fiubzig3lr] Called com.bilibili.lib.passport.c.r()
(agent) [2fiubzig3lr] Backtrace:
        com.bilibili.lib.passport.c.r(Native Method)
        com.bilibili.lib.account.e.j(BL:1)
        tv.danmaku.bili.utils.l0$a.b(BL:1)
        z1.c.v.p.b.e.a(BL:1)
        com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.i(BL:1)
        z1.c.v.p.a.d.c.e.a.intercept(BL:5)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.bilow.flowcontrol.a.intercept(BL:6)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        z1.c.d.c.h.c.intercept(BL:2)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        com.bilibili.lib.dblconfig.c.intercept(BL:17)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        com.bilibili.fd_service.unicom.b.a.a.intercept(BL:13)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.j.a.intercept(BL:5)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.j.b.g(BL:7)
        tv.danmaku.bili.z.j.b.intercept(BL:5)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.e.a(BL:1)
        tv.danmaku.bili.z.e.intercept(BL:11)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.b$b.intercept(BL:13)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        com.bilibili.lib.blconfig.ConfigManager$interceptor$2$a.intercept(BL:7)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.utils.o0.intercept(BL:3)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        okhttp3.y.f(BL:13)
        okhttp3.y.execute(BL:9)
        z1.c.v.p.a.d.c.c.b.c(BL:1)
        z1.c.v.p.a.d.c.b.c(BL:4)
        com.bilibili.lib.moss.api.MossService.blockingUnaryCall(BL:1)
        com.bapis.bilibili.community.service.dm.v1.DMMoss.dmView(BL:2)
        tv.danmaku.biliplayerv2.service.resolve.n.a.d(BL:11)
        tv.danmaku.biliplayerv2.service.resolve.n.a.b(BL:1)
        tv.danmaku.biliplayerv2.service.resolve.n.a.c(BL:1)
        tv.danmaku.biliplayerv2.service.resolve.b.p(BL:2)
        tv.danmaku.biliplayerv2.service.resolve.h$b.run(BL:2)
        java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        java.lang.Thread.run(Thread.java:764)
(agent) [2fiubzig3lr] Return Value: 6114ff5751g6hjty

这时再用wallbreaker搜索com.bilibili.lib.passport.a类,并用objectdump打印出实例出来:


package com.bilibili.lib.passport
class a {
                                                                                                                 
        /* static fields */                                                                                      

        /* instance fields */
        long _a; => 2592000
        long _b; => 33007031
        String _c; => 6114ff5751g6hjty
        String _d; => aa09b6900a8b19d06af3bd35fa7e70b1
        long e; => 1607630162

        /* constructor methods */
        void a();
        void a(long, String);

        /* static methods */

        /* instance methods */
        boolean a();
        String b();
        boolean c();
        boolean d();
        boolean equals(Object);
        int hashCode();
        String toString();

}

如果没有用账号登录,那么这里的string类型c变量就会是”NO_LOGIN_TOKEN_STRING_”。此时com.bilibili.lib.passport.a类的String _c;中的内容与抓包抓到的authorization的内容,只差一串字符串”identify_v1 “,这段字符串在前面出现过,在com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.i方法中出现,也就是加上这串字符串就和抓包的内容一样了。

  1. 分析完请求头中的x-bili--bin的原始数据和authorization字段后,现在开始对x-bili--bin加密后的数据进行跟踪。
    上面在分析com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类时,方法a,b,c,d,e,g返回的就是x-bili-*-bin加密后的字符串。
    这一点也通过了frida主动调用进行了验证。那么就尝试用objection去hook com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的方法a,b,c,d,e,g,但是却都没有任何回显信息。
    以com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.a方法为例,在jadx中的java源码:

public final static String HeadersKt.a()	//method@00850a
{
   return d.a.b(HeadersKt.f());
}

看源码,追到z1.c.v.p.b.d$a.b方法,在jadx中的java源码如下:


public final String d$a.b(byte[] p0)	//method@00aac7
{
   w.q(p0, "in");
   String se = b0.BaseEncoding.e(p0);
   w.h(se, "BASE64_ENCODING_OMIT_PADDING.encode\(`in`\)");
   return se;
}

阅读z1.c.v.p.b.d$a.b方法源码,发现是将com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.f方法返回的原始数据作为参数,调用了com.google.common.io.BaseEncoding.e方法,对数据进行加密,最后将com.google.common.io.BaseEncoding.e方法返回的加密字符串作为返回结果。
这时,打开objection,对com.google.common.io.BaseEncoding.e方法进行hook,则有:


(agent) [13w62ulc35wq] Arguments com.google.common.io.BaseEncoding.e([object Object])
(agent) [13w62ulc35wq] Return Value: EgdhbmRyb2lkIKT69AIqBGJpbGkyJVhZQjJERDdCNDAxREU2Qzg2Mjk4QjYwNDAwNUQwMDNGREIxMDI6B2FuZHJvaWQ              
(agent) [13w62ulc35wq] Called com.google.common.io.BaseEncoding.e([B)

(agent) [13w62ulc35wq] Arguments com.google.common.io.BaseEncoding.e([object Object])
(agent) [13w62ulc35wq] Return Value: CAEQpPr0AholWFlCMkREN0I0MDFERTZDODYyOThCNjA0MDA1RDAwM0ZEQjEwMiIHYW5kcm9pZCoHYW5kcm9pZDoEYmlsaUIHQW5kcm9pZEoPQU9TUCBvbiBtc204OTk2UgU4LjEuMA                                                   
(agent) [13w62ulc35wq] Called com.google.common.io.BaseEncoding.e([B)

(agent) [13w62ulc35wq] Arguments com.google.common.io.BaseEncoding.e([object Object])
(agent) [13w62ulc35wq] Return Value: CAE
(agent) [13w62ulc35wq] Called com.google.common.io.BaseEncoding.e([B)

(agent) [13w62ulc35wq] Arguments com.google.common.io.BaseEncoding.e([object Object])
(agent) [13w62ulc35wq] Return Value: (none)
(agent) [13w62ulc35wq] Called com.google.common.io.BaseEncoding.e([B)

(agent) [13w62ulc35wq] Arguments com.google.common.io.BaseEncoding.e([object Object])
(agent) [13w62ulc35wq] Return Value: Cg4KAnpoEgRIYW5zGgJDThIOCgJ6aBIESGFucxoCQ04
(agent) [13w62ulc35wq] Called com.google.common.io.BaseEncoding.e([B)

(agent) [13w62ulc35wq] Arguments com.google.common.io.BaseEncoding.e([object Object])
(agent) [13w62ulc35wq] Return Value: CgdhbmRyb2lkEgRwcm9k
(agent) [13w62ulc35wq] Called com.google.common.io.BaseEncoding.e([B)

可以看到返回值就是原始数据加密后的字符串,即抓包抓到的请求头中x-bili-*-bin中的内容。
能在其中找到调用com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.a方法的调用栈:


(agent) [p8ataly5v6f] Called com.google.common.io.BaseEncoding.e([B)
(agent) [p8ataly5v6f] Backtrace:
		com.google.common.io.BaseEncoding.e(Native Method)
        z1.c.v.p.b.d$a.b(BL:1)
        com.bilibili.lib.moss.internal.impl.common.header.HeadersKt.a(BL:1)
        z1.c.v.p.a.d.c.e.b.a.intercept(BL:3)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        z1.c.v.p.a.d.c.e.a.intercept(BL:12)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.bilow.flowcontrol.a.intercept(BL:6)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        z1.c.d.c.h.c.intercept(BL:2)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        com.bilibili.lib.dblconfig.c.intercept(BL:17)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        com.bilibili.fd_service.unicom.b.a.a.intercept(BL:13)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.j.a.intercept(BL:5)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.j.b.g(BL:7)
        tv.danmaku.bili.z.j.b.intercept(BL:5)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.e.a(BL:1)
        tv.danmaku.bili.z.e.intercept(BL:11)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.z.b$b.intercept(BL:13)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        com.bilibili.lib.blconfig.ConfigManager$interceptor$2$a.intercept(BL:7)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        tv.danmaku.bili.utils.o0.intercept(BL:3)
        okhttp3.g0.f.g.h(BL:9)
        okhttp3.g0.f.g.b(BL:1)
        okhttp3.y.f(BL:13)
        okhttp3.y.execute(BL:9)
        z1.c.v.p.a.d.c.c.b.c(BL:1)
        com.bilibili.lib.moss.internal.impl.failover.FailoverEngine$a$a.run(BL:1)
        java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        java.lang.Thread.run(Thread.java:764)

表明确实有用到 com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的a,b,c,d,e,g方法对x-bili-*-bin加密后的字符串进行获取。

  1. 除了看到含有com.bilibili.lib.moss.internal.impl.common.header.HeadersKt类的方法调用com.google.common.io.BaseEncoding.e的调用栈外,还看到了一个不一样的调用栈:

(agent) [p8ataly5v6f] Called com.google.common.io.BaseEncoding.e([B)
(agent) [p8ataly5v6f] Backtrace:
        com.google.common.io.BaseEncoding.e(Native Method)
        io.grpc.internal.a2.d(BL:8) 
        io.grpc.c1.c.P(BL:4)
        io.grpc.c1.c.J(BL:1)
        io.grpc.c1.c$c.g(BL:18)
        io.grpc.internal.a.l(BL:3)
        io.grpc.internal.d0.l(BL:1)
        io.grpc.internal.p0$f$a.l(BL:2)
        io.grpc.internal.n.C(BL:39)
        io.grpc.internal.n.e(BL:2)
        io.grpc.internal.l$f$a.e(BL:1)
        io.grpc.internal.k$d$a.e(BL:1)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.a$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.e.a$a.e(BL:3)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.f.b$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.c.a$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.b.b$a.e(BL:2)
        io.grpc.v.e(BL:1)
        z1.c.v.p.a.d.b.f.d.a$a.e(BL:5)
        io.grpc.stub.ClientCalls.n(BL:1)
        io.grpc.stub.ClientCalls.f(BL:1)
        io.grpc.stub.ClientCalls.g(BL:1)
        io.grpc.stub.ClientCalls.e(BL:1)
        com.bilibili.lib.moss.internal.impl.failover.FailoverEngine.d(BL:7)
        com.bilibili.lib.moss.api.MossService.asyncUnaryCall(BL:1)
        com.bapis.bilibili.account.fission.v1.FissionMoss.window(BL:1)
        tv.danmaku.bili.ui.main.usergrow.a$a.a(BL:2)
        tv.danmaku.bili.ui.main.usergrow.UserGrowManager.n(BL:6)
        tv.danmaku.bili.ui.main.usergrow.UserGrowManager.j(BL:8)
        tv.danmaku.bili.ui.main2.StartupFragmentV2.onStart(BL:5)
        androidx.fragment.app.Fragment.performStart(BL:5)
        androidx.fragment.app.FragmentManagerImpl.moveToState(BL:87)
        androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(BL:9)
        androidx.fragment.app.FragmentManagerImpl.moveToState(BL:166)
        androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(BL:2)
        androidx.fragment.app.FragmentManagerImpl.dispatchStart(BL:3)
        androidx.fragment.app.FragmentController.dispatchStart(BL:1)
        androidx.fragment.app.FragmentActivity.onStart(BL:9)
        androidx.appcompat.app.e.onStart(BL:1)
        com.bilibili.lib.ui.f.onStart(BL:1)
        tv.danmaku.bili.MainActivityV2.onStart(BL:2)
        android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1334)
        android.app.Activity.performStart(Activity.java:7019)
        android.app.Activity.performRestart(Activity.java:7094)
        android.app.Activity.performResume(Activity.java:7099)
        android.app.ActivityThread.performResumeActivity(ActivityThread.java:3755)
        android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3820)
        android.app.ActivityThread$H.handleMessage(ActivityThread.java:1779)
        android.os.Handler.dispatchMessage(Handler.java:106)
        android.os.Looper.loop(Looper.java:164)
        android.app.ActivityThread.main(ActivityThread.java:7009)
        java.lang.reflect.Method.invoke(Native Method)
        com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

按照此调用栈的从上往下顺序查看源码, io.grpc.internal.a2.d方法,调用了com.google.common.io.BaseEncoding.e。
上面使用了objection hook了com.google.common.io.BaseEncoding.e方法,但是传入的参数为[object Object],即无法打印出调用此方法传入的参数,这时写个frida js脚本去hook了com.google.common.io.BaseEncoding.e方法,打印出传入前和传入后的结果以及调用栈, 这里只贴出其中一组数据:


java.lang.Throwable
        at com.google.common.io.BaseEncoding.e(Native Method)
        at io.grpc.internal.a2.d(BL:8)
        at io.grpc.c1.c.P(BL:4)
        at io.grpc.c1.c.J(BL:1)
        at io.grpc.c1.c$c.g(BL:18)
        at io.grpc.internal.a.l(BL:3)
        at io.grpc.internal.d0.l(BL:1)
        at io.grpc.internal.p0$f$a.l(BL:2)
        at io.grpc.internal.n.C(BL:39)
        at io.grpc.internal.n.e(BL:2)
        at io.grpc.internal.l$f$a.e(BL:1)
        at io.grpc.internal.k$d$a.e(BL:1)
        at io.grpc.v.e(BL:1)
        at z1.c.v.p.a.d.b.f.a$a.e(BL:2)
        at io.grpc.v.e(BL:1)
        at z1.c.v.p.a.d.b.f.e.a$a.e(BL:3)
        at io.grpc.v.e(BL:1)
        at z1.c.v.p.a.d.b.f.f.b$a.e(BL:2)
        at io.grpc.v.e(BL:1)
        at z1.c.v.p.a.d.b.f.c.a$a.e(BL:2)
        at io.grpc.v.e(BL:1)
        at z1.c.v.p.a.d.b.f.b.b$a.e(BL:2)
        at io.grpc.v.e(BL:1)
        at z1.c.v.p.a.d.b.f.d.a$a.e(BL:5)
        at io.grpc.stub.ClientCalls.n(BL:1)
        at io.grpc.stub.ClientCalls.f(BL:1)
        at io.grpc.stub.ClientCalls.l(BL:2)
        at io.grpc.stub.ClientCalls.i(BL:3)
        at com.bilibili.lib.moss.internal.impl.failover.FailoverEngine.c(BL:5)
        at com.bilibili.lib.moss.api.MossService.blockingUnaryCall(BL:1)
        at com.bapis.bilibili.app.playurl.v1.PlayURLMoss.playView(BL:2)
        at com.bilibili.lib.media.resolver.resolve.d.k.a(BL:15)
        at com.bilibili.lib.media.resolver.resolve.d.h.d(BL:14)
        at com.bilibili.lib.media.resolver.resolve.d.h.resolveMediaResource(BL:2)
        at com.bilibili.lib.media.resolver.resolve.a.d(BL:10)
        at com.bilibili.lib.media.resolver.resolve.MediaResolveProvider.l(BL:4)
        at com.bilibili.lib.media.resolver.resolve.MediaResolveProvider.call(BL:6)
        at android.content.ContentProvider$Transport.call(ContentProvider.java:401)
        at android.content.ContentResolver.call(ContentResolver.java:1708)
        at com.bilibili.lib.media.resolver.resolve.MediaResolveProvider.k(BL:3)
        at com.bilibili.lib.media.resolver.resolve.a.e(BL:4)
        at com.bilibili.lib.media.c.c.c.b.a(BL:3)
        at com.bilibili.lib.media.c.b.e.a.a(BL:1)
        at com.bilibili.lib.media.c.b.e.a.call(BL:1)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at com.bilibili.lib.media.c.b.b.c$c.call(BL:1)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)

byte1: 

zhHansCN
zhHansCN
result: Cg4KAnpoEgRIYW5zGgJDThIOCgJ6aBIESGFucxoCQ04

查看io.grpc.internal.a2.d方法,在jadx中的java源码:


public static byte[][] a2.d(l0 p0)	//method@003240
{
   byte[][] bc;
   byte[] obyteArray;
   byte[] obyteArray1;
   if (!(bc = b0.c(p0))) {	
      bc = new byte[][0];
      return bc;
   }else {	
      int vint = 0;
      int vint1 = 0;
      while (vint < bc.length) {	
         obyteArray = bc[vint];
         obyteArray1 = bc[(vint+1)];
         if (a2.a(obyteArray, a2.b)) {	
            bc[vint1]=obyteArray;
            bc[(vint1+1)]=b0.BaseEncoding.e(obyteArray1).getBytes(b.Charset);
         }else if(a2.b(obyteArray1)){		
            bc[vint1]=obyteArray;
            bc[(vint1+1)]=obyteArray1;
         }else {	
            a2.loggera.warning(new StringBuilder().append("Metadata key=").append(new String(obyteArray, b.Charset)).append(", value=").append(Arrays.toString(obyteArray1)).append(" contains invalid ASCII characters").toString());
         label_006d :
            vint = vint+2;
         }	
         vint1 = vint1+2;
         goto label_006d ;	
      }	
      if (vint1 == bc.length) {	
         return bc;
      }	
      return Arrays.copyOfRange(bc, 0, vint1);
   }	
}

通过编写frida脚本,hook io.grpc.internal.a2.d方法,①打印调用此方法传入的l0Var参数,以及②打印用此参数主动调用io.grpc.b0.c方法返回的结果,还有③打印io.grpc.internal.a2.d方法最后的返回结果:


①
Metadata(x-bili-fawkes-req-bin=CgdhbmRyb2lkEgRwcm9k,x-bili-metadata-bin=CiA2MTE0ZmY1N2E2NTRiMjkyY2VkMWQwZTUyMmIyYTliMRIHYW5kcm9pZCCk+vQCKgRiaWxpMiVYWUIyREQ3QjQwMURFNkM4NjI5OEI2MDQwMDVEMDAzRkRCMTAyOgdhbmRyb2lk,authorization=identify_v1 6114ff57a654b292ced1d0e522b2a9b1,x-bili-device-bin=CAEQpPr0AholWFlCMkREN0I0MDFERTZDODYyOThCNjA0MDA1RDAwM0ZEQjEwMiIHYW5kcm9pZCoHYW5kcm9pZDoEYmlsaUIHQW5kcm9pZEoPQU9TUCBvbiBtc204OTk2UgU4LjEuMA,x-bili-network-bin=CAE,x-bili-restriction-bin=,x-bili-locale-bin=Cg4KAnpoEgRIYW5zGgJDThIOCgJ6aBIESGFucxoCQ04,grpc-encoding=gzip,grpc-accept-encoding=identity,gzip,grpc-timeout=17992885u)

②
b0_result[0]:x-bili-fawkes-req-bin
b0_result[1]:
androidprod
b0_result[2]:x-bili-metadata-bin
b0_result[3]:
 6114ff57a654b292ced1d0e522b2a9b1android ���*bili2%XYB2DD7B401DE6C86298B604005D003FDB102:android
b0_result[4]:authorization
b0_result[5]:identify_v1 6114ff57a654b292ced1d0e522b2a9b1
b0_result[6]:x-bili-device-bin
b0_result[7]���%XYB2DD7B401DE6C86298B604005D003FDB102"android*android:biliBAndroidJAOSP on msm8996R8.1.0
b0_result[8]:x-bili-network-bin
b0_result[9]:
b0_result[10]:x-bili-restriction-bin
b0_result[11]:
b0_result[12]:x-bili-locale-bin
b0_result[13]:

zhHansCN
zhHansCN
b0_result[14]:grpc-encoding
b0_result[15]:gzip
b0_result[16]:grpc-accept-encoding
b0_result[17]:identity,gzip
b0_result[18]:grpc-timeout
b0_result[19]:17979177u

③
result[0]:x-bili-fawkes-req-bin
result[1]:CgdhbmRyb2lkEgRwcm9k
result[2]:x-bili-metadata-bin
result[3]:CiA2MTE0ZmY1N2E2NTRiMjkyY2VkMWQwZTUyMmIyYTliMRIHYW5kcm9pZCCk+vQCKgRiaWxpMiVYWUIyREQ3QjQwMURFNkM4NjI5OEI2MDQwMDVEMDAzRkRCMTAyOgdhbmRyb2lk
result[4]:authorization
result[5]:identify_v1 6114ff57a654b292ced1d0e522b2a9b1
result[6]:x-bili-device-bin
result[7]:CAEQpPr0AholWFlCMkREN0I0MDFERTZDODYyOThCNjA0MDA1RDAwM0ZEQjEwMiIHYW5kcm9pZCoHYW5kcm9pZDoEYmlsaUIHQW5kcm9pZEoPQU9TUCBvbiBtc204OTk2UgU4LjEuMA
result[8]:x-bili-network-bin
result[9]:CAE
result[10]:x-bili-restriction-bin
result[11]:
result[12]:x-bili-locale-bin
result[13]:Cg4KAnpoEgRIYW5zGgJDThIOCgJ6aBIESGFucxoCQ04
result[14]:grpc-encoding
result[15]:gzip
result[16]:grpc-accept-encoding
result[17]:identity,gzip
result[18]:grpc-timeout
result[19]:17969931u

也就是说最终io.grpc.internal.a2.d方法返回的是抓包信息中请求头的一些数据,这些数据组成二维byte数组作为返回结果。回到com.google.common.io.BaseEncoding.e的调用栈,往下看是io.grpc.c1.c.P方法,
在gda中的java源码如下:


private void c.P(BidirectionalStream$Builder p0)	//method@003102
{
   String str;
   String str1;
   p0.a(l0.l0$g.c(), this.strf);
   p0.a(l0.l0$g.c(), "application/grpc");
   p0.a("te", "trailers");
   byte[][] bd = a2.d(this.i);
   int vint = 0;
   while (vint < bd.length) {	
      str = "UTF-8";
      str1 = new String(bd[vint], Charset.forName(str));
      if (c.O(str1)) {	
         p0.a(str1, new String(bd[(vint+1)], Charset.forName(str)));
      }	
      vint = vint+2;
   }	
}

到这里可以看到org.chromium.net.BidirectionalStreamBuilder类的a方法似乎是设置请求头信息的,并可以看到将io.grpc.internal.a2.d方法返回的二维byte数组的数据都作为参数来调用org.chromium.net.BidirectionalStreamBuilder类的a方法似乎是设置请求头信息的,并可以看到将io.grpc.internal.a2.d方法返回的二维byte数组的数据都作为参数来调用org.chromium.net.BidirectionalStreamBuilder类的a方法。
此类在jadx中的java源码为:


package org.chromium.net;

import java.nio.ByteBuffer;
import org.chromium.net.UrlResponseInfo;

/* compiled from: BL */
public abstract class BidirectionalStream {

    /* compiled from: BL */
    public static abstract class Builder {
        public abstract Builder a(String str, String str2);

        public abstract BidirectionalStream b();

        public abstract Builder c(boolean z);

        public abstract Builder d(String str);
    }

    /* compiled from: BL */
    public static abstract class Callback {
        public void a(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo) {
        }

        public abstract void b(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo, CronetException cronetException);

        public abstract void c(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo, ByteBuffer byteBuffer, boolean z);

        public abstract void d(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo);

        public void e(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo, UrlResponseInfo.HeaderBlock headerBlock) {
        }

        public abstract void f(BidirectionalStream bidirectionalStream);

        public abstract void g(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo);

        public abstract void h(BidirectionalStream bidirectionalStream, UrlResponseInfo urlResponseInfo, ByteBuffer byteBuffer, boolean z);
    }

    public abstract void a();

    public abstract void b();

    public abstract void c(ByteBuffer byteBuffer);

    public abstract void d();

    public abstract void e(ByteBuffer byteBuffer, boolean z);
}

既然这个a方法是一个抽象方法,那么一定有类实现它,才能被调用。jadx中搜索extends BidirectionalStream.Builder,定位到org.chromium.net.ExperimentalBidirectionalStream,
此类在jadx中的java源码为:


public abstract class ExperimentalBidirectionalStream extends BidirectionalStream {

    /* compiled from: BL */
    public static abstract class Builder extends BidirectionalStream.Builder {
        public /* bridge */ /* synthetic */ BidirectionalStream.Builder a(String str, String str2) {
            e(str, str2);
            return this;
        }

        public /* bridge */ /* synthetic */ BidirectionalStream.Builder c(boolean z) {
            h(z);
            return this;
        }

        public /* bridge */ /* synthetic */ BidirectionalStream.Builder d(String str) {
            i(str);
            return this;
        }

        public abstract Builder e(String str, String str2);

        public Builder f(Object obj) {
            return this;
        }

        /* renamed from: g */
        public abstract ExperimentalBidirectionalStream b();

        public abstract Builder h(boolean z);

        public abstract Builder i(String str);

        public Builder j(int i) {
            return this;
        }

        public Builder k(int i) {
            return this;
        }
    }
}

jadx中继续搜索extends ExperimentalBidirectionalStream.Builder,定位到org.chromium.net.impl.BidirectionalStreamBuilderImpl类,
抽象方法a的具体实现代码为:


public /* bridge */ /* synthetic */ BidirectionalStream.Builder a(String str, String str2) {
        l(str, str2);
        return this;
    }

可以看到此方法调用的当前类的l方法,l方法在gda中的java源码:


public BidirectionalStreamBuilderImpl BidirectionalStreamBuilderImpl.l(String p0,String p1)	//method@005339
{
   if (!p0) {	
      throw new NullPointerException("Invalid header name.");
   }	
   if (!p1) {	
      throw new NullPointerException("Invalid header value.");
   }	
   this.arrayListe.add(new AbstractMap$SimpleImmutableEntry(p0, p1));
   return this;
}

可以看到org.chromium.net.impl.BidirectionalStreamBuilderImpl.l方法,最终将传过来的数据,都放在了当前类(org.chromium.net.impl.BidirectionalStreamBuilderImpl)中的一个类型为ArrayList的变量_e中。
打开objection,点击视频,寻找org.chromium.net.impl.BidirectionalStreamBuilderImpl类的实例,打印其中一个,内容如下:


package org.chromium.net.impl

class BidirectionalStreamBuilderImpl {
                                                                                                                                                        
        /* static fields */                                                                                                                             

        /* instance fields */
        org.chromium.net.impl.CronetEngineBase _a; => [0x346a]: org.chromium.net.impl.CronetUrlRequestContext@e9db3f4
        java.lang.String _b; => https://grpc.biliapi.net:443/bilibili.main.community.reply.v1.Reply/MainList
        org.chromium.net.BidirectionalStream$Callback _c; => [0x3442]: io.grpc.c1.c$a@933ad92
        java.util.concurrent.Executor _d; => [0x3422]: MoreExecutors.directExecutor()
        java.util.ArrayList _e; => [0x341a]: [user-agent=Dalvik/2.1.0 (Linux; U; Android 8.1.0; AOSP on msm8996 Build/OPM1.171019.011) 6.11.0 os/android model/AOSP on msm8996 mobi_app/android build/6110500 channel/bili innerVer/6110500 osVer/8.1.0 network/2 grpc-java-cronet/1.21.0, content-type=application/grpc, te=trailers, x-bili-fawkes-req-bin=CgdhbmRyb2lkEgRwcm9k, x-bili-metadata-bin=EgdhbmRyb2lkIKT69AIqBGJpbGkyJVhZQjJERDdCNDAxREU2Qzg2Mjk4QjYwNDAwNUQwMDNGREIxMDI6B2FuZHJvaWQ, x-bili-device-bin=CAEQpPr0AholWFlCMkREN0I0MDFERTZDODYyOThCNjA0MDA1RDAwM0ZEQjEwMiIHYW5kcm9pZCoHYW5kcm9pZDoEYmlsaUIHQW5kcm9pZEoPQU9TUCBvbiBtc204OTk2UgU4LjEuMA, x-bili-network-bin=CAE, x-bili-restriction-bin=, x-bili-locale-bin=Cg4KAnpoEgRIYW5zGgJDThIOCgJ6aBIESGFucxoCQ04, grpc-encoding=gzip, grpc-accept-encoding=identity,gzip, grpc-timeout=17997488u]                                                                               
        java.lang.String _f; => POST
        int _g; => 3
        boolean _h; => true
        java.util.Collection _i; => [0x33e6]: [RpcExtra(tunnel=MOSS_CRONET, traceId=2d511adb-dc2c-4481-9d17-75899c5dac73, downgrade=false, persistent=false, sample=# com.bilibili.lib.rpc.track.model.g@73a844ae, logicalUrl=https://grpc.biliapi.net/bilibili.main.community.reply.v1.Reply/MainList)]        
        boolean _j; => false
        int _k; => 0
        boolean _l; => false
        int _m; => 0

        /* constructor methods */
        void BidirectionalStreamBuilderImpl(java.lang.String, org.chromium.net.BidirectionalStream$Callback, java.util.concurrent.Executor, org.chromium.net.impl.CronetEngineBase);                                                                                                                            

        /* static methods */

        /* instance methods */
        org.chromium.net.BidirectionalStream$Builder a(java.lang.String, java.lang.String);
        org.chromium.net.BidirectionalStream b();
        org.chromium.net.BidirectionalStream$Builder c(boolean);
        org.chromium.net.BidirectionalStream$Builder d(java.lang.String);
        org.chromium.net.ExperimentalBidirectionalStream$Builder e(java.lang.String, java.lang.String);
        org.chromium.net.ExperimentalBidirectionalStream$Builder f(java.lang.Object);
        org.chromium.net.ExperimentalBidirectionalStream g();
        org.chromium.net.ExperimentalBidirectionalStream$Builder h(boolean);
        org.chromium.net.ExperimentalBidirectionalStream$Builder i(java.lang.String);
        org.chromium.net.ExperimentalBidirectionalStream$Builder j(int);
        org.chromium.net.ExperimentalBidirectionalStream$Builder k(int);
        org.chromium.net.impl.BidirectionalStreamBuilderImpl l(java.lang.String, java.lang.String);
        org.chromium.net.impl.BidirectionalStreamBuilderImpl m(boolean);
        org.chromium.net.impl.BidirectionalStreamBuilderImpl n(java.lang.String);
}      

从打印出的org.chromium.net.impl.BidirectionalStreamBuilderImpl类的实例中,java.util.ArrayList _e变量存放的就是抓包抓到的请求头中的信息(除了method,authority,scheme,path字段及其内容)。
至此所知:
(1)加密x-bili--bin原始数据的是com.google.common.io.BaseEncoding.e方法
(2)获取加密后和加密前的x-bili-
-bin的数据的类是com.bilibili.lib.moss.internal.impl.common.header.HeadersKt
(3)设置加密后的请求头信息的 org.chromium.net.impl.BidirectionalStreamBuilderImpl.a方法
参考文章:https://blog.seeflower.dev/explore-bilibili-video-grpc-request/