今天带来某某网站JS逆向的分析全过程,欢迎大家阅读。文中若有错误内容,欢迎大家多多指正!
特别声明:本公众号分享内容只作为学术研究,不用于其它用途。
目录
① 参数分析
② 断点调试
③ 算法还原
④ 思路总结
一、参数分析
1. 打开指定的网站,点击指定栏目标题,如下图所示:
2. 确定获取当前推荐页面下的文章信息,使用fn+f12打开开发者工具,然后刷新当前页面,在Network工具栏进行Search内容搜索,结果如下图所示:
3. 接口参数分析
request参数:
从上图中可得知,参数data为加密参数,初步猜测为base64加密,打开base64在线工具进行解密分析,如下图所示:
总结:这是一些什么内容呢?感觉和我们猜测的不一致,加密前的参数是什么样子呢?带着这些疑问,我们还是进行断点调试吧!
二、JS断点调试
1. 使用XHR请求进行断点调试对指定的url地址,截图如下所示:
2. 然后刷新当前页面,让断点开始生效,如下图所示:
3. 进行断点调试,逐步分析过程,如下图所示:
总结分析:分析上面断点截图,我们发现此刻已经产生了加密参数data,显然我们的断点打早了,在截取到xhr请求之前,data的加密算法已经计算完成!
问题思考:那么此刻我们该如何快速定位到加密参数呢?总不能一行行看上面的代码吧?那样效率太低了,接下来,直接上大招!
4. Call Stack 调用栈回溯,如下图所示(顺序从上到下):
5. 通过查看函数栈,我们确定加密的地方如下图所示:
6. 打印此函数的入参和出参如下图所示:
调试总结:从上图可以查看到加密前的参数及加密后的data体,接下来我们只需要对"encryptSm4ECB(t)"加密算法进行还原即可!
三、算法还原
1. js代码脱机还原如下:
ut = [214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72] , lt = [462357, 472066609, 943670861, 1415275113, 1886879365, 2358483617, 2830087869, 3301692121, 3773296373, 4228057617, 404694573, 876298825, 1347903077, 1819507329, 2291111581, 2762715833, 3234320085, 3705924337, 4177462797, 337322537, 808926789, 1280531041, 1752135293, 2223739545, 2695343797, 3166948049, 3638552301, 4110090761, 269950501, 741554753, 1213159005, 1684763257] , ct = [2746333894, 1453994832, 1736282519, 2993693404];function r(t) { for (var e, n = new Array, a = t.length, o = 0; o < a; o++) 65536 <= (e = t.charCodeAt(o)) && e <= 1114111 ? (n.push(e >> 18 & 7 | 240), n.push(e >> 12 & 63 | 128), n.push(e >> 6 & 63 | 128), n.push(63 & e | 128)) : 2048 <= e && e <= 65535 ? (n.push(e >> 12 & 15 | 224), n.push(e >> 6 & 63 | 128), n.push(63 & e | 128)) : 128 <= e && e <= 2047 ? (n.push(e >> 6 & 31 | 192), n.push(63 & e | 128)) : n.push(255 & e); return n}function mt(t, e) { return (255 & t[e]) << 24 | (255 & t[e + 1]) << 16 | (255 & t[e + 2]) << 8 | 255 & t[e + 3]}function pt(t, e) { return e ? function (t) { return t ^ Ft(t, 2) ^ Ft(t, 10) ^ Ft(t, 18) ^ Ft(t, 24) }(dt(t)) : function (t) { return t ^ Ft(t, 13) ^ Ft(t, 23) }(dt(t))}function Ft(t, e) { return t << e | t >>> 32 - e}function dt(t) { return (255 & ut[t >>> 24 & 255]) << 24 | (255 & ut[t >>> 16 & 255]) << 16 | (255 & ut[t >>> 8 & 255]) << 8 | 255 & ut[255 & t]}function vt(t) { // console.log(t); for (var e = new Array(32), r = new Array(4), n = 0; n < 4; n++) r[n] = mt(t, 4 * n); var i = new Array(4); for (n = 0; n < 4; n++) i[n] = r[n] ^ ct[n]; for (n = 0; n < 32; n += 4) e[n] = i[0] = i[0] ^ pt(i[1] ^ i[2] ^ i[3] ^ lt[n], !1), e[n + 1] = i[1] = i[1] ^ pt(i[2] ^ i[3] ^ i[0] ^ lt[n + 1], !1), e[n + 2] = i[2] = i[2] ^ pt(i[3] ^ i[0] ^ i[1] ^ lt[n + 2], !1), e[n + 3] = i[3] = i[3] ^ pt(i[0] ^ i[1] ^ i[2] ^ lt[n + 3], !1); // console.log('eeeee'); // console.log(e); // console.log("vt"); return e}function yt(t, e, r, n, i) { return t ^ pt(e ^ r ^ n ^ i, !0)}function wt(t, e, r) { e[r] = (4278190080 & t) >>> 24, e[r + 1] = (16711680 & t) >>> 16, e[r + 2] = (65280 & t) >>> 8, e[r + 3] = 255 & t}function bt(t, e, r, n, i, o) { for (var s = new Array(4), f = 0; f < 4; f++) s[f] = mt(t, e + 4 * f); for (f = 0; f < 32; f += 4) 0 == o ? (s[0] = yt(s[0], s[1], s[2], s[3], i[f]), s[1] = yt(s[1], s[2], s[3], s[0], i[f + 1]), s[2] = yt(s[2], s[3], s[0], s[1], i[f + 2]), s[3] = yt(s[3], s[0], s[1], s[2], i[f + 3])) : (s[0] = yt(s[0], s[1], s[2], s[3], i[31 - f]), s[1] = yt(s[1], s[2], s[3], s[0], i[30 - f]), s[2] = yt(s[2], s[3], s[0], s[1], i[29 - f]), s[3] = yt(s[3], s[0], s[1], s[2], i[28 - f])); for (f = 0; f < 4; f++) wt(s[3 - f], r, n + 4 * f)}function encodeWithPKCS5(t, e) { for (var r = e - t.length % e, n = new Array(t.length + r), i = 0; i < t.length; i++) n[i] = t[i]; for (i = t.length; i < r + t.length; i++) n[i] = r; // console.log(n, 9999); return n}function h(t, e) { if (!t) throw new TypeError("Illegal Argument:" + e)}function encryptWithECB(t, e) { h(void 0 !== t && t.length % 16 == 0, "illegal plaintext:the length of plaintext must be the multiple of 16."), h(void 0 !== e && 16 === e.length, "illegal key:the length of sm4Key must be 16 bytes."); for (var r = vt(e), n = t.length, i = new Array(n), o = 0; o < n;) bt(t, o, i, o, r, 0), o += 16; // console.log("iiiiii"); return i}B.failed = function (t) { var e = B.getResMark(t); return new B(t, e)}function CMBSM4EncryptWithECB(t, e) { if (!e || !t) return B.failed(a); if ("object" != typeof e || "object" != typeof t) return B.failed(m); if (e.length <= 0) return B.failed(l); if (16 != t.length) return B.failed(F); var r = encodeWithPKCS5(e, 16) , n = encryptWithECB(r, t) , i = new A; // console.log(n, i); return i.set("result", n), B.success(i)}function encryptSm4ECB(t) { var c = 'eht'; var e = r("string" == typeof t ? t : JSON.stringify(t)) , n = (new Date).getTime() , a = [(a = (n + "").split(""))[5], a[10]].join("") , a = r("CFKt03X9Ufk" + c + a); try { var o = CMBSM4EncryptWithECB(a, e); console.log(o, 'o'); if (o && 0 == o.errorCode) { for (var i = "", s = 0; s < o.result.length; s++) i += String.fromCharCode(o.result[s]); // console.log(i, "i"); return { data: btoa(i), timestamp: n } } } catch (t) { } return 'error'}var t = '{"loadType":"loadMore","modelCheck":null,"locationData":{"latitude":"","longitude":"","location_state":"","city":""},"pageNo":1,"authorList":[],"tagList":[],"cacheNews":[],"appid":"","uid":"","isFirstLoad":true}';var data = encryptSm4ECB(t);console.log(data);// 输出结果如下/*{ data: 'SJPWjy6c4vZhQ7yBe1b14l7RlXFzWc1hh6+zRKOO1PfdouEn8s1qGwrgAYzqIQaDTiV7t1vXtXyZEMlpKTeI1eq8ieOzE5mMIwLjfQKsnBQdcxH9jNw2asgWUrjhvZ5S+zKTlVwbqVGoxvS5WZ3vvUAVuDvgLhm78zrPC3DoP8Kf4q/KYxC/cKn23LV4w0zzj8JloGP57Ah9jBlvfdZo7Ui3TLPFeWfYUjpW+YTpj0jnHB7N2UOKWEDS//qrUogQKCfwMG5qTOYGTn9iEmx5x+OHw4zwEOvbR2a6cpjaArk=', timestamp: 1646897686910} */2. Python代码还原如下:
import jsonimport execjsimport requestsclass Spider: def __init__(self): with open('zs.js', 'r', encoding='UTF-8') as f: lines = f.read() self.ctx = execjs.compile(lines) self.url = "https://xxxxxxx/ccgateway/api/paas-entry/recommend/content/list" self.headers = { 'Connection': 'keep-alive', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"', 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json;charset=UTF-8', 'Auth-Type': 'optional', '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/99.0.4844.51 Safari/537.36', 'sec-ch-ua-platform': '"macOS"', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Dest': 'empty', 'Accept-Language': 'zh-CN,zh;q=0.9', } def start_requests(self): req_data = '{"loadType":"loadMore","modelCheck":null,"locationData":{"latitude":"","longitude":"","location_state":"","city":""},"pageNo":2,"authorList":[],"tagList":[],"cacheNews":[],"appid":"","uid":"","isFirstLoad":true}' data = self.ctx.call('encryptSm4ECB', req_data) print(data) response = requests.post(url=self.url, headers=self.headers, data=json.dumps(data)) print(response.json())if __name__ == '__main__': spider = Spider() spider.start_requests()3. 打印结果如下:
算法还原流程到此就结束了,欢迎大家提问!
四、思路总结
确定参数 > 断点调试 > 加密定位 > 算法还原 > 代码测试 > 验证结果 > 完毕
我是TheWeiJun,有着执着的追求,信奉终身成长,不定义自己,热爱技术但不拘泥于技术,爱好分享,喜欢读书和乐于结交朋友,欢迎加我微信与我交朋友。
分享日常学习中关于爬虫、逆向和分析的一些思路,文中若有错误的地方,欢迎大家多多交流指正☀️