js逆向:基于python、js的逆向加密与解密
1.js逆向的准备工作(查找解密加密的方法)
1.1 选择合适的触发事件,排除干扰请求
- 想要爬取什么内容就选中哪个地方发起请求,排除其他请求的干扰
- 就比如下图,需要爬取通知公告,就先清除网站的其他请求,然后再点击需要爬取的地方(这时候就默认发起请求)
1.2 注意请求中的请求载荷(负载,就是需要的参数)
- 在发起请求之后,先看一下Content-type(传送的数据是json格式,还是form表单格式),然后response中写那种格式
1.3 请求和响应入口定位(方法)
1.3.1 关键字搜索
-
key关键字:portal-sign(请求解密中可以使用到)
-
方法关键字:encrypt(加密)、decrypt(解密)
-
headers关键字
-
拦截器关键字
请求拦截器 响应拦截器 interceptors.request.use(f1) interceptors.request.use(f2) interceptors.response.use(f3) interceptors.response.use(f4) this.interceptors.response.forEach(function(e) { t.push(e.fulfilled, e.rejected) }); t.length; ) n = n.then(t.shift(), t.shift()); return n 路径关键字:
-
请求堆栈:主要是针对请求逆向
1.4 断点
- 普通断点
- XHR断点
- 条件断点
- 日志断点
2.实战案例:公共资源交易平台
- 案例网址 :https://ggzyfw.fj.gov.cn/index/new/
- 要求:爬取首页或者通知公告中的新闻内容
2.1 第一步:先模拟网站发起请求,获取数据
右键点击名称下面的圈的网址:复制它的cURL,到https://curlconverter.com/,粘贴它请求的代码
代码如下:
import requests
cookies = {
'ASP.NET_SessionId': 'k4wbjl0uwixk5l4d4zygwjx0',
}
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/json;charset=UTF-8',
'Origin': 'https://ggzyfw.fj.gov.cn',
'Pragma': 'no-cache',
'Referer': 'https://ggzyfw.fj.gov.cn/index/new/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0',
'portal-sign': '7147d1389bec4bc68f2e1617ee426713',
'sec-ch-ua': '"Chromium";v="134", "Not:A-Brand";v="24", "Microsoft Edge";v="134"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
# 'Cookie': 'ASP.NET_SessionId=k4wbjl0uwixk5l4d4zygwjx0',
}
json_data = {
'pageSize': 5,
'type': '12',
'ts': 1742886819782,
}
response = requests.post('https://ggzyfw.fj.gov.cn/FwPortalApi/Article/PageList', cookies=cookies, headers=headers, json=json_data)
print(respnse.text)
2.2 第二步:获取数据成功,但是数据是加密的(我们需要破解数据),如下图
2.3 第三步:破解加密数据,这时候我们就需要进行关键字搜索,看看浏览器是怎样破解本地的数据的,我们可以仿照观察它使用了哪种算法进行加密的
2.3.1.搜索:decrypt(
- 将搜索到的进行打断点,如果搜出来的很多,说明我们需要换其他关键字搜索(恰好我们搜索出来的刚好是)
- 在图中可以看出使用的是AES算法,k是r["e"],iv是r["i"],我们就需要使用AES算法进行解密
2.3.2 进行破解(代码和上面自己拼接)
代码如下:
b64_encrypt_data = response.json().get('Data')
# print(b64_encrypt_data)
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
# (1) 解码base64
encrypt_data = base64.b64decode(b64_encrypt_data)
print("encrypt_data:::",encrypt_data)
# (2) 解密:算法和什么钥匙
key = 'EB444973714E4A40876CE66BE45D5930'.encode()
iv = 'B5A8904209931867'.encode()
aes_obj = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
data = aes_obj.decrypt(encrypt_data)
data = unpad(data,16).decode()
print("data:::",data)
2.3.3 上述是使用python进行破解的,我们也可以使用js逆向进行破解
-
和上述操作一样,进行断点,但是不同的是我们需要将解密的这一段复制过来,放到js文件中模拟它解码
-
放到js文件中进行调试(先运行代码,哪里不合适就去浏览器观察,缺什么就补什么,然后将Data搬过来进行测试)
代码如下: const crypto_js = require("crypto-js") // 数据解密 function b(t) { var e = crypto_js.enc.Utf8.parse("EB444973714E4A40876CE66BE45D5930") , n = crypto_js.enc.Utf8.parse("B5A8904209931867") , a = crypto_js.AES.decrypt(t, e, { iv: n, mode: crypto_js.mode.CBC, padding: crypto_js.pad.Pkcs7 }); return a.toString(crypto_js.enc.Utf8) } // 测试代码 t = "" // 这里和我们上面获取的加密数据相同 console.log(b(t))
2.3.4 js测试成功之后,我们需要在python文件中运行我们的js脚本文件(最终完成了解密)
- 准备工作:
node.js版本18 python版本3.10以上,但不要使用最新版 其次还需要导入以下: import subprocess from functools import partial subprocess.Popen = partial(subprocess.Popen, encoding="utf-8") import execjs
- 在python中实现js解密
代码如下: with open("007 js逆向解密、加密数据.js", encoding="utf8") as f: jsCode = f.read() js_compile = execjs.compile(jsCode) ret = js_compile.call("b", base64_encrypt_data)
2.4 模仿浏览器sign加密,实现载荷中的参数无论怎样改变,都可以获取到数据
2.4.1 关键字搜索 portal-sign
- 找到先打断点
- 请求看验证断点,验证成功确实断在这里,然后悬浮鼠标点进函数里面
- 发现确实是加密的sign,最后可以验证一下,使用MD5进行加密的
2.4.2 进行模仿加密,实现我们改载荷也能爬取数据
-
将找到的代码,复制到我们创建的js文件中,进行调试
代码如下: // 数据加密 function l(t, e) { return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() == e.toString().toUpperCase() ? 0 : -1 } function u(t) { for (var e = Object.keys(t).sort(l), n = "", a = 0; a < e.length; a++) if (void 0 !== t[e[a]]) if (t[e[a]] && t[e[a]] instanceof Object || t[e[a]] instanceof Array) { var i = JSON.stringify(t[e[a]]); n += e[a] + i } else n += e[a] + t[e[a]]; return n } function d(t) { for (var e in t) "" !== t[e] && void 0 !== t[e] || delete t[e]; var n = "B3978D054A72A7002063637CCDF6B2E5" + u(t); return crypto_js.MD5(n).toString() } // 测试js,sign加密 data = { 'pageNo': 1, 'pageSize': 10, 'total': 797, 'type': '12', 'timeType': '0', 'ts': 1742800934952, } // console.log(d(data))
-
将它放到我们的python中运行
代码如下: with open("007 js逆向解密、加密数据.js", encoding="utf8") as f: jsCode = f.read() js_compile = execjs.compile(jsCode) sign = js_compile.call("d", json_data) headers['portal-sign'] = sign
3.爬取完整代码
import time import subprocess from functools import partial subprocess.Popen = partial(subprocess.Popen, encoding="utf-8") import execjs import requests cookies = { 'ASP.NET_SessionId': 'yw4wual11my4nd4pzpat0er2', } headers = { 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Type': 'application/json;charset=UTF-8', 'Origin': 'https://ggzyfw.fj.gov.cn', 'Pragma': 'no-cache', 'Referer': 'https://ggzyfw.fj.gov.cn/index/newList?type=12', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0', # 'portal-sign': '4b9c48b640dc79d393ee23c9be59849e', 'sec-ch-ua': '"Chromium";v="134", "Not:A-Brand";v="24", "Microsoft Edge";v="134"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', # 'Cookie': 'ASP.NET_SessionId=yw4wual11my4nd4pzpat0er2', } json_data = { 'pageNo': 3, 'pageSize': 10, 'total': 797, 'type': '12', 'timeType': '0', 'ts': int(time.time() * 1000), } # 基于js,sign加密 with open("007 js逆向解密、加密数据.js", encoding="utf8") as f: jsCode = f.read() js_compile = execjs.compile(jsCode) sign = js_compile.call("d", json_data) headers['portal-sign'] = sign response = requests.post('https://ggzyfw.fj.gov.cn/FwPortalApi/Article/PageList', cookies=cookies, headers=headers, json=json_data) base64_encrypt_data = response.json().get("Data") # print(base64_encrypt_data) # 基于JS逆向解密数据 with open("007 js逆向解密、加密数据.js", encoding="utf8") as f: jsCode = f.read() js_compile = execjs.compile(jsCode) ret = js_compile.call("b", base64_encrypt_data) import json ret_table = json.loads(ret).get('Table') for i in ret_table: print(i)