[JS逆向]对某省人才服务平台政策数据爬取
背景介绍
源(脱敏):aHR0cHM6Ly9zZXJ2aWNlLnpqcmMuY29tL3BvbFF1ZXJ5LyU3QiUyMmtleVdvcmQlMjI6JTIyJTIyLCUyMm9wJTIyOiUyMlYlMjIlN0Q=
目标数据路径:政策查询->直接点击搜索->目标数据
逆向流程
1. 接口定位
我们可以看到,在经过翻页点击之后,数据发生了变化,同时可以发现网络抓包模块里也多了一条记录,而我们点开这个记录,就可以看到,这条返回的结果中有我们需要的数据,所以,我们的爬虫只需要对这个接口发起请求,就可以获得我们所需要的数据了。
2. 请求参数分析
通过查看之前接口的负载,我们可以看到
其所传的参数为json格式,并且包含了tnonce、ttimes、tsign、reqdata、citycode这几个参数
显然,citycode为空,ttimes可以很容易知道是一个时间戳,但是其余三个参数(tnonce,tsign,reqdata)是经过加密处理的
通过浏览器的全局搜索,我们能够找到这几个参数出现的位置
2.1 tnonce参数
我们可以看到tnonce对应的p参数就是一个随机数,但是显然,这个随机数和我们最终的tnonce不太像,随意可以断定后续的函数中还会修改这个p值;图中还能看到reqdata是i = o.i(l.a)(i)
,tsign的m是o.i(r.a)(t, p, i)
(与ttime、tnonce、reqdata有关),可以得知,在后面的这个tsign的生成过程中还会改变我们这个p值,得到最终参数中的tnonce
我们接着通过浏览器打断点运行网页查看对应的函数
注意,要通过随机数生成最终tnonce需要清除本地存储中的XD.GNONCE,所在位置如下
2.2 reqdate参数
对于参数reqdata,也就是i = o.i(l.a)(i)
,我们可以找到下列的函数,可见,reqdata是经过AES加密后的数据
对于传入该函数的参数,我们可以看到就是请求的参数信息,我们可以通过python代码实现对应的该部分的AES加密
加密代码如下,需要了解具体机密原理的请自行百度
# ASE-CBC加密
def AESEncrypt(text):
iv = TOM_AES_IV
key = TOM_AES_KEY
aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
encrypt_text = aes.encrypt(pad(text.encode('utf-8'), AES.block_size, style='pkcs7'))
encrypt_text = str(base64.encodebytes(encrypt_text), encoding='utf-8').replace("\n", "")
return encrypt_text
# ASE-CBC解密
def AESDecrypt(text):
iv = TOM_AES_IV
key = TOM_AES_KEY
decode_encrypt_text = base64.b64decode(text)
aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
decrypt_text = aes.decrypt(decode_encrypt_text).decode('utf8')
decrypt_text = decrypt_text.replace(b'\x00'.decode(), "").replace("", "")
return decrypt_text
2.3 tsign参数
运行进入2.1中所示的o.i(r.a)(t, p, i)
函数时,我们可以看到如下图所示,我们可以得到关键函数d(c)
,而参数c是一个数组,其中还有一个常量"zFdxUjrD3rfIXKhq4mfLTzTLUV2F@JK9"
进入函数d(c)
,我们可以看到,经过排序后,将数组中的所有字符串都拼接起来了,并将结果放入 o.i(i.a)(n)
中返回
接着又进入了另一个函数l(c(r(e), e.length * u))
依次经过r(e)
、c(e, n)
、l(e)
三个函数后,就得到我们最终的tsign了,这三个部分我们可以直接复制其js代码,然后经过参数补全之后直接通过python的js2py
库去执行相应的代码(耐心的小伙伴可以根据相关的js代码写出对应的python代码)