0
点赞
收藏
分享

微信扫一扫

某某网站JS逆向分析(JS逆向第六期)

dsysama 2023-02-02 阅读 116

    今天带来某某网站JS逆向的分析全过程,欢迎大家阅读。文中若有错误内容,欢迎大家多多指正!

    特别声明:本公众号分享内容只作为学术研究,不用于其它用途。




 目录



①    参数分析

②    断点调试

③    算法还原

④    思路总结


一、参数分析


1.  打开指定的网站,点击指定栏目标题,如下图所示:

某某网站JS逆向分析(JS逆向第六期)_json_02



2. 确定获取当前推荐页面下的文章信息,使用fn+f12打开开发者工具,然后刷新当前页面,在Network工具栏进行Search内容搜索,结果如下图所示:

某某网站JS逆向分析(JS逆向第六期)_ide_03



3. 接口参数分析

request参数:

某某网站JS逆向分析(JS逆向第六期)_ide_04


从上图中可得知,参数data为加密参数,初步猜测为base64加密,打开base64在线工具进行解密分析,如下图所示:

某某网站JS逆向分析(JS逆向第六期)_json_05



总结:这是一些什么内容呢?感觉和我们猜测的不一致,加密前的参数是什么样子呢?带着这些疑问,我们还是进行断点调试吧!



二、JS断点调试


1.  使用XHR请求进行断点调试对指定的url地址,截图如下所示:

某某网站JS逆向分析(JS逆向第六期)_ide_06


2.  然后刷新当前页面,让断点开始生效,如下图所示:

某某网站JS逆向分析(JS逆向第六期)_json_07



3. 进行断点调试,逐步分析过程,如下图所示:

某某网站JS逆向分析(JS逆向第六期)_断点调试_08


总结分析:分析上面断点截图,我们发现此刻已经产生了加密参数data,显然我们的断点打早了,在截取到xhr请求之前,data的加密算法已经计算完成!


问题思考:那么此刻我们该如何快速定位到加密参数呢?总不能一行行看上面的代码吧?那样效率太低了,接下来,直接上大招!


4. Call Stack 调用栈回溯,如下图所示(顺序从上到下):

某某网站JS逆向分析(JS逆向第六期)_ide_09



5. 通过查看函数栈,我们确定加密的地方如下图所示:

某某网站JS逆向分析(JS逆向第六期)_json_10



6. 打印此函数的入参和出参如下图所示:

某某网站JS逆向分析(JS逆向第六期)_断点调试_11


调试总结:从上图可以查看到加密前的参数及加密后的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 json


import execjs
import requests




class 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. 打印结果如下:

某某网站JS逆向分析(JS逆向第六期)_ide_12


算法还原流程到此就结束了,欢迎大家提问!


四、思路总结


确定参数 > 断点调试 > 加密定位 > 算法还原 > 代码测试 > 验证结果 > 完毕

    我是TheWeiJun,有着执着的追求,信奉终身成长,不定义自己,热爱技术但不拘泥于技术,爱好分享,喜欢读书和乐于结交朋友,欢迎加我微信与我交朋友。

    分享日常学习中关于爬虫、逆向和分析的一些思路,文中若有错误的地方,欢迎大家多多交流指正☀️  


某某网站JS逆向分析(JS逆向第六期)_ide_13



举报

相关推荐

0 条评论