【python】【protobuf】逆向还原protobuf结构

文章介绍了如何从编码后的protobuf数据中还原proto结构文件,提出了三种方法,包括人工分析、使用protoc.exe和借助在线工具。通过示例展示了使用在线工具解析数据并修正结构的过程,最后给出了一个Pythondemo来解析编译后的protobuf数据。
摘要由CSDN通过智能技术生成

一、前言

很多场景都有一个需求:
得到了一个编码后的protobuf数据(比如竞品调研的的数据包),需要逆向还原其proto结构文件。
有3种方案去做这件事情:

  1. 从编码入手,人工肉眼分析。
  • 费时费力
  • 容易出错(在一些嵌套结构,或者有优化编码的地方)
  • 但,对理解 protobuf 编码原理非常有帮助
  1. 借助 protoc.exe
  • protoc.exe --decode_raw < f1.bin
  • 缺点:没有给出数据类型
# protoc.exe --decode_raw <f1.bin
1 {
  1 {
    1: "\004\032\224\354~m\350-?\266(qJ\264.0\031v\204I\364v,\001\341P\341\300\326\013!\351\014"
    2 {
      1892: "\000"
    }
  }
  2: "\001d\2457\370\332\362\335\345e0\231\212(\007\2275d\330\025\327\000pk\254\361\213\020.N<\010"
}
  1. 借助在线工具先把protobuf的基本结构识别出来,然后结合对数据的理解进行修正。
  • 为什么需要修正?
    因为有些字段刚好符合 protobuf 的编码规则,被错误识别为结构
  • 对比 protoc --decode_raw 功能,优势是:
    • 结构更加清晰
    • 会提供Field 的数据类型

二、示例

这里介绍第三种方案,借助在线工具 + 修正。

  1. 拿到了一段数据
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   0A 4D 0A 29 0A 21 04 1A  94 EC 7E 6D E8 2D 3F B6    M ) !  旍~m???
00000010   28 71 4A B4 2E 30 19 76  84 49 F4 76 2C 01 E1 50   (qJ?0 v処魐, 酨
00000020   E1 C0 D6 0B 21 E9 0C 12  04 A2 76 01 00 12 20 01   崂?!?       
00000030   64 A5 37 F8 DA F2 DD E5  65 30 99 8A 28 07 97 35   d?蜉錯0檴( ?
00000040   64 D8 15 D7 00 70 6B AC  F1 8B 10 2E 4E 3C 08      d??pk?.N< 
  1. 利用在线工具解析基本结构

Online Protobuf Decoder : https://protogen.marcgravell.com/decode

在这里插入图片描述
看到这个图,基本结构就可以还原出来了:

syntax = "proto2";

message Field1_1_2 {
	optional bytes f1 = 1892;
}

message Field1_1  {
	optional bytes f1 = 1;
	optional Field1_1_2 f2 = 2;
}

message Field1 {
	optional Field1_1 f1 = 1;
	optional bytes f2 = 2;
}

message Sample {
	optional Field1 f1 = 1;
}

这个结构,解析上面的数据是成功的
但是,可以注意一下,下面部分的定义不太常见,id比较特殊

message SampleField1_1_2 {
optional bytes f1 = 1892;
}

拿到了第二段数据,发现解码失败。

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   0A 78 0A 29 0A 21 04 3B  FC AF 46 BE E6 05 9B 88    x ) ! ;F炬 泩
00000010   D4 62 A6 96 AC 82 C7 EC  9D ED 96 BB BE 98 C9 18   詁瑐庆 頄痪樕 
00000020   BB 7C 75 15 BB C8 38 12  04 00 00 50 00 0A 29 0A   粅u 蝗8    P  ) 
00000030   21 04 09 AD 0C C5 B6 D8  E2 31 12 D9 86 7C 93 89   !  ?哦剽1 賳|搲
00000040   5D EB 4E 7C A8 87 E4 0F  F2 7C E2 31 5B 99 B3 CD   ]|▏?騶?[櫝?
00000050   B4 58 12 04 7C 2B 0D 00  12 20 4C 4C 90 8B 75 9A   碭  |+    LL 媢?
00000060   CD 3D 5C EC AD B7 6D 70  14 16 90 91 D2 09 DA A3   ?\飙穖p   懸 冢
00000070   F2 D6 53 FB 0A B4 46 E6  03 F2                     蛑S?碏??

再利用在线工具校验一下结构:
在这里插入图片描述
这时候发现跟第一次的结构确实有差异,差异在于:
在这里插入图片描述
对比可以发现,第二次的结构可以兼容第一次,而且解析出来的数据明显更合理,于是可以得到修正后的结构:

syntax = "proto2";

message Field1_1  {
	optional bytes f1 = 1;
	optional bytes f2 = 2;
}

message Field1 {
	optional Field1_1 f1 = 1;
	optional bytes f2 = 2;
}

message Sample {
	optional Field1 f1 = 1;
}

这样就完成了一次protobuf结构的还原与修订过程。

三、python demo

  1. 把以下结构保存为 sample.proto 文件:
syntax = "proto2";

message Field1_1  {
	optional bytes f1 = 1;
	optional bytes f2 = 2;
}

message Field1 {
	optional Field1_1 f1 = 1;
	optional bytes f2 = 2;
}

message Sample {
	optional Field1 f1 = 1;
}
  1. 利用 protoc.exe 编译 sample.proto, 得到 sample_pb2.py
protoc.exe --I=. --python_out=. sample.proto
  1. 引用
import sample_pb2

# f1.bin
chunk1 = file_chunk_keys_pb2.FileChunkKeys()
with open("f1.bin", "rb") as f:    
    chunk1.ParseFromString(f.read())
    
    print("#Field 1-1")
    for i in range(len(chunk1.f1.f1)):
        item1_1 = chunk1.f1.f1[i]
        print("chunk.f1.f1[", i, "]", ".f1 =", item1_1.f1.hex(' '))
        print("chunk.f1.f1[", i, "]", ".f2 =", item1_1.f2.hex(' '))

    print("")
    print("#Field 1-2")
    print("chunk.f1.f2 =", chunk1.f1.f2.hex(' '))

# f4.bin
chunk2 = file_chunk_keys_pb2.FileChunkKeys()
with open("f4.bin", "rb") as f:    
    chunk2.ParseFromString(f.read())
    
    print("#Field 1-1")
    for i in range(len(chunk2.f1.f1)):
        item1_1 = chunk2.f1.f1[i]
        print("chunk.f1.f1[", i, "]", ".f1 =", item1_1.f1.hex(' '))
        print("chunk.f1.f1[", i, "]", ".f2 =", item1_1.f2.hex(' '))

    print("")
    print("#Field 1-2")
    print("chunk.f1.f2 =", chunk2.f1.f2.hex(' '))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值