包名:V1RJNWRFeHVXbTlaVjJocFkwTTFiMXB0VW1oaE1tYzk=
首先使用HttpCanary+TrustMeAlready模块抓包的同时使用算法助手选中算法分析这四个选项,抓包的同时进行算法分析
Screenshot_20240807-213824_Pixel 启动器.png (69.19 KB, 下载次数: 0)
下载附件
2024-8-9 21:17 上传
通过在HttpCanary中搜索过滤,可以找到首页和二级页面和更多页面的的Host,过滤后全部保存到本地,并且把算法助手分析的日志保存到本地,一同复制到电脑上分析。
1. 首页分析
首先打开首页的request.hcy文件,分析cipherTxt参数,也就是加密文本
2024.08.07_230952.png (61.24 KB, 下载次数: 0)
下载附件
2024-8-9 21:18 上传
在算法助手的日志里搜索nonceStr的值WQDS23,可以发现搜索到了加密内容、密钥和算法类型,为什么搜索这个值,因为直接搜索加密文本搜不到,多次请求可以发现,nonceStr这个参数是一个六位数的,包含字母大写和数字的随机数
2024.08.07_231253.png (64.94 KB, 下载次数: 0)
下载附件
2024-8-9 21:18 上传
再搜索base64格式的加密结果,又得到了一个md5加密的结果,但是需要注意的是,在做md5加密前,末尾加了一段字符串:BC56EAAB76C5492E,多次请求发现,这个字符串是固定的
2024.08.07_231439.png (45.92 KB, 下载次数: 0)
下载附件
2024-8-9 21:18 上传
再把Hex格式的加密结果到请求的request.hcy文件中搜索,发现在sign这个参数这里用到了这个值,其实真实的操作应该是搜索这个sign的值,发现这个值来源的数据和前面的加密结果很相似,来得知该值是由前面的加密结果+BC56EAAB76C5492E再计算md5得到的
2024.08.07_231531.png (67.55 KB, 下载次数: 0)
下载附件
2024-8-9 21:18 上传
然后就是timestamp参数:timestamp=1723037695328,明显是个时间戳
然后其他参数在多次请求后都保持不变,所以不用更改
2. 二级页面分析
打开request.hcy,对比首页的request.hcy文件,发现只多了一个seriesId参数,其他参数都一样,而这个参数就来自于首页请求返回的json数据中
2024.08.09_212259.png (66.01 KB, 下载次数: 0)
下载附件
2024-8-9 21:54 上传
2024.08.09_212420.png (59.63 KB, 下载次数: 0)
下载附件
2024-8-9 21:31 上传
3. 详细参数页面分析
同样打开request.hcy,对比首页的request.hcy文件,发现多了一段json数据,其他参数也都一样,而这段json数据中的id则来自于二级页面请求返回的json数据中
2024.08.09_212850.png (69.8 KB, 下载次数: 0)
下载附件
2024-8-9 21:31 上传
2024.08.09_213111.png (77.12 KB, 下载次数: 0)
下载附件
2024-8-9 21:31 上传
然后用python模拟请求进行批量获取数据,代码如下:
[Python] 纯文本查看 复制代码
import base64
import hashlib
import json
import os
import random
import string
import time
import uuid
from datetime import datetime
import requests
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
# Base64格式的公钥
base64_public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCOHifICBXyxzDSj9yOg9HzBMs/D0C0YS1ZrF95j6HQrrQu4zOzDyJc5hLgwcEmHE/A6k39phkSpeRqb9a+5AONCz6q0mP7z7xdzOwVXu7KZX+Ch0QU4NZutgi0IWwzCBAcOJ5+O2FxAj+O4z3Q45JtIlGWKNn+YPIixjxVsypN4QIDAQAB"
token = '202408051001001370193195139083'
def rsa_encrypt_long_data(plaintext):
# 解码公钥
public_key = serialization.load_der_public_key(base64.b64decode(base64_public_key))
# 计算chunk_size
key_size_in_bytes = public_key.key_size // 8 # 公钥大小,以字节为单位
chunk_size = key_size_in_bytes - 11
encrypted_chunks = []
for i in range(0, len(plaintext), chunk_size):
chunk = plaintext[i:i + chunk_size]
encrypted_chunk = public_key.encrypt(
chunk.encode(),
padding.PKCS1v15()
)
encrypted_chunks.append(encrypted_chunk)
encrypted_data = b''.join(encrypted_chunks)
return base64.b64encode(encrypted_data).decode()
# 获取以毫秒为单位的Unix时间戳
def get_milliseconds_timestamp():
timestamp = int(time.time() * 1000)
return timestamp
def calculate_md5_string(input_string):
# 创建一个MD5 hash对象
md5_hash = hashlib.md5()
# 更新hash对象
md5_hash.update(input_string.encode('utf-8'))
# 获取16进制表示的MD5值
md5_digest = md5_hash.hexdigest()
return md5_digest
def generate_random_string(length=6):
# 大写字母和数字的字符集
characters = string.ascii_uppercase + string.digits
# 生成指定长度的随机字符串
random_string = ''.join(random.choices(characters, k=length))
return random_string
def generate_time_based_session_id():
return str(uuid.uuid1())
def generate_session_id():
return str(uuid.uuid4())
def get_time_now():
# 获取当前时间
now = datetime.now()
# 将时间格式化为 'YYYY-MM-DD HH:MM:SS'
formatted_time = now.strftime('%Y-%m-%d %H:%M:%S')
return formatted_time
def requestAllCar(cipherTxt: str, nonceStr: str, sign: str, timestamp: int):
# 设置请求的URL
url = 'http://ipc.api.smallfeiyu.cn/aibg-car-compare-api/car/homeLoad.do'
# 设置请求的参数
params = {
'productId': 'productId=746abe7c-796c-4ebb-a6c9-b287bf503da4',
'vestId': '6dbfae2e-776e-47fd-872c-695dffc1935d',
'channel': 'vivo',
'osType': 'android',
'version': '8',
'cipherTxt': cipherTxt,
'nonceStr': nonceStr,
'sign': sign,
'timestamp': timestamp,
'token': token
}
data = {
"app": "com.vhahbp.hfdakh",
'sessionId': generate_session_id(),
"remoteIp": "47.111.241.44",
"remotePort": 80,
"time": get_time_now(),
}
# 设置请求头
headers = {
'Host': 'ipc.api.smallfeiyu.cn',
'Accept-Encoding': 'gzip',
'User-Agent': 'okhttp/4.9.3',
}
# 发送POST请求
response = requests.post(url, headers=headers, params=params, data=data)
return response
def requestCar(seriesId: str, cipherTxt: str, nonceStr: str, sign: str, timestamp: int):
# 设置请求的URL
url = 'http://ipc.api.smallfeiyu.cn/aibg-car-compare-api/car/car.do'
# 设置请求的参数
params = {
'seriesId': seriesId,
'productId': 'productId=746abe7c-796c-4ebb-a6c9-b287bf503da4',
'vestId': '6dbfae2e-776e-47fd-872c-695dffc1935d',
'channel': 'vivo',
'osType': 'android',
'version': '8',
'cipherTxt': cipherTxt,
'nonceStr': nonceStr,
'sign': sign,
'timestamp': timestamp,
'token': token
}
data = {
"app": "com.vhahbp.hfdakh",
'sessionId': generate_session_id(),
"remoteIp": "47.111.241.44",
"remotePort": 80,
"time": get_time_now(),
}
# 设置请求头
headers = {
'Host': 'ipc.api.smallfeiyu.cn',
'Accept-Encoding': 'gzip',
'User-Agent': 'okhttp/4.9.3',
}
# 发送POST请求
response = requests.post(url, headers=headers, params=params, data=data)
# 打印响应
return response
def requestDetailCar(request_body, cipherTxt: str, nonceStr: str, sign: str, timestamp: int):
# 设置请求的URL
url = 'http://ipc.api.smallfeiyu.cn/aibg-car-compare-api/car/carDetail.do'
# 设置请求的参数
params = {
'productId': 'productId=746abe7c-796c-4ebb-a6c9-b287bf503da4',
'vestId': '6dbfae2e-776e-47fd-872c-695dffc1935d',
'channel': 'vivo',
'osType': 'android',
'version': '8',
'cipherTxt': cipherTxt,
'nonceStr': nonceStr,
'sign': sign,
'timestamp': timestamp,
'token': token
}
# 设置请求头
headers = {
'Host': 'ipc.api.smallfeiyu.cn',
'Accept-Encoding': 'gzip',
'User-Agent': 'okhttp/4.9.3',
}
# 发送POST请求
response = requests.post(url, headers=headers, params=params, json=request_body)
# 打印响应
return response
def delay_s(s: int):
time.sleep(s)
if __name__ == '__main__':
timestamp = get_milliseconds_timestamp()
nonceStr = generate_random_string()
# 首页请求
plaintext1 = f'channel=vivo&nonceStr={nonceStr}&osType=android&productId=746abe7c-796c-4ebb-a6c9-b287bf503da4&sdkIntVersion=4056×tamp={timestamp}&token={token}&version=8&vestId=6dbfae2e-776e-47fd-872c-695dffc1935d&bizCodeAbc=79fa3d5f857cf66a&'
cipherTxt1 = rsa_encrypt_long_data(plaintext1)
sign = calculate_md5_string(cipherTxt1 + "BC56EAAB76C5492E")
response1 = requestAllCar(cipherTxt1, nonceStr, sign, timestamp)
if response1.status_code == 200:
file = os.path.join('汽车比价大全', '总览.json')
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, 'w', encoding='utf-8') as save_file:
save_file.write(response1.text)
print(f'{file} 已保存')
delay_s(5)
json_map = json.loads(response1.text)
for data in json_map['data']:
seriesId = data['id']
timestamp = get_milliseconds_timestamp()
nonceStr = generate_random_string()
plaintext = f'channel=vivo&nonceStr={nonceStr}&osType=android&productId=746abe7c-796c-4ebb-a6c9-b287bf503da4&sdkIntVersion=4056&seriesId={seriesId}×tamp={timestamp}&token={token}&version=8&vestId=6dbfae2e-776e-47fd-872c-695dffc1935d&bizCodeAbc=79fa3d5f857cf66a&'
cipherTxt = rsa_encrypt_long_data(plaintext)
sign = calculate_md5_string(cipherTxt + "BC56EAAB76C5492E")
response2 = requestCar(seriesId, cipherTxt, nonceStr, sign, timestamp)
file = os.path.join('汽车比价大全', '车辆信息', f'{seriesId}.json')
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, 'w', encoding='utf-8') as save_file:
save_file.write(response2.text)
print(f'{file} 已保存')
delay_s(5)
car_id_list = list()
if response2.status_code == 200:
detail_json = json.loads(response2.text)
for detail in detail_json['data']:
car_id_list.append(detail['id'])
car_id_map = {'carIds': car_id_list}
response3 = requestDetailCar(car_id_map, cipherTxt, nonceStr, sign, timestamp)
file = os.path.join('汽车比价大全', '详细参数', f'{seriesId}.json')
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, 'w', encoding='utf-8') as save_file:
save_file.write(response3.text)
print(f'{file} 已保存')
delay_s(5)