【万方数据】protobuf 逆向
1. 定位分析
可以看到 proto.SearchService.SearchRequest 是一个 Protocol Buffers (protobuf) 定义的对象。我们搜索的关键词,也就是请求体里面的“中药”在里面
- proto:通常表示这是一个 Protocol Buffers 的定义文件(.proto 文件)中的对象。
- SearchService:这是一个服务名称,定义在 .proto 文件中。服务定义通常包含一组 RPC(Remote Procedure Call)方法。
- SearchRequest:这是 SearchService 服务中的一个请求消息类型。它定义了客户端发送给服务端的数据结构。
2. 代码分析
e = (0, n.a)(e.getRequestMessage())
// 调用 n.a 方法处理 e.getRequestMessage() 的结果,得到一个新的字节数组
// 这里的e是长度为26的Uint8Arrayn = e.length
// 获取字节数组 e 的长度 这里等于26f = [0, 0, 0, 0]
// 初始化一个长度为 4 的数组 f,用于存储长度前缀。a = new Uint8Array(5 + n)
// 创建一个长度为 5 + n 的字节数组 a,其中前 5 个字节用于存储长度前缀。
// 这里的a是长度为31的Uint8Array数组,里面全是0// 然后f经过运算变为了[0, 0, 0, 26]
a.set(new Uint8Array(f), 1)
// 将 f 数组的内容复制到 a 的第 1 个位置开始的位置。
// 所以 a就变成了[0, 0, 0, 0, 26, 0, 0, ... , 0]a.set(e, 5)
// 将原始字节数组 e 的内容复制到 a 的第 5 个位置开始的位置
// 所以 a就变成了[0, 0, 0, 0, 26, 前面生成的e](e = a)
// 将 a 赋值给 e
这是前面的得到的26位Uint8Array
[10, 22, 10, 5, 112, 97, 112, 101, 114, 18, 6, 228, 184, 173, 232, 141, 175, 40, 1, 48, 20, 66, 1, 0, 16, 1]
这是请求体的hex
前面分析了前面4个都是0,第五个1A是其长度26
后面的hex字符串就是请求体参数
“0A 16 0A 05 70 61 70 65 72 12 06 E4 B8 AD E8 8D AF 28 01 30 14 42 01 00 10 01”
hex_str = "0A 16 0A 05 70 61 70 65 72 12 06 E4 B8 AD E8 8D AF 28 01 30 14 42 01 00 10 01"
e = [int(hex_value, 16) for hex_value in hex_str.split()]
print(e)
# 运行结果:[10, 22, 10, 5, 112, 97, 112, 101, 114, 18, 6, 228, 184, 173, 232, 141, 175, 40, 1, 48, 20, 66, 1, 0, 16, 1]
3. blackboxprotobuf解析
3.1 反序列化
import blackboxprotobuf# 给定的十六进制字符串
hex_string = "0A 16 0A 05 70 61 70 65 72 12 06 E4 B8 AD E8 8D AF 28 01 30 14 42 01 00 10 01"# 移除空格并将十六进制字符串转换为字节串
byte_string = bytes.fromhex(hex_string.replace(" ", ""))
print(byte_string)# 解析得到原始数据和消息类型
original_data, message_type = blackboxprotobuf.protobuf_to_json(byte_string)
print(original_data) # str类型
print(message_type) # dict类型
3.2 序列化
如何进行序列化呢?
import blackboxprotobuf# 给定的十六进制字符串
hex_string = "0A 16 0A 05 70 61 70 65 72 12 06 E4 B8 AD E8 8D AF 28 01 30 14 42 01 00 10 01"# 移除空格并将十六进制字符串转换为字节串
byte_string = bytes.fromhex(hex_string.replace(" ", ""))
print(byte_string)# 解析得到原始数据和消息类型
original_data, message_type = blackboxprotobuf.protobuf_to_json(byte_string)
print(original_data) # str类型
print(message_type) # dict类型# 序列化数据
form_data = bytes(blackboxprotobuf.encode_message(original_data, message_type))
print("序列化数据-->", form_data)
如果直接进行序列化,这样是会报错的,因为没有将数据和消息类型一一对应
{"1": {"1": "paper","2": "中药","5": "1","6": "20","8": "\u0000"},"2": "1"
}
这是反序列化之后的原始数据,其类型是str
{"1": {"type": "message","message_typedef": {"1": {"type": "bytes","name": ""},"2": {"type": "bytes","name": ""},"5": {"type": "int","name": ""},"6": {"type": "int","name": ""},"8": {"type": "bytes","name": ""}},"name": ""},"2": {"type": "int","name": ""}
}
这是其对应的消息类型
# 修改消息类型之后的数据
original_data = {"1": {"1": "paper","2": "中药","5": 1,"6": 20,"8": "\u0000"},"2": 1
}form_data = bytes(blackboxprotobuf.encode_message(original_data, message_type))
print("序列化数据-->", form_data)
print(len(form_data)) # 长度为26
4. 如何请求
import requests# 请求参数的十六进制字符串
hex_string = "00 00 00 00 1A 0A 16 0A 05 70 61 70 65 72 12 06 E4 B8 AD E8 8D AF 28 01 30 14 42 01 00 10 01"# 移除空格并将十六进制字符串转换为字节串
byte_string = bytes.fromhex(hex_string.replace(" ", ""))headers = {"content-type": "application/grpc-web+proto","user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
}url = "https://s.wanfangdata.com.cn/SearchService.SearchService/search"response = requests.post(url, data=byte_string, headers=headers)
print(response)
print(response.text)
得到的结果是序列化之后的数据
import blackboxprotobuf
import requests# 给定的十六进制字符串
hex_string = "0A 16 0A 05 70 61 70 65 72 12 06 E4 B8 AD E8 8D AF 28 01 30 14 42 01 00 10 01"# 移除空格并将十六进制字符串转换为字节串
byte_string = bytes.fromhex(hex_string.replace(" ", ""))
print(byte_string)# 解析得到原始数据和消息类型
original_data, message_type = blackboxprotobuf.protobuf_to_json(byte_string)
print(original_data) # str类型
print(message_type) # dict类型# 序列化数据
# 修改消息类型之后的数据
original_data = {"1": {"1": "paper","2": "中药","5": 1,"6": 20,"8": "\u0000"},"2": 1
}form_data = bytes(blackboxprotobuf.encode_message(original_data, message_type))
print("序列化数据-->", form_data) # b'\n\x16\n\x05paper\x12\x06\xe4\xb8\xad\xe8\x8d\xaf(\x010\x14B\x01\x00\x10\x01'
print(len(form_data))headers = {"content-type": "application/grpc-web+proto","user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
}url = "https://s.wanfangdata.com.cn/SearchService.SearchService/search"response = requests.post(url, data=bytes([0,0,0,0,len(form_data)]) + form_data, headers=headers)
print(response)# 反序列化响应结果
response_data, message_type = blackboxprotobuf.protobuf_to_json(response.content[5:])
print(response_data)