文章目录
安装
sudo apt install protobuf-c-compiler
sudo apt install protoc
protobuf 基本使用
这个protobuf消息定义(.proto
文件)定义了一个名为Devicemsg
的消息类型,使用的是proto2
语法。proto2
是Google的Protocol Buffers(Protobuf)序列化框架的一个版本,主要用于跨平台的数据交换。下面是Devicemsg
消息结构中每个字段的详细解释:
syntax = "proto2";
这一行指定了.proto文件使用的协议缓冲区语言版本为proto2。Protocol Buffers支持两种主要的语法版本:proto2和proto3,它们之间有一些行为和特性的差异。
message Devicemsg {
定义了一个名为Devicemsg
的消息类型。消息是一组命名的字段集合,用于封装数据结构。
required sint64 actionid = 1;
actionid
是一个必需的字段,类型为sint64
,标识号为1。在proto2中,required
关键字表示该字段在消息实例中必须被赋值。sint64
是一种变体整型,用于存储带符号的64位整数,使用变字节编码(Varint)以节省空间,尤其适合负数的高效存储。
required sint64 msgid = 2;
msgid
也是一个必需的sint64
类型字段,标识号为2,用于存储消息的唯一标识符或其他相关信息。
required sint64 msgsize = 3;
msgsize
字段同样是必需的sint64
类型,标识号为3,可能用于表示msgcontent
字段的字节大小,帮助接收方预先分配缓冲区。
required string msgcontent = 4;
msgcontent
是最后一个必需字段,类型为string
,标识号为4。在protobuf中,string
类型用来存储可变长度的文本数据,编码时使用UTF-8格式。
如何使用
要使用这个消息类型,你需要按照以下步骤操作:
- 生成代码:使用
protoc
编译器,根据.proto
文件生成相应语言的源代码。例如,如果你的项目是C++项目,你可以运行如下命令生成C++代码:
protoc --cpp_out=. Devicemsg.proto
当你执行这个正确的命令后,如果Devicemsg.proto
文件定义了有效的protobuf消息格式,protoc
编译器将会做以下事情:
-
解析: 首先,
protoc
会解析Devicemsg.proto
文件中的内容。这个文件包含了你定义的所有消息类型及其字段、枚举类型等。 -
生成代码: 然后,它会根据这些定义自动生成C++源代码文件。通常,对于每个在
.proto
文件中定义的消息类型,它会生成一个对应的.pb.cc
(实现文件)和.pb.h
(头文件)。此外,还会为所有.proto
文件共同生成一个google/protobuf/message.h
等依赖所需的头文件。 -
输出位置: 由于你使用了
.
作为输出目录,生成的C++源代码文件会被放在当前目录下。如果没有特别指定输出目录,生成的文件名会基于.proto
文件的名称,例如,如果Devicemsg.proto
定义了一个DeviceMessage
消息类型,你可能会得到Devicemsg.pb.cc
和Devicemsg.pb.h
两个文件。 -
包含头文件:在你的C++源文件中,包含生成的头文件,如
Devicemsg.pb.h
。 -
创建和操作消息对象:你可以像操作普通的类实例一样创建
Devicemsg
对象,设置其字段值,然后序列化或反序列化数据。#include "Devicemsg.pb.h" int main() { Devicemsg msg; msg.set_actionid(12345); msg.set_msgid(67890); msg.set_msgsize(123); // 假设msgcontent的长度 msg.set_msgcontent("Hello, this is the message content!"); // 序列化到字符串 std::string serialized; if (!msg.SerializeToString(&serialized)) { std::cerr << "Serialization failed." << std::endl; return -1; } // 反序列化(示例未展示) // ... }
通过这种方式,Devicemsg
消息可以方便地在网络间或进程间传递,保证了数据的一致性和高效性。
.pb.cc
(实现文件)和.pb.h
(头文件)
https://www.cnblogs.com/JmpCliff/articles/17595397.html
.pb.cc
(实现文件)和.pb.h
(头文件)是Google Protocol Buffers(protobuf)编译器生成的C++源代码文件,它们用于序列化和反序列化你定义的协议消息。下面详细介绍这两个文件通常包含的内容:
.pb.h
(头文件)
-
消息类型定义: 包含了你定义的消息类型的C++类声明。这个类通常继承自
google::protobuf::Message
。类中包含了你定义的所有字段的访问器(getter和setter),如set_field_name()
和field_name()
,以及一些其他用于操作消息实例的方法。 -
枚举类型: 如果你的
.proto
文件中定义了枚举类型,这些枚举也会被转换为C++的枚举,并在头文件中声明。 -
序列化与反序列化方法声明: 提供了将消息对象转换为字节流(序列化)和从字节流恢复消息对象(反序列化)的方法声明,如
SerializeToString()
、ParseFromString()
等。 -
默认实例: 可能会提供一个默认的静态消息实例,用于快速访问消息的默认值。
-
静态成员函数和变量: 用于消息类型的识别符(如
.descriptor()
)、大小计算等。
.pb.cc
(实现文件)
-
消息类型实现: 包含了上述头文件中声明的消息类的实现代码。这包括构造函数、析构函数、序列化和反序列化逻辑的具体实现,以及其他内部处理函数。
-
枚举类型的实现: 如果有的话,定义了枚举类型的成员和相关函数的实现。
-
描述符初始化: 包含了协议缓冲区描述符(
Descriptor
,EnumDescriptor
等)的创建和注册代码,这些描述符在运行时用于反射,允许动态地获取消息结构信息。 -
静态变量初始化: 实现文件还负责静态变量的初始化,这些变量通常与消息类型的元数据相关。
综上所述,.pb.h
文件提供了用户直接交互的接口,而.pb.cc
则实现了这些接口背后的具体逻辑,包括内存管理、数据转换等。这两者一起使得你能够在C++项目中轻松地使用protobuf定义的数据结构进行高效的序列化和反序列化操作。
生成的c和h文件
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
/* Generated from: devicemsg.proto */
/* Do not generate deprecated warnings for self */
#ifndef PROTOBUF_C__NO_DEPRECATED
#define PROTOBUF_C__NO_DEPRECATED
#endif
#include "devicemsg.pb-c.h"
void devicemsg__init
(Devicemsg *message)
{
static const Devicemsg init_value = DEVICEMSG__INIT;
*message = init_value;
}
size_t devicemsg__get_packed_size
(const Devicemsg *message)
{
assert(message->base.descriptor == &devicemsg__descriptor);
return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
}
size_t devicemsg__pack
(const Devicemsg *message,
uint8_t *out)
{
assert(message->base.descriptor == &devicemsg__descriptor);
return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
}
size_t devicemsg__pack_to_buffer
(const Devicemsg *message,
ProtobufCBuffer *buffer)
{
assert(message->base.descriptor == &devicemsg__descriptor);
return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
}
Devicemsg *
devicemsg__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data)
{
return (Devicemsg *)
protobuf_c_message_unpack (&devicemsg__descriptor,
allocator, len, data);
}
void devicemsg__free_unpacked
(Devicemsg *message,
ProtobufCAllocator *allocator)
{
if(!message)
return;
assert(message->base.descriptor == &devicemsg__descriptor);
protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
}
static const ProtobufCFieldDescriptor devicemsg__field_descriptors[4] =
{
{
"actionid",
1,
PROTOBUF_C_LABEL_REQUIRED,Devicemsg
PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */
offsetof(Devicemsg, actionid),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"msgidx",
2,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */
offsetof(Devicemsg, msgidx),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"msgsize",
3,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */
offsetof(Devicemsg, msgsize),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"msgcontent",
4,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_BYTES,
0, /* quantifier_offset */
offsetof(Devicemsg, msgcontent),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
static const unsigned devicemsg__field_indices_by_name[] = {
0, /* field[0] = actionid */
3, /* field[3] = msgcontent */
1, /* field[1] = msgidx */
2, /* field[2] = msgsize */
};
static const ProtobufCIntRange devicemsg__number_ranges[1 + 1] =
{
{ 1, 0 },
{ 0, 4 }
};
const ProtobufCMessageDescriptor devicemsg__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"devicemsg",
"Devicemsg",
"Devicemsg",
"",
sizeof(Devicemsg),
4,
devicemsg__field_descriptors,
devicemsg__field_indices_by_name,
1, devicemsg__number_ranges,
(ProtobufCMessageInit) devicemsg__init,
NULL,NULL,NULL /* reserved[123] */
};
devicemsg__pack中用到devicemsg__descriptor 的字段即可还原出来message
重点关注
magic,一般为0x28AAEEF9
message结构名字
n_fields,关系到原始的message结构内有几条记录、
fields,这个指向message内所有记录类型组成的一个数组,可以借此逆向分析message结构。
const ProtobufCMessageDescriptor devicemsg__descriptor =
{
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, magic, 一般为0x28AAEEF9
"devicemsg", message结构名字
"Devicemsg",
"Devicemsg",
"",
sizeof(Devicemsg),
4, n_fields,关系到原始的message结构内有几条记录、
devicemsg__field_descriptors, fields, 这个指向message内所有记录类型组成的一个数组,可以借此逆向分析message结构。
devicemsg__field_indices_by_name,
1, devicemsg__number_ranges,
(ProtobufCMessageInit) devicemsg__init,
NULL,NULL,NULL /* reserved[123] */
};
重点关注
name,名字,变量名
id,序号,即在message结构体中的顺序(等价于位置)
label,前面标记的required等
type,数据类型,string还是int64等
label和type都是枚举类型,占4个字节。可以查,但一般名字就能看出来了
static const ProtobufCFieldDescriptor devicemsg__field_descriptors[4] =
{
{
"actionid", name,名字,变量名
1, id,序号,即在message结构体中的顺序(等价于位置)
PROTOBUF_C_LABEL_REQUIRED,Devicemsg label,前面标记的required等
PROTOBUF_C_TYPE_SINT64, type,数据类型,string还是int64等
0, /* quantifier_offset */
offsetof(Devicemsg, actionid),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"msgidx",
2,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */
offsetof(Devicemsg, msgidx),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"msgsize",
3,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */
offsetof(Devicemsg, msgsize),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
{
"msgcontent",
4,
PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_BYTES,
0, /* quantifier_offset */
offsetof(Devicemsg, msgcontent),
NULL,
NULL,
0, /* flags */
0,NULL,NULL /* reserved1,reserved2, etc */
},
};
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
/* Generated from: devicemsg.proto */
#ifndef PROTOBUF_C_devicemsg_2eproto__INCLUDED
#define PROTOBUF_C_devicemsg_2eproto__INCLUDED
#include <protobuf-c/protobuf-c.h>
PROTOBUF_C__BEGIN_DECLS
#if PROTOBUF_C_VERSION_NUMBER < 1000000
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
#elif 1004001 < PROTOBUF_C_MIN_COMPILER_VERSION
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
#endif
typedef struct Devicemsg Devicemsg;
/* --- enums --- */
/* --- messages --- */
struct Devicemsg
{
ProtobufCMessage base;
int64_t actionid;
int64_t msgidx;
int64_t msgsize;
ProtobufCBinaryData msgcontent;
};
#define DEVICEMSG__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&devicemsg__descriptor) \
, 0, 0, 0, {0,NULL} }
/* Devicemsg methods */
void devicemsg__init
(Devicemsg *message);
size_t devicemsg__get_packed_size
(const Devicemsg *message);
size_t devicemsg__pack
(const Devicemsg *message,
uint8_t *out);
size_t devicemsg__pack_to_buffer
(const Devicemsg *message,
ProtobufCBuffer *buffer);
Devicemsg *
devicemsg__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void devicemsg__free_unpacked
(Devicemsg *message,
ProtobufCAllocator *allocator);
/* --- per-message closures --- */
typedef void (*Devicemsg_Closure)
(const Devicemsg *message,
void *closure_data);
/* --- services --- */
/* --- descriptors --- */
extern const ProtobufCMessageDescriptor devicemsg__descriptor;
PROTOBUF_C__END_DECLS
#endif /* PROTOBUF_C_devicemsg_2eproto__INCLUDED */
ida中解包
func(*(v4 + 3), *(v4 + 4), *(v4 + 5), *(v4 + 6), *(v4 + 7));
v4是解包后返回的message的地址,这里有两个问题:
为什么是从+3开始?(v4为QWORD指针)
为什么从v4里面拉出来了五个参数?
struct Devicemsg
{
ProtobufCMessage base; //占24个字节,固定数据存放关键信息
int64_t actionid;
int64_t msgidx;
int64_t msgsize;
ProtobufCBinaryData msgcontent;
};
struct ProtobufCMessage {
/** The descriptor for this message type. */
const ProtobufCMessageDescriptor *descriptor;
/** The number of elements in `unknown_fields`. */
unsigned n_unknown_fields; 8个字节
/** The fields that weren't recognized by the parser. */
ProtobufCMessageUnknownField *unknown_fields;
};
struct ProtobufCBinaryData {
size_t len; /**< Number of bytes in the `data` field. */ 8个字节
uint8_t *data; /**< Data bytes. */ 被当做8个字节
};
前十六个字节给了base,用来存放一些关键信息
bytes类型,转化为c语言结构时会变成一个结构体,里面存放长度和内容指针。IDA由于没有内置相关结构信息,将其当做八字节数组进行解析,因此会产生一个有5个记录的错觉,实际上后两个参数是同一个记录内置的两条记录。
打包解题
protoc --python_out=. devicemsg.proto
之后在exp中将其import进去,利用其中的devicemsg()函数(取决于定义的message的名字)创建类型,利用SerializeToString()函数生成字节流。
1. 定义.proto文件
创建一个.proto
文件来定义你的数据结构。例如,创建一个person.proto
文件:
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
message AddressBook {
repeated Person people = 1;
}
2. 生成Python代码
使用protoc
编译器根据.proto
文件生成Python代码:
protoc --python_out=. person.proto
这将在当前目录下生成一个person_pb2.py
文件,包含了Python类,用于序列化和反序列化protobuf消息。
3. 使用生成的Python代码
现在可以在Python程序中使用这些类来序列化和反序列化数据了:
import person_pb2
# 创建一个Person对象
person = person_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
# 序列化到字节串
data = person.SerializeToString()
# 反序列化回Person对象
new_person = person_pb2.Person()
new_person.ParseFromString(data)
print("Name:", new_person.name)
print("Id:", new_person.id)
print("Email:", new_person.email)
通过这种方式,你可以方便地在Python应用中使用protobuf进行高效的结构化数据处理。