-
-
google protubuf 序列化分析
-
发表于: 2022-10-23 23:35 6240
-
# 前言
protubuf 支持多种语言跨平台的 协议序列化库
所以有很多公司用此库
发此文用于记录逆向心得,以及交流
Github:https://github.com/protocolbuffers/protobuf
docs:https://developers.google.com/protocol-buffers/docs/reference/overview
# 逆向
发包函数:
WSASend WSARecv
Send Recv
例子使用的是 WSASend WSARecv
在 WSASend 下段获取原始发包内容
如下:
发包长度:0x24
$ ==> 00 00 00 20 00 06 63 68 00 0C 00 03 33 17 1B DE ... ..ch....3..Þ $+10 48 BC 43 2A F1 CA 5C 91 0A A1 70 7D 34 B4 81 0F H¼C*ñÊ\..¡p}4´.. $+20 DE D8 23 3D 0C 01 B6 C3 DB E7 68 29 8C 27 4D 3E ÞØ#=..¶ÃÛçh).'M>
经过对比分析发现其中
00 00 00 20 包总体长度除去自身字节,当然这个是倒叙排列
00 06 包头长度 除去自身字节以及上面的4字节
头部分析
63 68 “ch” 特征字 无用
00 0C 00 03 特征字
头总共大小 0xC
剩下的都是Message主体部分
发包前他把消息主体进行了加密,经过解密如下
$ ==> 08 09 10 02 18 0D 20 1F 2A 06 0A 00 10 7B 18 01 ...... .*....{.. $+10 08 08 08 08 08 08 08 08 51 B8 3D 92 00 C2 00 80 ........Q¸=..Â..
Message 消息一共 0x18字节
我们使用 **protoc.exe --decode_raw < pbuf.bin**
根据解析内容编写 proto 文件
syntax = "proto2";//使用的语法 package bs.client_ms; message GoldLockReq { required string fld1 = 1; required int64 ilock_gold = 2; required int64 op_type = 3; } message GoldLockHeadReq { required int64 fld1 = 1; required int64 fld2 = 2; required int64 fld3 = 3; required int64 res_op_type = 4; required GoldLockReq GoldSuccor = 5; }
从结构对应上图,有的小盆友可能会问 8888
为什么没有成员?
因为经过分析 后面的为填充字节 (值为填充了多少字节),使用protubuf 解析将会失败,有时候会成功,有时候会失败
这个根据后面填充的字节数值是什么决定
编译"proto"文件
protoc.exe --python_out=. *.proto
得到同文件名.cpp .h 文件
复制到自己工程目录并引用他
编译 会发现一大堆报错
根源就在于
#include <google/protobuf/port_def.inc>
#include <google/protobuf/port_undef.inc>
引入了 .inc 文件
找到这两个文件,复制一份改为.h
并在引用的地方把后缀改为.h
即可
bs::client_ms::GoldLockHeadReq pResGoldLockHead = bs::client_ms::GoldLockHeadReq();//创建GoldLockHeadReq对象 if (pResGoldLockHead.ParseFromArray(newdata, nNewAlignSize))// 反序列化数组 { printf("fld1:%s lock gold:%lld op_type:%lld\n", pResGoldLockHead.goldsuccor().fld1().c_str() , pResGoldLockHead.goldsuccor().ilock_gold(), pResGoldLockHead.goldsuccor().op_type() ); pResGoldLockHead.mutable_goldsuccor()->set_ilock_gold(2222);//设置子结构成员 pResGoldLockHead.SerializeToArray(newdata, nCurrtenAlignSize);// 序列化到数组 }
以上代码编译成功,但测试无法解析,重点就在于后面填充的字节,也就是大小传入错误填充字节不属于 结构
怎么办呢?
同性交友走起 github
protodec:https://github.com/schdub/protodec
这是一个 proto 文件解析库,包含示例
我们引用重要文件 : protoraw.hpp
RawMessage msg;//创建对象 char* endbyssdd = &newdata[nCurrtenAlignSize];//结尾指针 bool Success = msg.parse((const unsigned char*)newdata, (const unsigned char*)endbyssdd);//解析
这里仍然会解析失败,但不重要,我们修改 parse 函数
添加成员:m_ErrorPos 标识错误位置
添加函数:GetErrorPos 获取错误位置
添加相同数据跳出
这样我们就知道错误位置在哪里以便于修正大小
那么又有小盆友问了
为什么不直接使用这个进行解析
关于这个问题,我的回答是他不如官方解析来的方便
当然你可以使用这个来进行解析
# 结尾
没有结尾,故事没有结束(屁来的故事啊!可恶)