前言
有的网站请求参数或者URL是明文的,但是有些是加密后的。接下来以易车网为例,巩固一下逆向思路。
一、目标分析
使用接口获取汽车的参数配置,但是请求参数无法直接阅读,点击解码即可。
请求头里还有一下加密参数
估计重点就是X-Sign,其他的可能用处不大。
其实可以试试直接请求接口,看是否有返回值:
// https://mapi.yiche.com/web_api/car_model_api/api/v1/car/config_new_param?cid=508¶m=%7B%22cityId%22%3A%22201%22%2C%22carId%22%3A%22166210%22%7D { "message": "公共参数缺失", "status": "11036" }
所以重点来了,可能就是这个X-Sign起决定性因素。
二、逻辑分析
1. 定位函数获取X-Sign
只有两条记录,全部打上断点,进去哪个哪个就是。
看输出的值,有点像。
继续分析这句代码:
"headers" == e.encryptType && (n["x-sign"] = u(e, t));
执行规则:
-
只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值;
-
只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值;
总结:假前真后
所以有效的的值一定是u(e, t)。
接下来分别看一下e和t这两个参数:
// t { "cid": "508", "ver": "v10.80.0", "timestamp": 1700192890484, "gradeParam": {}, "uid": "", "headerEncryptKeys": [ { "name": "pc", "value": "19DDD1FBDFF065D3A4DA777D2D7A81EC", "cid": "508" }, { "name": "phone", "value": "DB2560A6EBC65F37A0484295CD4EDD25", "cid": "601" }, { "name": "h5", "value": "745DFB2027E8418384A1F2EF1B54C9F5", "cid": "601" }, { "name": "business_applet", "value": "64A1071F6C3C3CC68DABBF5A90669C0A", "cid": "601" }, { "name": "wechat", "value": "AF23B0A6EBC65F37A0484395CE4EDD2K", "cid": "601" }, { "name": "tencent", "value": "1615A9BDB0374D16AE9EBB3BBEE5353C", "cid": "750" } ], "paramsKey": "f48aa2d0-31e0-42a6-a7a0-64ba148262f0" }
然后是e:
这不太对啊,接口地址不是这个,那就继续执行,当URL是config_new_param再追踪。
果然有执行了几次才拿到了我们想要的结果。
也就是说如果开始找出了URL,那么后面的一切都是南辕北辙。
接下来就跳转到了s函数:
至此,我们找到了X-Sign的位置,之后使用JS来补环境就可以了吗?不要着急,执行一下没准能得到意想不到的结果。
好像恰巧获取的cid和param的值,而X-Sign就是加密后的密文。
2. 使用代码实现X-Sign
刚才我们获取了
cid=508¶m={"cityId":"201","carId":"166210"}19DDD1FBDFF065D3A4DA777D2D7A81EC1700201316661
这个值,把这个值传给h函数,并把相同作用域的函数全部复制过来就可以了。很顺利,一次就跑通了。
接下来思考一下如何获取这个参数变量。我们回到刚才的s函数,可以看到这个参数是由n提供的。
可以看到就是把n参数进行一个MD5的处理,这时候我们可以猜测一下是不是普通的MD5,可以用python或者js代码试试:
import hashlib content = 'cid=508¶m={"cityId":"201","carId":"166210"}19DDD1FBDFF065D3A4DA777D2D7A81EC1700211170720' md5_hash = hashlib.md5(content.encode()).hexdigest() print(md5_hash) # 输出 166cc7dd317a737794ecaa2bd795f37d
可以看到和结果中是一致的。这就简单很多了,大部分逻辑就分析完了。
3. 编写Python代码,进行调用
根据前面的分析,可以确定以下参数是需要传值的:
-
查询参数 {“cityId”:“201”,“carId”:“166210”}
-
时间戳,可以从Python传过去,也可以直接使用JS来生成
其他值可以是固定或者处理过的。
三、代码实现
全部代码如下,使用python将字符串进行MD5处理,然后发出请求。需要注意以下几个问题:
-
有些字段是必须存在的,比如X开头几个,因为前面的JS代码中可以看到有使用到,否则就会报错;
-
python的JSON数据和JS的JSON是有区别的,需要单独处理一下,否则校验失败。
以下代码是请求一种车型的方式,如果是批量请求,提前准备好多个ID就可以了。
import json
import time
import hashlib
import requests
def start(city_data):
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0",
"Content-Type": "application/json;charset=UTF-8",
"X-Platform": "pc",
}
ts = str(int(time.time() * 1000))
new_data = json.dumps(city_data, separators=(',', ':'))
s1 = f'cid=508¶m={new_data}19DDD1FBDFF065D3A4DA777D2D7A81EC' + ts
print(s1)
md5_hash = hashlib.md5(s1.encode()).hexdigest()
print(md5_hash)
headers['Referer'] = 'https://car.yiche.com/biyadifsuv/m166210/peizhi/'
headers['x-sign'] = md5_hash
headers['x-timestamp'] = ts
headers['x-city-id'] = "201"
headers['cid'] = "508"
params = {
"cid": "508",
"param": json.dumps(city_data, separators=(',', ':'))
}
print(params)
# 注意要使用separators,否则会出现签名验证失败的问题,使用后间隔更小,符合网站中JS的要求
# 'cid=508¶m={"cityId":"201","carId":"166210"}19DDD1FBDFF065D3A4DA777D2D7A81EC1700211170720',
# 否则会出现后面的两种情况
# {'cid': '508', 'param': '{"cityId": "201", "carId": "166210"}'}
# {'cid': '508', 'param': '{"cityId":"201","carId":"166210"}'}
res = requests.get(
'https://mapi.yiche.com/web_api/car_model_api/api/v1/car/config_new_param', params=params,
headers=headers,
)
print(res.json())
if __name__ == '__main__':
city_data = {
"cityId": "201", "carId": "166210"
}
start(city_data)
返回结果:
四、总结回顾
今天分析JS逻辑的时候在md5这一块卡了很久,主要原因如下;
-
起初看到进行MD5处理的时候怀疑了一下是不是基础的MD5,有没有进行特殊处理。为了图省事,直接问的GPT。结果这家伙输出了和控制台不一致的值,所以就当成JS特殊处理了。因此就把相关的函数单独拉出来进行处理,生成Sign值后传给python。但最后一直校验失败,百思不得其解。后来又多次反复查看使用Python生成MD5值后才发现了GPT的问题。但还是很奇怪为啥JS生成的MD5值不对呢,难到少复制什么函数或者变量了?
-
拼接参数的时候出现了Python和JS两个版本JSON不一致的问题,就是空格。
-
还发现个问题,在不同的浏览器上请求头存在大小写不一致的问题,但是并不影响结果。后来问了一下GPT,回答放在后面。
在HTTP协议中,请求头是不区分大小写的。这意味着,根据HTTP规范,请求头的字段名不应该被视为大小写敏感的。例如,Content-Type和content-type在HTTP的角度来看是相同的。
然而,实际上在某些情况下,服务器和浏览器的处理可能会导致大小写的差异。一些服务器可能会将请求头字段名视为大小写敏感,而另一些则可能不敏感。
此外,浏览器和其他HTTP客户端有时会以不同的方式处理请求头。有些浏览器可能会按照规范的方式处理,而另一些可能会将请求头字段名规范化为特定的形式。
为了确保在不同浏览器和服务器之间具有一致的行为,最好遵循HTTP规范,将请求头字段名视为不区分大小写。在编写代码时,最好使用规范的大小写形式。
因此,还是需要经验,多动手多动脑。
感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
工具都帮大家整理好了,安装就可直接上手!
三、最新Python学习笔记
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、Python视频合集
观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
五、实战案例
纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
六、面试宝典