爬虫逆向学习(十六):某方数据protobuf协议逆向解析
此分享只用于学习用途,不作商业用途,若有冒犯,请联系处理
某方数据protobuf协议逆向解析
- protobuf
- 站点信息
- 发包研究
- 反爬点定位
- 请求体protobuf解析
- 接口请求实现
- 总结
protobuf
承接上文什么是protobuf?在Python中如何应用?,前面我们了解什么是protobuf
以及它的相关应用,接下来我们来实战protobuf
协议逆向解析。当然,在研究该站点前我也是阅读了其它相关资料,谨在此以自己的理解记录下逆向过程。
站点信息
- 网站:
aHR0cHM6Ly9kLndhbmZhbmdkYXRhLmNvbS5jbi9wZXJpb2RpY2FsL3dseGIyMDIzMDEwMDE=
- 接口:
aHR0cHM6Ly9kLndhbmZhbmdkYXRhLmNvbS5jbi9EZXRhaWwuRGV0YWlsU2VydmljZS9nZXREZXRhaWxJbkZvcm1hdGlvbg==
发包研究
我们复制接口的CURL
包到postman
请求,可以成功,然后转成python-requests请求也能拿到数据,通过调参可以确定该站点只有protobuf
需要处理,没有其它明显的反爬。
其实看请求体和响应内容,如果业务对数据完整有太大要求,也可以不需要处理protobuf
,提取需要的数据即可,像请求体直接替换参数,其它保持不变也能拿到数据。
反爬点定位
老操作,添加xhr断点
,然后刷新页面,会在send
时断住
然后看到调用堆栈
,双击1
处,之所以操作这里,是因为它很明显就是跟目标接口相关的
然后在2
处加上断电,它也是跟目标接口相关的,而且大家看到serializeBinary
没,这在上篇文章我们就讲过了,它就是protobuf
的特征代码,所以这里大胆确定就是在这个JS文件进行的protobuf
数据序列化与反序列化。
刷新界面断住后跟进去
看到这里就确定入口了,接下来只要跟着找到结构数据即可
请求体protobuf解析
对比上面两张图,第一张是目标站点的,第二张是上篇文章中我们生成的JS文件,通过它我们可以确定其基本结构如下:
继续跟下去,这里可以看出DetailInfoRequest
定义了六个变量:
- 序号为1、类型为String的resourcetype
- 序号为2、类型为String的id
- 序号为3、类型为String的referer
- 序号为4、类型为String的md5id
- 序号为5、类型为String的transaction
- 序号为6、类型为Bool的isFetchAccountField
所以,proto文件结构如下:
这里我们需要验证一下结构是否准确,需要用到Fiddler抓包,它可以导出二进制文件,抓到包后,打开二进制数据,最下面黑色部分就是请求体的二进制数据
注意
:就如下图中的 00 00 00 00 1B
,这段校验和是不属于数据序列化后的字节,是后来加上去的,所以导出时不要选择这部分,不行会解析失败,然后右键选择红色框标注的操作然后保存为bin文件即可。
使用命令protoc.exe --decode_raw < post.bin
可以查看实际数据,这里说一下,吐过出现下图问题,是因为用的是PowerShell
窗口,需要使用cmd
打开的命令行窗口
看打印结果只需要用到
看打印结果只需要用到前两个参数
逆向时不需要完全按照代码的来,只要满足需求即可,最终的.proto文件
是
接口请求实现
-
将最终的.proto转成python代码
protoc.exe --python_out=. Information.proto
-
编写调用代码
import Information_pb2 as pb detail_body = pb.DetailInfoRequest() detail_body.resourcetype = 'Periodical' detail_body.id = 'wlxb202301001' with open('me.bin', mode="wb") as f: f.write(detail_body.SerializeToString()) print(detail_body.SerializeToString().decode())
-
bin文件对比
两者生成结果一致
-
请求接口
import requests import Information_pb2 as pb url = "url" detail_body = pb.DetailInfoRequest() detail_body.resourcetype = 'Periodical' detail_body.id = 'wlxb202301001' headers = { 'accept': '*/*', 'accept-language': 'zh-CN,zh;q=0.9', 'content-type': 'application/grpc-web+proto', 'origin': 'origin', 'referer': 'referer', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', 'x-grpc-web': '1', 'x-user-agent': 'grpc-web-javascript/0.1' } bytes_body = detail_body.SerializeToString() # 构造字节序列的头部 bytes_head = bytes([0, 0, 0, 0, len(bytes_body)]) print(bytes_head + bytes_body) resp = requests.post(url=url, data=bytes_head + bytes_body, headers=headers) print(resp.text)
对比后确定与实际结果一致,这里请求就构建成功了。
总结
暂时就到这里,还有响应结果需要处理,后续再讲解记录!!!