初步逆向思路
首先进入页面,这里我们爬取软件开发工程师的薪资。找到网络,发送页面数据那个请求,可以看到,请求数据有两个加密的,这个是get请求,请求url是https://www.kanzhun.com/search?cityCode=31&industryCodes=&pageNum=1&query=%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88&type=4。但是为什么会传两个参数呢,一个猜想,kiv是随机的,用于传递给后端使用特定算法解密。
接着观察请求头,发现应该是没有需要逆向的,都是常规的请求头(Traceid需要注意一下)
最后观察返回数据,发现返回数据也是经过解密的,因此确定本次逆向的目标,两个请求参数和返回数据
找加密入口
搜索interceptors
对这种使用promise的请求,第一个就是无脑搜拦截器,不要跟栈,不要跟栈,不要跟栈,重要的事情说三遍。
搜索一下发现有这么多,点进去打断点观察数据的情况
到响应拦截器发现数据已经被加密了,注意,这里虽然看到了data,但前端是可以进行拆分之类的,data不会跟着请求发过去。
搜索URL
搜索url的思路是从小往大搜(因为前端在处理组装数据的时候有时会对url进行修改),这个url是https://www.kanzhun.com/api_to/search/salary.json,先搜索search/salary.json。找到唯一一个,因此直接搜getSearchSalaryList,在里面搜是当前js文件,在外面搜是整个项目,直接在外面搜。
找到打个断点,接下来就是漫长的跟代码过程了
1:最后还有一个小点是当你鼠标划上去的时候你发现跳过去没有自动定位到那个函数,在控制台打出来这个函数,点击也能跳转过去
使用这种方式
2:再跳到下一个函数,打上断点,然后让断点往后走一步,观察一下这个函数的一些参数即可
3:点击这个U函数,然后跳到这里,这里看一下代码,应该是在组装url和data,method这些参数,直接从a函数继续跟。
4:找到了这个入口,其实一步一步跟过去的路上分析代码就是需要经验的熟练度的问题了,最后找到了加密的JS文件如下
看到这个文件跟之前的只是函数调用就明显不一样了,这个基本上就在打名牌了,看到这里面的b和kiv就已经结束了。在函数开头打个断点,开始单步调试。调试过程中发现:
我们传过来的e被拷贝到了a对象里面(注意,这个是一个重要信息,别傻傻的还跟e,前端对于这种对象拷贝很常见,就是组合参数为了发请求)。看到headers里面还有个traceId,这个运行几次发现值是固定的,就不需要逆向。
JS代码逻辑跟踪
加密参数的获取
参数s
s是经过这个s = (0,
M._A)()这个函数调用而来的,前面这个判断大体意思就是看看s有没有值,有的话就用之前的,没有就重新赋值。这个里面
但是这个参数在控制台运行几次发现值是随机的,且验证了之前的猜想
点进去看看,发现M._A是这样一个函数,总体意思就是使用逻辑与判断e有没有值,没有值就给e赋值为16。这里的e进来的时候是underfined。这个void 0的值是underfined,跟e比较,如果e有值那逻辑与右边的就不走了,没有给e一个默认值。这个还是蛮重要的
然后下面的循环是从那62的数当中循环随机选取16个数,返回回去。其实看到random的时候就可以出去了,它随机都可以,我们直接给他定死就得了。
参数t(b)
继续往下走,进入给t赋值的那个函数(0,
M.mA),发现调用了函数l,继续跟
看到AES,加密就找到了。
这里我们找他的key和iv,mode这三个就可以收官了(pad默认的pad填充就行)。注意下面,decrypt就在下面(这个也是一个非常常见的现象)。找到这三个值,去python里面直接复现就可以了。接下来单步调试
key的获取
在控制台一输出发现是一个数组,这是cryptoJS的东西,解决方法就是直接toString再把cryptoJS enc对象传过去就可以了(这里的cryptojs就是o,可能是进行了打包时候的压缩)
得到key的值为:G$$QawckGfaLB97r
iv的获取
观察iv,其实就是之前随机出来的s,直接定死(如果害怕服务端检测就把他随机的逻辑复现一下)。得到iv:CSpFHJE0TN9oL3rF
mode的获取
有iv,AES,DES 99.9%都是CBC模式。得到mode:CBC
最后复现的时候需要注意这里把字符串做了一个替换。且它的查询的明文是这样的
"{"query":"软件开发工程师","cityCode":"","industryCodes":"","pageNum":1,"limit":15}"
解密的逻辑
解密跟加密共用的是一套AES,逻辑一样,就不再赘述了(下面这张图我重新发了一次请求,因此t不一样)
python代码复现
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
url = "https://www.kanzhun.com/api_to/search/salary.json"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
'Cookie': 'wd_guid=5632262a-3e0b-4746-b729-c972d1d21db9; historyState=state; __c=1700303213; __g=-; __l=l=%2Fwww.kanzhun.com%2Fsearch%3FcityCode%3D31%26industryCodes%3D%26pageNum%3D1%26query%3D%25E8%25BD%25AF%25E4%25BB%25B6%25E5%25BC%2580%25E5%258F%2591%25E5%25B7%25A5%25E7%25A8%258B%25E5%25B8%2588%26type%3D4&r=; Hm_lvt_1f6f005d03f3c4d854faec87a0bee48e=1700299731,1700303213; R_SCH_CY_V=25761614; W_CITY_S_V=31; pageType=1; lasturl="https://www.kanzhun.com/search?cityCode=31&industryCodes=&pageNum=1&query=%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88&type=4'
} # 带上cookie,获取的数据可以多一点
# 进行加密的逻辑
kiv = "CSpFHJE0TN9oL3rF"
# AES复现
aes = AES.new(key=b'G$$QawckGfaLB97r', IV=b'CSpFHJE0TN9oL3rF', mode=AES.MODE_CBC)
ming = '{"query":"软件开发工程师","cityCode":"","industryCodes":"","pageNum":1,"limit":15}'
mi = aes.encrypt(pad(ming.encode('utf-8'), 16))
b = base64.b64encode(mi).decode().replace("/", "_").replace("+", "-").replace("=", "~")
# 准备查询参数
params = {
'b': b,
'kiv': kiv
}
res = requests.get(url, params=params, headers=headers)
print(res.text)
# 进行解密的逻辑
aes_decrypt = AES.new(key=b'G$$QawckGfaLB97r', IV=b'CSpFHJE0TN9oL3rF', mode=AES.MODE_CBC)
data=unpad(aes_decrypt.decrypt(base64.b64decode(res.text)),16).decode('utf-8')
print(data)
最后提取json数据即可
总结
第一次写逆向的相关博文教程,这个是我跟着樵夫老师的课再结合自己的思考来的一些总结,前前后后写了一个半小时,作者还是一个逆向小白,这个网站确实很简单,大佬轻喷。以后简单写一下思路,肯定是不会这么细了,会考虑把自己的实战放在B站上讲解一下。有问题请评论或联系我~
文章评论