某方数据库protobuf详解

某方数据库protobuf详解

1. 什么是protobuf

Protobuf 是由 Google 开发的一种语言无关,平台无关,可扩展的序列化结构数据的方法,可用于通信和数据存储。提到 Protobuf 就不得不提到 序列化和反序列化 的概念。序列化和反序列化属于通信协议的一部分,它们位于 TCP/IP 四层模型中的应用层和 OSI 七层模型中的表示层。序列化是把应用层的对象转换为二进制串,反序列化是把二进制串转化成应用层的对象。这里详细分析请看 参考文章3

1.1 protobuf实现方法1:protoc

  1. 安装protoc

    • 点击下载
    • 解压后将 bin 目录路径添加到系统环境变量中
  2. 编译proto文件

    • proto文件示例

      proto 复制代码
      syntax = "proto3";
      
      // 定义消息类型
      message Person {
          string name = 1;        // 姓名
          int32 age = 2;          // 年龄
          string email = 3;       // 邮箱
          repeated string hobbies = 4;  // 爱好列表
          Address address = 5;    // 嵌套消息
      }
      
      // 定义嵌套消息
      message Address {
          string city = 1;
          string street = 2;
          int32 zipcode = 3;
      }
      
      // 定义响应消息
      message PersonResponse {
          int32 code = 1;
          string message = 2;
          Person data = 3;
      }
    • 编译proto文件

      cmd 复制代码
      protoc --python_out=. person.proto
  3. 在python中导入

    python 复制代码
    import person_pb2
    
    # ========== 序列化:创建对象并转换为字节 ==========
    print("=" * 50)
    print("1. 序列化示例")
    print("=" * 50)
    
    # 创建 Person 对象
    person = person_pb2.Person()
    person.name = "张三"
    person.age = 25
    person.email = "zhangsan@example.com"
    person.hobbies.extend(["读书", "游泳", "编程"])
    
    # 创建嵌套的 Address 对象
    person.address.city = "北京"
    person.address.street = "朝阳路123号"
    person.address.zipcode = 100000
    
    # 打印对象内容
    print("原始对象:")
    print(f"  姓名: {person.name}")
    print(f"  年龄: {person.age}")
    print(f"  邮箱: {person.email}")
    print(f"  爱好: {', '.join(person.hobbies)}")
    print(f"  地址: {person.address.city}{person.address.street}, {person.address.zipcode}")
    
    # 序列化为字节串
    serialized_data = person.SerializeToString()
    print(f"\n序列化后的字节串长度: {len(serialized_data)} 字节")
    print(f"字节串(前50字节): {serialized_data[:50]}")
    
    # 写入文件
    with open('person.bin', 'wb') as f:
        f.write(serialized_data)
    print("已写入 person.bin 文件")
    
    # ========== 反序列化:从字节恢复对象 ==========
    print("\n" + "=" * 50)
    print("2. 反序列化示例")
    print("=" * 50)
    
    # 创建新的空对象
    new_person = person_pb2.Person()
    
    # 从字节串解析
    new_person.ParseFromString(serialized_data)
    
    # 打印恢复后的对象
    print("反序列化后的对象:")
    print(f"  姓名: {new_person.name}")
    print(f"  年龄: {new_person.age}")
    print(f"  邮箱: {new_person.email}")
    print(f"  爱好: {', '.join(new_person.hobbies)}")
    print(f"  地址: {new_person.address.city}{new_person.address.street}, {new_person.address.zipcode}")
    
    # ========== 验证数据一致性 ==========
    print("\n" + "=" * 50)
    print("3. 数据一致性验证")
    print("=" * 50)
    
    assert person.name == new_person.name
    assert person.age == new_person.age
    assert person.email == new_person.email
    assert list(person.hobbies) == list(new_person.hobbies)
    assert person.address.city == new_person.address.city
    print("✓ 所有字段验证通过!")

1.2 protobuf实现方法2:blackboxprotobuf

  1. 安装blackboxprotobuf

    cmd 复制代码
    # 这里我记得有个问题,就是blackboxprotobuf与protoc的某个依赖包版本冲突,使用的时候可能会遇到
    pip install blackboxprotobuf
  2. 导入blackboxprotobuf

    python 复制代码
    import blackboxprotobuf
    import json
    # 反序列化
    with open(r"person.bin", "rb") as fp:
        data = fp.read()
        print(f"原始序列化后的字节串: {data}")
        print(f"原始序列化后的字节串长度: {len(data)} 字节")
    
    deserialize_data, message_type = blackboxprotobuf.protobuf_to_json(data)
    print(f"原始数据: {deserialize_data}")
    print(f"消息类型: {message_type}")  # 消息类型
    
    # 序列化,注意数据类型解析错误需要手动处理
    deserialize_data = json.loads(deserialize_data)
    deserialize_data["2"] = int(deserialize_data["2"])
    deserialize_data["5"]["3"] = int(deserialize_data["5"]["3"])
    serialized_data = blackboxprotobuf.encode_message(deserialize_data, message_type)
    print(f"序列化后的字节串: {serialized_data}")
    print(f"序列化后的字节串长度: {len(serialized_data)} 字节")
    
    
    # ========== 验证数据一致性 ==========
    print("\n" + "=" * 50)
    print("3. 数据一致性验证")
    print("=" * 50)
    
    assert serialized_data == data
    print("✓ 所有字段验证通过!")

2. 某方数据库protobuf实现

案例地址: aHR0cHM6Ly9zLndhbmZhbmdkYXRhLmNvbS5jbi9wYXBlcj9xPSVFNSU5QiVCNCVFNiVBMyU4Qg==

2.1 搜索参数生成

这里详细分析请看 参考文章1

  1. 定义proto文件

    • 调试找到序列化的代码,代码是逐层嵌套的,每个字段都有一个对应的proto类型,逐层跟进处理。

    • 根据原始参数,定义proto文件

      proto 复制代码
      syntax = "proto3";
      
      message SearchService {
          enum InterfaceType {
              A = 0;
          }
          enum SearchScope {
              B = 0;
          }
          enum SearchFilter {
              C = 0;
          }  
          enum Order {
              D = 0;
          }
          message SearchSort {
              string field = 1;
              Order order = 2;
          }
      
          message SecondsList {
              string Field = 1;
              string Value = 2;
          }
          message Commonrequest {
              string searchType = 1;
              string searchWord = 2;
              SearchSort searchSort = 3;
              repeated SecondsList secondslist = 4;
              int32 currentPage = 5;
              int32 pageSize = 6;
              SearchScope searchScope = 7;
              repeated SearchFilter searchFilter = 8;
              bool LanguageExpand = 9;
              bool TopicExpand = 10;
              bool semantic_retrieval = 11;
              string channel = 12;
              string module = 13;
          }
      
          message SearchRequest {
              Commonrequest commonrequest = 1; // 任意变量名
              InterfaceType interfaceType = 2; // 任意变量名
              repeated string optional_discovery_type = 4; // 任意变量名
          }
      }
  2. 编译proto文件

    cmd 复制代码
    protoc --python_out=. xx.proto

2.2 响应数据分析

  1. 方法1: 使用 blackboxprotobuf 解析响应数据编写proto文件

    • 根据 message_data 和 message_type 确定响应类型和内容,定义对应的proto文件。

      python 复制代码
      message_data, message_type = blackboxprotobuf.protobuf_to_json(response.content[5:])
      print("message_data:\n", message_data)
      print("message_type:\n", message_type)
  2. 方法2: 调试找到反序列化的代码,代码是逐层嵌套的,每个字段都有一个对应的proto类型,逐层跟进处理。

    • 这里详细分析请看 参考文章1,我是失败了,需要耐心调试和擅长 AST希望大佬路过多多指点😁
  3. 定义proto文件

    proto 复制代码
    // 我的想法是我并不需要定义完整的proto文件,我只需要通过方法1定义出我需要的字段即可,示例:
    syntax = "proto3";
    
    message SearchServiceResponse {
        repeated Item field_4 = 4;
        
        message Item {
            oneof content {
                Periodical periodical = 101;
                Patent patent = 119;
            }
        }
        
        message Periodical {
            string author_cn = 3;   // 中文作者名
            string author_en = 6;   // 英文作者名
        }
        
        message Patent {
            repeated string inventors = 5;  // 发明人列表
        }
    }

2.3 完整代码示例

python 复制代码
import xx_pb2 as pb #导入包
import xx_parse_pb2 as pb_test #导入包
from google.protobuf.json_format import MessageToJson
import requests
import blackboxprotobuf

SearchRequest= pb.SearchService.SearchRequest() #实例化对象S
#按上面解析数据,按照对应的属性设置值
#字符串,数字型的都是直接赋值
SearchRequest.commonrequest.searchType = 'periodical' 
SearchRequest.commonrequest.searchWord = '围棋'
SearchRequest.commonrequest.currentPage = 1
SearchRequest.commonrequest.pageSize = 10
SearchRequest.commonrequest.searchSort.field = "出版时间"
SearchRequest.commonrequest.searchSort.order = 1
SearchRequest.commonrequest.searchScope = 0
SearchRequest.commonrequest.channel = 'pc'
SearchRequest.commonrequest.module = 'search'
#repeated修饰的messsage类型和enum类型,则需要稍微多几个步骤
SearchRequest.interfaceType = 1
SearchRequest.optional_discovery_type.extend(['AI_READ', 'AI_EXTRACT'])

print(MessageToJson(SearchRequest))
form_data = SearchRequest.SerializeToString()
# print(form_data)

bytes_head = bytes([0, 0, 0, 0, len(form_data)])
data=bytes_head+form_data
# print(data)

headers = {
    "Accept": "*/*",
    "Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8",
    "Content-Type": "application/grpc-web+proto",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
}
url = "https://xxxx/SearchService.SearchService/search"
response=requests.post(url,headers=headers,data=bytes_head+form_data)
message_data, message_type = blackboxprotobuf.protobuf_to_json(response.content[5:])
print("message_data:\n", message_data)
print("message_type:\n", message_type)

3. 参考文章

  1. 某方数据平台的逆向分析-------学会逆向protobuf
  2. JS逆向案例-万方的protobuf逆向解析
  3. protobuf协议浅析
  4. 代码资源
相关推荐
weixin_5806140017 小时前
如何提取SQL日期中的年份_使用YEAR或EXTRACT函数
jvm·数据库·python
2301_8135995518 小时前
SQL生产环境规范_数据库使用最佳实践
jvm·数据库·python
李可以量化18 小时前
QMT 量化实战:用 Python 实现线性回归通道,精准识别趋势中的支撑与压力(下)
python·qmt·量化 qmt ptrade
a95114164218 小时前
Go 中通过 channel 传递切片时的数据竞争与深拷贝解决方案
jvm·数据库·python
Dxy123931021618 小时前
Python 使用正则表达式将多个空格替换为一个空格
开发语言·python·正则表达式
qq_1898070318 小时前
如何修改RAC数据库名_NID工具在集群环境下的改名步骤
jvm·数据库·python
zhangchaoxies18 小时前
如何检测SQL注入风险_利用模糊测试技术发现漏洞
jvm·数据库·python
Luca_kill19 小时前
MCP数据采集革命:从传统爬虫到智能代理的技术进化
爬虫·python·ai·数据采集·mcp·webscraping·集蜂云
zhangchaoxies19 小时前
CSS如何实现响应式弹性网格布局_配合media query修改flex-wrap属性
jvm·数据库·python
ZC跨境爬虫19 小时前
Scrapy分布式爬虫(单机模拟多节点):豆瓣Top250项目设置与数据流全解析
分布式·爬虫·python·scrapy