Protobuf(基本使用和IDA中的protobuf解包逆向识别和重新利用)

2024-05-14 21:12

本文主要是介绍Protobuf(基本使用和IDA中的protobuf解包逆向识别和重新利用),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 安装
  • protobuf 基本使用
      • 如何使用
  • `.pb.cc`(实现文件)和`.pb.h`(头文件)
      • `.pb.h`(头文件)
      • `.pb.cc`(实现文件)
  • 生成的c和h文件
  • ida中解包
  • 打包解题
      • 1. 定义.proto文件
      • 2. 生成Python代码
      • 3. 使用生成的Python代码

安装

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格式。

如何使用

要使用这个消息类型,你需要按照以下步骤操作:

  1. 生成代码:使用protoc编译器,根据.proto文件生成相应语言的源代码。例如,如果你的项目是C++项目,你可以运行如下命令生成C++代码:
protoc --cpp_out=. Devicemsg.proto

当你执行这个正确的命令后,如果Devicemsg.proto文件定义了有效的protobuf消息格式,protoc编译器将会做以下事情:

  1. 解析: 首先,protoc会解析Devicemsg.proto文件中的内容。这个文件包含了你定义的所有消息类型及其字段、枚举类型等。

  2. 生成代码: 然后,它会根据这些定义自动生成C++源代码文件。通常,对于每个在.proto文件中定义的消息类型,它会生成一个对应的.pb.cc(实现文件)和.pb.h(头文件)。此外,还会为所有.proto文件共同生成一个google/protobuf/message.h等依赖所需的头文件。

  3. 输出位置: 由于你使用了.作为输出目录,生成的C++源代码文件会被放在当前目录下。如果没有特别指定输出目录,生成的文件名会基于.proto文件的名称,例如,如果Devicemsg.proto定义了一个DeviceMessage消息类型,你可能会得到Devicemsg.pb.ccDevicemsg.pb.h两个文件。

  4. 包含头文件:在你的C++源文件中,包含生成的头文件,如Devicemsg.pb.h

  5. 创建和操作消息对象:你可以像操作普通的类实例一样创建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,DevicemsgPROTOBUF_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.
#endiftypedef 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进行高效的结构化数据处理。

这篇关于Protobuf(基本使用和IDA中的protobuf解包逆向识别和重新利用)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/989866

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的