如何解码二进制/原始谷歌 protobuf 数据

Posted

技术标签:

【中文标题】如何解码二进制/原始谷歌 protobuf 数据【英文标题】:How to decode binary/raw google protobuf data 【发布时间】:2016-01-27 22:50:34 【问题描述】:

我有一个带有编码的 protobuf 数据的 coredump,我想解码这些数据并查看内容。我有在原始协议缓冲区中定义此消息的 .proto 文件。 我的 proto 文件如下所示:

$  cat my.proto 
message header 
  required uint32 u1 = 1;
  required uint32 u2 = 2;
  optional uint32 u3 = 3 [default=0];
  optional bool   b1 = 4 [default=true];
  optional string s1 = 5;
  optional uint32 u4 = 6;
  optional uint32 u5 = 7;
  optional string s2 = 9;
  optional string s3   = 10; 
  optional uint32 u6 = 8;

和protoc版本:

$  protoc --version
libprotoc 2.3.0

我尝试了以下方法:

    从核心转储原始数据

    (gdb) dump memory b.bin 0x7fd70db7e964 0x7fd70db7e96d

    将其传递给 protoc

    //proto file (my.proto) is in the current dir$ protoc --decode --proto_path=$pwd my.proto < b.binMissing value for flag: --decodeTo decode an unknown message, use --decode_raw.

    $ protoc --decode_raw < /tmp/b.binFailed to parse input.

关于如何解码它的任何想法?文档并没有详细说明如何去做。

编辑: 二进制格式的数据(10字节)

(gdb) x/10xb 0x7fd70db7e964
0x7fd70db7e964: 0x08    0xff    0xff    0x01    0x10    0x08    0x40    0xf7
0x7fd70db7e96c: 0xd4    0x38

【问题讨论】:

【参考方案1】:

您正确使用了--decode_raw,但您的输入似乎不是 protobuf。

对于--decode,需要指定类型名称,如:

protoc --decode header my.proto < b.bin

但是,如果--decode_raw 报告解析错误,--decode 也会报告。

您通过 gdb 提取的字节似乎不是有效的 protobuf。也许您的地址并不完全正确:如果您在任一端添加或删除了一个字节,它可能无法解析。

我注意到,根据你指定的地址,protobuf 只有 9 个字节长,只够设置三四个字段的空间。这是你所期待的吗?也许你可以在这里发布字节。

编辑:

您添加到问题中的 10 个字节似乎使用 --decode_raw 成功解码:

$ echo 08ffff01100840f7d438 | xxd -r -p | protoc --decode_raw
1: 32767
2: 8
8: 928375

交叉引用字段编号,我们得到:

u1: 32767
u2: 8
u6: 928375

【讨论】:

感谢您的回复,我在上面的问题中添加了原始字节(10 个字节)。是的,这里只会设置一些可选字段,所以这是意料之中的。 @brokenfoot:在我看来,您提供的字节实际上已成功解析——我通过回答进行了编辑以显示这一点。不知何故,b.bin 一定不能完全包含这些字节。您给出的dump memory 命令看起来只会转储 9 个字节。请记住,转储确实包含结束地址处的字节——它包含直到它之前的字节。 完美!我不知道转储内存命令不包括输出中的最后一个字节。非常感谢! 是否可以传递 protoc 十六进制而不是二进制? @Arya 否,但 unix 命令 xxd -r -p 将十六进制解码为二进制,因此您可以在我的回答中显示的管道中使用它。如果您没有从 unix 命令行运行 protoc,那么您将不得不想出一些其他的解决方案...【参考方案2】:

protoc --decode [message_name] [.proto_file_path] &lt; [binary_file_path],

在哪里

[message_name] 是 .proto 文件中消息对象的名称。如果消息在 .proto 文件的包中,请使用 package_name.message_name。 [.proto_file_path] 是定义消息的 .proto 文件的路径。 [binary_file_path] 是您要解码的文件的路径。

问题中的情况示例(假设my.protob.bin 在您当前的工作目录中):

protoc --decode header my.proto &lt; b.bin

【讨论】:

谢谢package_name.message_name 是我的关键! 我想知道my.proto是否有导入其他proto文件protoc如何找到它们?路径是什么??? @dahohu527,感觉替代品要么是你在命令行上的当前路径,要么是 .proto 文件的路径。也许你可以试试看它是哪一个:) 提到“引用”到“包名”.. 真的很有帮助。谢谢【参考方案3】:

原型文件:

syntax = "proto3";
package response;

// protoc --gofast_out=. response.proto

message Response 
  int64 UID        
  ....


use protoc:
protoc --decode=response.Response response.proto < response.bin
protoc --decode=[package].[Message type] proto.file < protobuf.response

【讨论】:

以上是关于如何解码二进制/原始谷歌 protobuf 数据的主要内容,如果未能解决你的问题,请参考以下文章

如何检查数据/有效载荷是否可以被protobuf解码

十一.Netty入门到超神系列-Netty使用Protobuf编码解码

解码没有模式的protobuf

Netty4.XUnity与Netty使用protoBuf

Protobuf协议实现原理

Go是如何实现protobuf的编解码的: 源码