简介
蝉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的博客!

