今天我们爬取某准网-’操作员‘的工资:
1. 抓包:
发现参数b、kiv以及返回值均为加密:
查看调用栈:
发现promise+其他的请求也是类似的加密格式
判断是:拦截器 ——>搜interceptors
网页源码:
找到请求与返回的拦截器(共四个),打断点:
切换城市:发现在请求拦截器,拦截下来了
注意:
数据在这里之前就已经被加密了,
所有请求时的参数加密,和拦截器是没有关系的!
所以应该继续往调用前找
由于该promise是给异步的框架,所以这些调用栈不一定是该固定的操作顺序:
我们可以重新在嫌疑函数处打断点!
当找到 a 调用栈时:发现有重大嫌疑的代码:
打断点,重新切换城市:
发现还是被加密的,继续溯源 r
发现r是上面定义的:
在i.request处的 r 是有params的,而r又是上面定义的
说明params的生成应该介于两者之间:
所以断点应该往前移:
这里是没有params的。
我们一直往下走一步,直到走到这里,发现params生成了:
即找到了入口函数。
2.分析:
逻辑:
a ? { b: t, kiv: a} : r.data : “post” == r.method.toLowerCase() && “json” == r.requestType.toLowerCase() ? (r.headers[“Content-Type”] = “application/json;charset=utf-8”,
a存在的话,{ b: t, kiv: a}
a是存在的!那么就溯源:a 和 t 哪来的:
先看到了t:
t = (0, M.mA)(n, { iv: a}
).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "~")));
逻辑:(函数)(实参)
而函数(0, M.mA)里面是逗号表达式:0不影响该函数
所以:
t = M.mA(n, { iv: a}
n是:明文
经过 M.mA 的计算后,变成了 :t
此时,还差 a 的来源:
"string" == typeof r.data && r.data.indexOf("kiv") > -1 ? (t = (n = "string" == typeof r.data ? JSON.parse(r.data) : r.data).b,
a = n.kiv) : (a = (0,
M._A)(),
分析代码,判断(a = (0,M._A)() ——>a = M._A()
进入 M._A():
进来发现:random
随机值,那么可以直接拿生成的数值即可
a = M._A()
等价于:a = '6ncum6fEaTfvL5HS'
那么t:
t = M.mA(n, { iv: '6ncum6fEaTfvL5HS'}
就差M.mA函数,打断点进入:
分析应该是走:l(e, t.iv)) : “”
传入e(明文)和t.iv(“6ncum6fEaTfvL5HS”)
进入l:
发现了加密逻辑:
l = function(e, t) {
void 0 === e && (e = ""),
void 0 === t && (t = "");
var n = u()
, r = o.AES.encrypt(e.toString(), n.key, { // key是?
iv: o.enc.Utf8.parse(t),
mode: n.mode,
padding: n.pad
});
return r = r.toString()
},
key是什么?进入n = u()发现:拿到mode和pad以及key
3.代码:
AES解密:
这次我们通过js第三方库crypto-js来完成aes解密逻辑:
const CryptoJS = require("crypto-js");
e = '{"query":"操作员","cityCode":34,"industryCodes":"","pageNum":1,"limit":15}';
t = 'i4PKiRyiKb65iVhe'
key = 'G$$QawckGfaLB97r'
r = CryptoJS.AES.encrypt(e.toString(), CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(t),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log(r.toString())
返回值解密代码整合:
分析网站逻辑:
请求逻辑:
1. 生成iv.(随机)
2. 加密
3. 把密文和iv传递给服务器
响应逻辑:
1. 拿到请求时发的iv
2. 进行解密(用的是请求时的iv)
3. 得到明文
综上, 加密和解密用的iv是同一个.
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
import requests
key = 'G$$QawckGfaLB97r'
iv = "3dgM3hFt2DG0Be2w"
ming = '{"query":"操作员","cityCode":1,"industryCodes":"","pageNum":1,"limit":15}'
#参数加密:
aes = AES.new(key=key.encode("utf-8"), mode=AES.MODE_CBC, iv=iv.encode("utf-8"))
mi = aes.encrypt(pad(ming.encode("utf-8"), 16))
mi = base64.b64encode(mi).decode().replace("/", "_").replace("+", "-").replace("=", "~")
params = {
'b': mi,
"kiv": iv
}
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
}
resp = requests.get(url="https://www.kanzhun.com/api_to/search/salary.json", params=params, headers=headers)
mi = resp.text
# 数据解密:
aes_dec = AES.new(key=key.encode("utf-8"), mode=AES.MODE_CBC, iv=iv.encode("utf-8"))
ming = aes_dec.decrypt(base64.b64decode(mi))
ming = unpad(ming, 16)
ming = ming.decode("utf-8")
print(json.loads(ming))