简介
蝉MM上商品库数据返回后是加密的需要处理才能正常阅读。经过分析为AES加密ECB模式,Pkcs7填充,解密后经过一个u函数处理再gzip解压。
目标页面:aHR0cHM6Ly93d3cuY2hhbm1hbWEuY29tL3Byb21vdGlvblJhbmsvP2tleXdvcmQ9Jmhhc19qeF9jb21taXNzaW9uPTA=
分析
- 浏览器F12打开开发者工具,切换到“网络”工具可以看到返回数据类似下面这样
1
2
3
4
5
6
7
8{
"data": {
"is_encrypt": true,
"data": "YS2uKFD807w8u6sRgZ20YYglwYz2JvhIymEnYMQNKcudNDDLLP8+HhcOeb6zTv6btyIPjU7DlznAJts6v7LZmtVY040w80qVCQdlT0VHMDHrdLvBPJiyOsNsn2FNoC5S6E8ka8U5YM1iwuKIe6I+kGG5HMY1reCV0kyoIucnud8="
},
"errCode": 0,
"rid": "3d9c327a08d38387cecf87966e74c266"
} - data字段看起来像是base64编码的,但是经过base64解码后发现是乱码,可能是加密后的数据,所以需要解密才能看到明文。直接搜索“is_encrypt”可以在js内🫡两个地方,在两个地方直接下断点,刷新页面略微看了下断下来的数据发现ajax返回的数据走其中一个断点参数为e。代码片段如下: 所有数据都走这里看起来解密的时候也在这里根据条件走某个分支,直接在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33function $() {
return $ = (0,
r.Z)((0,
a.Z)().mark((function t(e, n) {
var i, o, r, s, c, l, u;
return (0,
a.Z)().wrap((function(t) {
while (1)
switch (t.prev = t.next) {
case 0:
if (c = null === e || void 0 === e ? void 0 : e.data,
!(!0 === (null === c || void 0 === c || null === (i = c.data) || void 0 === i ? void 0 : i.is_encrypt) && "string" === typeof (null === c || void 0 === c || null === (o = c.data) || void 0 === o ? void 0 : o.data) && (null === c || void 0 === c || null === (r = c.data) || void 0 === r || null === (s = r.data) || void 0 === s ? void 0 : s.length) > 0)) {
t.next = 6;
break
}
return t.next = 4,
(0,
Z.ZP)(null === c || void 0 === c || null === (l = c.data) || void 0 === l ? void 0 : l.data);
case 4:
u = t.sent,
c.data = u;
case 6:
n(e);
case 7:
case "end":
return t.stop()
}
}
), t)
}
))),
$.apply(this, arguments)
}i.is_encrypt
行下条件断点e.data.data.is_encrypt == true
,刷新页面等断下来单步步入。十多步之后发现了如下加密代码:主要特征为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55function m() {
return m = (0,
o.Z)((0,
i.Z)().mark((function t(e) {
var n, a, o, r;
return (0,
i.Z)().wrap((function(t) {
while (1)
switch (t.prev = t.next) {
case 0:
return t.prev = 0,
n = s.enc.Utf8.parse(27..toString(36).toLowerCase().split("").map((function(t) {
return String.fromCharCode(t.charCodeAt() + -39)
}
)).join("") + 24901..toString(36).toLowerCase() + 33..toString(36).toLowerCase().split("").map((function(t) {
return String.fromCharCode(t.charCodeAt() + -39)
}
)).join("") + 976..toString(36).toLowerCase() + 20..toString(36).toLowerCase().split("").map((function(t) {
return String.fromCharCode(t.charCodeAt() + -39)
}
)).join("") + function() {
var t = Array.prototype.slice.call(arguments)
, e = t.shift();
return t.reverse().map((function(t, n) {
return String.fromCharCode(t - e - 24 - n)
}
)).join("")
}(10, 127, 154, 91, 151, 91, 136) + 11..toString(36).toLowerCase() + 13..toString(36).toLowerCase().split("").map((function(t) {
return String.fromCharCode(t.charCodeAt() + -13)
}
)).join("")),
a = s.AES.decrypt(e, n, {
mode: s.mode.ECB,
padding: s.pad.Pkcs7
}),
o = u(a),
r = c.ungzip(o, {
to: "string"
}),
t.abrupt("return", JSON.parse(r));
case 8:
throw t.prev = 8,
t.t0 = t["catch"](0),
console.log(t.t0),
new Error("date decrypt error");
case 12:
case "end":
return t.stop()
}
}
), t, null, [[0, 8]])
}
))),
m.apply(this, arguments)
}据此判断为AES解密,n为key,测试用n不会变。解密之后经过u函数再ungzip解压。可以跟进s.AES.decrypt函数查看上下文,发现是调用了CryptoJS的aes解密函数。1
2
3
4
5
6
7
8a = s.AES.decrypt(e, n, {
mode: s.mode.ECB,
padding: s.pad.Pkcs7
}),
o = u(a),
r = c.ungzip(o, {
to: "string"
})
搞定
那么剩下就是搞个CryptoJS再搞个ungzip解压。我直接实用了CryptoJS库,ungzip实用了pako库。u函数在这段代码上面直接扣。
1 | function u(t) { |
然后组合js到一起,由于主程序实用python,所以直接execjs模块跑起来
1 | # 此处解决 execjs 报下面错误的问题 一定写在import execjs前 |
输出为:
1 | { |
经过对比和网站上的一致。
附件
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 CN-P5的博客!