protobuf流的反解析Message
0x01 protobuf的基本概念
protobuf通过定义".proto"文件来描述数据的结构。.proto文件中用"Message"所表示所需要序列化的数据的格式。Message由field组成,Field类似Java或者C++中成员变量,通常一个field的定义包含修饰符、类型、名称和ID。下面看一个简单的.proto文件的例子:
[plain] view plaincopy
- package testInfo;
- message Chats
- {
- optional string content1 = 1;
- required int32 content2 = 2;
- message EmbMsg
- {
- optional string a = 1;
- }
- repeated EmbMsg a = 3;
- }
[cpp] view plaincopy
- testInfo::Chats chatTest;
- chatTest.set_content1("hello");
- chatTest.set_content2(32);
- testInfo::Chats::EmbMsg *pMsg = chatTest.add_a();
- pMsg->set_a("aaaa");
- pMsg = chatTest.add_a();
- pMsg->set_a("bbb");
- std::string outputStr;
- chatTest.SerializeToString(&outputStr);
- printf("str %s", outputStr.c_str());
[plain] view plaincopy
- 0x00DE94C8 0a 05 68 65 6c 6c 6f 10 20 1a 06 0a 04 61 61 61 ..hello. ....aaa
- 0x00DE94D8 61 1a 05 0a 03 62 62 62
0x02 protobuf流的反解析
2.1 Varint编码
Protobuf的二进制使用Varint编码。Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。
Varint 中的每个 byte 的最高位 bit 有特殊的含义,如果该位为 1,表示后续的 byte 也是该数字的一部分,如果该位为 0,则结束。其他的 7 个 bit 都用来表示数字。因此小于 128 的数字都可以用一个 byte 表示。
例如:十六进制流里面其中两个字节:0x95 0x01,则其转换运算为:(0x95 & 0x7F) | (0x01 << 0x7) = 0x5 | 0x80 = 0x95。
若其中四个字节:0x9D 0xF4 0xC1 0xCB 0x05,则其转换运算为:
(0x9D & 0x7F) | (0xF4 & 0x7F)<<7 | (0xC1 & 0x7F)< = 1D | 3A00 | 104000 | 9600000 | 50000000 = 59707A1D 2.2 数值类型 Protobuf经序列化后以二进制数据流形式存储,这个数据流是一系列key-Value对。Key用来标识具体的Field,在解包的时候,Protobuf根据 Key 就可以知道相应的 Value 应该对应于消息中的哪一个 Field。 (field_number << 3) | wire_type Key由两部分组成。第一部分是 field_number,比如消息chatTest.content1中 的 field_number 为 1。第二部分为 wire_type。表示 Value 的传输类型。Wire Type 可能的类型如下表所示: required和optional不会有任何字节来表示这个修饰符。 repeated会存在相同的field_number。 05 -> 代表字符串长度05 20->value=0x20; 06->结构体长度06 0a->field_num=1,type=2; 04->字符串长度04 61 61 61 61 ->value="aaaa" 1a->field_num=3, type=2; 0a->field_num=1,type=2; 03->字符串长度03 61 61 61 61 ->value="bbb" 2.3 protoc 进行反序列化 上面的步骤是手动解析的过程,而利用google提供的工具可以帮助我们自动化的解析以上过程,在面对复杂的protobuf结构的时候能达到事半功倍的效果。按下面步骤来做: 首先配置java环境 其次安装jython,这里 然后编写Python脚本,protobuf.py 最后运行脚本 利用反序列化的结构来推测.proto的message的结构及每个字段的含义,就能达到protobuf流反解析的目的了。 上述方案是采用python调用protoc的命令,由于protobuf是开源的,是在牛逼可以看他的源码,看他怎么解析出来的,我看了下他的源码,由于太菜和项目时间比较紧,每太看明白,暂时采用上述方法。
Key 的定义如下:
type Meaning Used For 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum 1 64-bit fixed64, sfixed64, double 2 Length-delimi string, bytes, embedded messages, packed repeated fields 3 Start group Groups (deprecated) 4 End group Groups (deprecated) 5 32-bit fixed32, sfixed32, float
以上面生成的十六进制流我们可以开始分析
0A -> field_num=1, type=2;
-> "hello"
10->field_num=2, type=0;
1a->field_num=3, type=2;
05->结构体长度05
其中testprotobuf.bin是我们的protobuf流文件。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
