文章目录
- 1. 接口分析
- 2. 断点调式
- 3. 补环境重写加密
- 4. 验证结果
1. 接口分析
目标站点:
aHR0cHM6Ly93d3cucWltYWkuY24vcmFuay9tYXJrZXRSYW5r
先刷新网页,请求接口中的analysis参数本次分析的目标
2. 断点调式
打上XHR断点,刷新页面之后断点会断在接口处,接下来单步调式分析找到加密位置
这里我们可以看到e就是加密后生成的analysis值。不过这上图加密区域的代码逻辑还是有些复杂的,不能直接扣下来用
得分析每一个函数,还有其中有一些固定的值(这个值是会经常变动的),接下来我们开始补环境然后还原最终的JS加密代码
3. 补环境重写加密
function v(t) {
t = encodeURIComponent(t).replace(/%([0-9A-F]{2})/g, function (n, t) {
return o("0x" + t)
});
try {
return btoa(t)
} catch (n) {
return Buffer.from(t).toString("base64")
}
}
function o(n) {
let f2 = '66';
let s2 = '72';
let d2 = '6f';
let m2 = '6d';
let l2 = '43';
let v2 = '68';
let p2 = '61';
let h2 = '64';
let y2 = '65';
t = "",
[f2, s2, d2, m2, l2, v2, p2, s2, l2, d2, h2, y2].forEach(function (n) {
t += unescape("%u00" + n)
});
var t, e = t;
return String.fromCharCode(n)
}
function h(n, t) {
t = t || u();
for (var e = (n = n.split("")).length, r = t.length, a = "charCodeAt", i = 0; i < e; i++)
n[i] = o(n[i][a](0) ^ t[(i + 10) % r][a](0));
return n.join("")
}
function encrypted(key) {
var s = 1359
var H = 0
var e, r = +new Date() - (s || H) - 1661224081041, a = [];
var v1 = "@#"
// 固定值
var d = "xyz517cda96abcd"
// a = a[Ot]()[I1](_),
a = a.sort().join("")
a = (a += v1 + key) + (v1 + r) + (v1 + 3)
e = (0,
v)((0,
h)(a, d))
return e
}
现在我们来说一说上面重写的JS代码流程,首先看这一行代码var e, r = +new z[W] - (s || H) - 1661224081041, a = [];
z[W]我们鼠标放置可以看到它是一个Data(),然后我们需要做的就是把s、H两个变量的值拿到,这两个值写死就可以
通过上述方法,将d、v两个参数的值找到填好即可。这里需要注意的是d的值是在后续可能随时会变化的,相当于一个令牌
接下来我们需要找到代码中的i[jt]、i[qt]两个函数方法
i[jt]方法断点如下:
i[qt]方法断点如下:
其中h方法,我们也需要补一下值,直接替换一下方法跟值:
原始代码:for (var e = (n = n$1)[R], r = t[R], a = q1, i = H; i < e; i++)
替换:for (var e = (n = n.split(“”)).length, r = t.length, a = “charCodeAt”, i = 0; i < e; i++)
接下来处理一下v方法中的这行代码,也是需要重写还原的:
原始代码:t = zV1[T](/%([0-9A-F]{2})/g, function(n, t)
替换:t = encodeURIComponent(t).replace(/%([0-9A-F]{2})/g, function (n, t)
至于怎么补怎么还原可以根据断点,分析每一个参数、变量来补就可以,比如上面的z[V1]它就是一个encodeURIComponent方法,T是一个replace方法,数据内容不变
接下来,继续接着往下走,可以看到在v方法内调用了一个o,我们需要将这个o的方法也扣出来
断点调式找到o方法后,可以发现这个方法内有f2、s2、d2…多个参数值,因为外部没有在调用的时候传参,直接补上显示的值:
let f2 = '66';
let s2 = '72';
let d2 = '6f';
let m2 = '6d';
let l2 = '43';
let v2 = '68';
let p2 = '61';
let h2 = '64';
let y2 = '65';
v方法内还有一个z方法使用同样的方式重写:
原始代码:return z[W1]K1U1
替换:return Buffer.from(t).toString(“base64”)
4. 验证结果
接下来我们简单的写一个python请求调用代码,测试一下效果:
import time
import execjs
import requests
headers = {
'authority': 'api.qimai.cn',
'sec-ch-ua': '"Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"',
'accept': 'application/json, text/plain, */*',
'sec-ch-ua-mobile': '?0',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'accept-language': 'zh-CN,zh;q=0.9',
}
url = "https://api.qimai.cn"
url_args = "/rank/marketRank"
with open("./qimai.js") as f:
js_code = f.read()
analysis = execjs.compile(js_code).call("encrypted", url_args)
res = requests.get(f"{url}{url_args}?analysis={analysis}", headers=headers).json()
print(res)
验证如下:
好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章