前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >某气网js逆向解密

某气网js逆向解密

作者头像
@莜莜
修改2021-07-23 14:21:26
3.5K0
修改2021-07-23 14:21:26
举报
文章被收录于专栏:爬小虫

“近日受小伙伴所托,说是想要获取本地空气数据的历史数据用作分析。但是抓不到包,无法获取网页数据,让我参谋参谋。

下面进入正题:

Js逆向需要的开发环境:

1、安装node.js并配置好环境变量。

2、Python3,并已经装好pyexecjs模块和requests模块。

3、使用chrome内核的浏览器即可。”

得到url打开某气网:

图片
图片

    在网页中,选择大气环境选项。

图片
图片

    这就是我们需要的数据,输入开始结束日期能获得一张数据图。

    但是现在我们还不知道这个表是怎么生成出来的,可能是静态返回,也有可能是动态加载。

图片
图片

    这个页面还包含其他的数据

    —  月度统计分析,

    —  当年累计变化趋势比较,

    —  历史排名曲线(168城市),

    —  监测点空气质量。

    好了,接下来开始尝试抓包!!

图片
图片

    在时间框选好起始结束时间后,点击确定,抓到N个ajax数据包。到底哪一个是呢??猜一个??

    继续分析,我是点击确定才发起ajax请求的,所以应该是最后一个获得的数据包(waterfall最靠后的包)。

图片
图片

    点击,看看返回的响应数据:

图片
图片

    ???一堆乱码,还是加长版的???再来看看,post请求form数据:

图片
图片

 是一个POST请求,表单(Form)是一个变量为加长乱码的数据。

    等等,结尾是“==”,神似base64加密的尾巴。(暂定有Base64加密)。

    就是说,用乱码请求,获取乱码。咳咳,难道是request提交加密的表单数据,获得返回了加密的response???我们继续验证:

图片
图片

 我们以param参数为切入点,全局搜索param参数的加密规则。

    找到五个比较符合规则的param。

图片
图片

从上图分析,param参数是一个getParam的js函数生成的,然后发起了ajax请求。但是,我选定的开始和结束的日期呢???再看看下一个param:

图片
图片

    gettopaqicity函数内也没时间参数,但是我发现了method= “GETCITYAQIRANK”,从字面分析,这对应的应该是,网页中历史排名曲线(168城市)的数据,从这里看出5个不同的param对应5个不同的数据生成方式。我们继续:

图片
图片

    找到了!这个函数有接收开始结束日期参数的“param”,我们来分析下,method参数是固定的,obj里city就是城市名了,type应该是对应(日,周,月,年)的数据,后面的startTime, endTime就不用说了。。。

    既然已经找到了对应的js代码,下面就是常规打断点调试环节了。

图片
图片

    在1575处打上断点,点击网页刷新。

    果然在1575处停了,说明数据加载要通过这个函数,上面咖啡色框框里就是对应生成变量,和我们的分析八九不离十。

    选中getParam函数,进去函数内部看看是什么鬼。

图片
图片

    嗯?getParam是个闭包函数。。这个函数前面一大段是关于AES对称加密的代码,包含加密的密钥。前半段看不懂没关系,重头戏在1336-1350行。

    继续调试,1347行上断点,点刷新!

图片
图片

我们从1336行开始分析,大函数返回一个匿名函数,该匿名接收method和obj两个参数(这两个参数,我们前面提及也分析过),

1337行,appID应该是固定字符串。

1338行,timestamp创建一个时间对象,时间戳。

1339行,need对象内部一堆参数,除了secret,其余我们都了解了。

1345行,secret就是need对象内部的一堆参数通过拼接字符串的形式进行了16进制的MD5加密。

1347行,函数返回值为:AES方式对need对象进行字符串加密的数据。

    到此,getParam函数也分析完了。

图片
图片

    加密部分分析完了,上python:先写个生成method,obj的小函数暖暖手。

图片
图片

    接下来我们需要去偷网站的js代码,因为我们选择走js逆向最简单的路——靠Pyexecjs模块,用python去执行js代码。这里我们把这个Base64整个模块偷过来,我们需要这里面的的hex_md5函数(在上文生成secret变量时用到)。

图片
图片

    把getParam函数也偷过来。

图片
图片

把上面偷的代码Ctrl+C,Ctrl+V到一个js文件:

图片
图片
图片
图片

 输的代码略长就展示一部分。

    按Alt+Tab切到python,

mport requestsimport execjs#请求参数加密def post_data(startTime,endTime):   #YYYY-MMMM-DDDD    method = "CETCITYPERIOD"    obj = {        "city": "杭州",        "type": "DAY",        "startTime": startTime,        "endTime": endTime}    return method,str(obj)method,obj = post_data('2020-12-28','2020-12-29')#1.实例化一个node对node = execjs.get()#2.js源文件编译ctx = node.compile(open('./getParam.js',encoding='utf-8').read())#3.执行js函数#funcName = '''getParam("CETCITYPERIOD", "{'city': '杭州', 'type': 'DAY', 'startTime': '2020-12-28', 'endTime': '2020-12-29'}")'''funcName = f'''getParam("{method}",{obj})'''param = ctx.eval(funcName)print(param) 

   输出结果:

图片
图片

  测试下代码,完美获得param参数值

    现在我们可以拿起param值去搞一下。。

import requestsimport execjs#请求参数加密def post_data(startTime,endTime):   #YYYY-MMMM-DDDD    method = "CETCITYPERIOD"    obj = {        "city": "杭州",        "type": "DAY",        "startTime": startTime,        "endTime": endTime}    return method,str(obj)method,obj = post_data('2020-12-28','2020-12-29')#1.实例化一个node对node = execjs.get()#2.js源文件编译ctx = node.compile(open('./getParam.js',encoding='utf-8').read())#3.执行js函数#funcName = '''getParam("CETCITYPERIOD", "{'city': '杭州', 'type': 'DAY', 'startTime': '2020-12-28', 'endTime': '2020-12-29'}")'''funcName = f'''getParam("{method}",{obj})'''param = ctx.eval(funcName)#print(param)url = "https://www.zq12369.com/api/newzhenqiapi.php"headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'}form_data = {    'param':param}response = requests.post(url=url, headers=headers, data=form_data) print(response.text)

    输出结果:

图片
图片

    嚯,熟悉的乱码出现了。

    这就是网站服务给我们返回的加密数据。

    好了,漫漫逆向路才走一半,还要把这堆乱码解密。。

    #### 数据解密 ####

    我们再回过头去看开始那个ajax请求。

图片
图片

 1542行的success应该是请求成功后返回的数据,

    1544行应该是将data数据解密,

    打断点测试验证猜想。

图片
图片

  断点在1544行停了,data应该就是返回加密的数据(18kB的文本,和上文测试得出的加密数据开头一致)。

    我们再去看看data是什么,这里代码用了eval函数,估计是网页开发不想直接把data在调试中显示出来,那我们选中eval整个函数带参数看看返回的值是什么。

图片
图片

    Json数据出现!!!这就是我们要的数据。这表示我们的解密思路是对的。

    再来我们要搞定b.decode函数和decryData函数,步骤同上文的加密方式。

    输decryData:

图片
图片
图片
图片

继续偷b.decode函数:

图片
图片

    我又在Base64的对象里找到了decode函数。。。不用偷了,上面偷过了。。

    那我们还是需要进一下对象实例化:

图片
图片

ar b = new Base64(); 在偷的代码后加一句。

    这样一来 b.decode(decryptData(datas))我们就都不缺了。

    上python代码:

import requestsimport execjs#请求参数加密def post_data(startTime,endTime):   #YYYY-MMMM-DDDD    method = "CETCITYPERIOD"    obj = {        "city": "杭州",        "type": "DAY",        "startTime": startTime,        "endTime": endTime}    return method,str(obj)method,obj = post_data('2020-12-28','2020-12-29')#print(method,obj)#print(post_data('2020-12-28','2020-12-29'))#1.实例化一个node对node = execjs.get()#2.js源文件编译ctx = node.compile(open('./getParam.js',encoding='utf-8').read())#3.执行js函数#funcName = '''getParam("CETCITYPERIOD", "{'city': '杭州', 'type': 'DAY', 'startTime': '2020-12-28', 'endTime': '2020-12-29'}")'''funcName = f'''getParam("{method}",{obj})'''param = ctx.eval(funcName)#print(param)url = "https://www.zq12369.com/api/newzhenqiapi.php"headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'}form_data = {    'param':param}response = requests.post(url=url, headers=headers, data=form_data) #print(response.text)response_encrypt_data = response.text#响应数据解密decrypt_data = f'''b.decode(decryptData("{response_encrypt_data}"))'''data_json = ctx.eval(decrypt_data)print(data_json)

输出结果:

    到此,数据解密部分也完成了。

代码语言:javascript
复制

import requests
import execjs
#请求参数加密
def post_data(startTime,endTime):   #YYYY-MMMM-DDDD
    method = "CETCITYPERIOD"
    obj = {
        "city": "杭州",
        "type": "DAY",
        "startTime": startTime,
        "endTime": endTime}
    return method,str(obj)
method,obj = post_data('2020-12-28','2020-12-29')
#print(method,obj)
#print(post_data('2020-12-28','2020-12-29'))
#1.实例化一个node对
node = execjs.get()
#2.js源文件编译
ctx = node.compile(open('./getParam.js',encoding='utf-8').read())
#3.执行js函数
#funcName = '''getParam("CETCITYPERIOD", "{'city': '杭州', 'type': 'DAY', 'startTime': '2020-12-28', 'endTime': '2020-12-29'}")'''
funcName = f'''getParam("{method}",{obj})'''
param = ctx.eval(funcName)
#print(param)
url = "https://www.某气网.com/api/newzhenqiapi.php"
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'}
form_data = {
    'param':param
}
response = requests.post(url=url, headers=headers, data=form_data) 
#print(response.text)
response_encrypt_data = response.text
#响应数据解密
decrypt_data = f'''b.decode(decryptData("{response_encrypt_data}"))'''
data_json = ctx.eval(decrypt_data)
print(data_json)

在对某气网逆向的过程中,我还发现网站开发人员采取了很多另类的加密方式,例如:AES与DES相互嵌套加密,Base64连续加密3次 等等。本案例我们就只有解决了一类数据的加密和解密,有兴趣的小伙伴可以也来尝试下其他数据的获取。

     本文用到了很多“应该”这个词,让人感觉很不严谨,实则不然,做逆向就是在一步一步去验证自己对对方网站逻辑的猜想,虽说本文只有寥寥数百字,但是我刚入手这个网站逆向时,也是打了很多断点去验证自己的猜想,就这样提出假设,再去验证,一步步接近真相,并获得数据。写爬虫,分析永远是第一位的,这就是它的魅力。

    这是保存下来的某气网js逆向解密方法,如有不足之处或更多技巧,欢迎指教补充。愿本文的分享对您之后爬虫有所帮助。谢谢~

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档