目标:中国空气质量在线监测分析平台|城市分析 参考CSDN中文章,记录一下学习过程
通过切换城市,页面数据是通过 Ajax 加载的,数据接口:https://www.aqistudy.cn/apinew/aqistudyapi.php
请求的POST Data、返回的数据都被加密了
分析网站接口的加密逻辑
js加密的一般处理思路:
- 加密url或某个post data参数时,可以全局搜索对应的参数名或者值,确定js函数
- 整个post data加密或无法搜索到参数,可通过元素的事件监听,确定js函数
1. 这里搜索参数名 d 显然不现实,当我们点击切换城市或者搜索按钮之后,后台便会发出 Ajax 请求,说明这个点击动作是被监听的,所以我们可以找一下这个点击事件对应的处理代码在哪里,这里可以借助于 Firefox 来实现,它可以分析页面某个元素的绑定事件以及定位到具体的代码在哪一行
切换城市or点击搜索
2.发现绑定的 click 事件调用了 getData() 函数,显然是获取数据的js,搜索这个函数
在 city_detail.html 的第 463 行就找到了这个函数的声明
3.发现它又调用了 getAQIData() 和 getWeatherData() ,这两个方法的声明就紧挨在下面,再进一步分析发现这两个方法都调用了 getServerData() 这个方法,并传递了参数 method、param ,还有一个回调函数很明显是对返回数据进行处理的,这说明 Ajax 请求就是由这个 getServerData() 方法发起的,如图所示:
4.继续搜索这个函数 getServerData()
city_detail.html 中的是上面两个方法调用了 getServerData() ,这个文件里面并没有getServerData() 的声明
貌似在 https://www.aqistudy.cn/js/jquery-1.8.0.min.js?v=1.2 中 右键 选择在Sources中打开,继续搜
果然找到了,发现这里经过 JavaScript 混淆加密了,这个方法名后面怎么直接跟了一些奇怪的字符串,而且不符合一般的 JavaScript 写法,而且这行代码的开头为eval(function(p,a,c,k,e,d)这种形式。
5.利用在线反混淆网站(http://www.bm8.com.cn/jsConfusion/)将 jquery-1.8.0.min.js 中这行 eval 开头的混淆后的 JavaScript 代码复制一下,然后粘贴到这个网站中进行反混淆,就可以看到正常的 JavaScript 代码了,搜索一下就可以找到 getServerData() 方法了,可以看到这个方法确实发出了一个 Ajax 请求,请求了Ajax 加载的数据接口:
在这里发现了一个getParam()方法:它接受了 method 和 object 参数,然后返回得到的 param 结果就作为加密后的 POST Data,也就是d:后面的那一堆字符串
这里面还有一个decodeData()方法:对服务器传回的数据进行解密
6.还是在这一堆反混淆代码中,紧挨着上面代码找到了getParam()和decodeData()
其中 POST Data 的加密过程是 Base64 + AES 加密,Response Data 的解密是 AES + DES + Base64 解密
代码
PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库
pip install PyExecJS
还需安装JS运行环境(推荐安装 Node.js )
Go~
不需要用 Python 重写一遍 JavaScript,直接用 Python 来模拟运行 JavaScript 就好
将刚才反混淆的 JavaScript 保存成一个文件,叫做 encryption.js,然后用 PyExecJS 模拟运行相关的方法即可。
首先我们来实现加密过程,这里 getServerData() 方法其实已经帮我们实现好了,并实现了 Ajax 请求,但这个方法里面有获取 Storage 的方法,Node.js 不适用,所以这里不用它也别管他, 添加一个 getEncryptedData() 方法实现加密,在 encryption.js 最后面加入一段代码,实现如下方法:
function getEncryptedData(method, city, type, startTime, endTime) {var param = {};param.city = city;param.type = type;param.startTime = startTime;param.endTime = endTime;return getParam(method, param);
}
用于传递数据,调用参数加密函数
接下来用python模拟执行这些方法即可:
import execjs
import json
import requests# Init environment
# 通过 execjs(即 PyExecJS)的 get() 方法声明一个运行环境
node = execjs.get()# Params
# 换成'GETCITYWEATHER'获取温度,风向等数据
method = 'GETDETAIL'
city = '重庆'
type = 'HOUR'
start_time = '2019-09-06 00:00:00'
end_time = '2019-09-06 12:00:00'# Compile javascript
file = 'encryption.js'
# 调用 compile() 方法来执行刚才保存下来的加密库 encryption.js,执行一遍才能调用
with open(file, encoding=('utf-8')) as f:ctx = node.compile(f.read())# Get params
# 调用一下 JavaScript 中的 getEncryptedData() 方法即可实现加密
# 通过 eval() 方法来模拟执行,得到的结果赋值为 params,这个就是 POST Data 的加密数据
js = 'getEncryptedData("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
params = ctx.eval(js)# Get encrypted response text
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
response = requests.post(url, data={'d': params})# Decode data
# 调用一下 JavaScript 中的 decodeData() 方法即可实现解密
js = 'decodeData("{0}")'.format(response.text)
decrypted_data = ctx.eval(js)data = json.loads(decrypted_data)
res = data.get('result').get('data').get('rows')
for i in res:print(i)
method = 'GETDETAIL' 可以获得aqi(空气质量指数),pm2.5 等数据
换成'GETCITYWEATHER' 可以获取温度,风向等数据
Github:https://github.com/Ingram7/AQI