本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关.本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责。
网址:
aHR0cHM6Ly9tYXNoYW5ncGEuY29tL3Byb2JsZW0tZGV0YWlsLzcv
如有错误,还请各位坛友指出。
一、定位并确定要分析的内容
先F12打开devtools,点击网络选项卡,附图:

定位.png (101.28 KB, 下载次数: 4)
下载附件
2025-8-18 12:42 上传
若是没有数据,可以去网页中刷新一下
然后定位

定位2.png (111.39 KB, 下载次数: 4)
下载附件
2025-8-18 12:44 上传
然后寻找要分析的数据

确定.png (31.74 KB, 下载次数: 4)
下载附件
2025-8-18 12:45 上传

确定2.png (14.36 KB, 下载次数: 3)
下载附件
2025-8-18 12:45 上传

确定3.png (14.78 KB, 下载次数: 4)
下载附件
2025-8-18 12:45 上传
好,这些都确定好以后就可以开始分析了,但是这无限debugger影响后续断点分析,所以进入步骤二
二、过无限debugger
当F12打开devtools时出现了无限debugger,
先通过调用堆栈找patch的位置

过无限debugger.png (51.56 KB, 下载次数: 4)
下载附件
2025-8-18 12:46 上传

过无限debugger2.png (12.67 KB, 下载次数: 4)
下载附件
2025-8-18 12:46 上传
很明显,应该重写掉Function 构造器,把带 debugger 的函数变成空函数
方法见图:

写代码段方法.png (29.83 KB, 下载次数: 4)
下载附件
2025-8-18 12:47 上传

写代码段方法2.png (11.95 KB, 下载次数: 5)
下载附件
2025-8-18 12:47 上传
然后复制一下代码(可以找AI生成):
[JavaScript] 纯文本查看 复制代码// 覆盖 Function 构造器,把带 debugger 的函数变成空函数
const OriginalFunction = window.Function;
window.Function = function (...args) {
const body = args[args.length - 1] || '';
console.log(args);
if (typeof body === 'string' && body.includes('debugger')) {
args[args.length - 1] = 'return undefined;';
}
return OriginalFunction.apply(this, args);
};
然后按ctrl+enter运行该代码段,再按F8,就会发现无限debugger
已经成功过掉了
三、解混淆并分析
方法:先将某一完整函数复制到代码编辑器中,再利用替换功能将 如ym替换为y(const ym = y)。
然后将 如Oc(0x2c1, '3@lG')放到 控制台上运行出结果(如这个的结果就为'em-detail/')并进行替换
(不知道诸位大佬是怎么做的,我这个有点麻烦)
1、解混淆
这里提供已经处理好的代码(error那里的If语句其实是没用的,它永远只会执行else中的)
[JavaScript] 纯文本查看 复制代码function loadPage(W) {//W=pagenumber
const ym = y
, yh = Oc
, yH = R4
, yQ = R4
, M = {
'iEIcs': function(x, I) {
return x(I);
},
'ITqcR': 'vHLVu',
'lddCe': 'tywea',
'KOFiU': 'Error fetching\x20problem\x20details:',
'syPwG': 'GET',
'aGcCj': 'json'
}
, O = {};
O['page'] = W;
const B = O
, Y = new URLSearchParams(B)['toString']();
$['ajax']({
'url': '/api/problem-detail/7/data/?' + Y,
'method': 'GET',
'dataType': 'json',
'success': function(x) {//更新界面
updatePageContent(x);
},
'error': function(x, I, w) {
console['error']('Error fetching\x20problem\x20details:', I, w);
});
}
对'url': '/api/problem-detail/7/data/?' + Y,下断点,发现W=pagenumber,Y="page=1"
发现这并没有上面要分析的内容
这怎么回事呢?
通过查看代码,发现其实是有提示的(就在loadPage的上面)

提示.png (59.93 KB, 下载次数: 3)
下载附件
2025-8-18 12:47 上传
控制台中运行OU(0x228, 'W)[u') + 'eIntercept' + 'or',得到'addResponseInterceptor'
这个函数上面那个得到addRequestInterceptor
同样的方法对这两个函数解混淆(有些不确定的参数就下断点后刷新网页查看)
[JavaScript] 纯文本查看 复制代码$['addRequestInterceptor'](function(N) {
const W = {
'QCqSI': function(B, Y) {
return B + Y;
},
'uSzcs': function(B, Y) {
return B(Y);
},
'KjfHo': 'xxoo'
};
let M = new Date()['getTime']()
, O = window['eeee']('xialuo' + M);//md5
return N['headers'] = N['headers'] || {},
N['headers']['m'] = O,//N['headers']['m'] = md5('xialuo' + M)
N['headers']['ts'] = M,
N['url'] += '&x=' + encodeURIComponent(CryptoJS['SHA256'](O+'xxoo')),//dd["a"]就是CryptoJS,原文件中有这个定义
N;
}),
[JavaScript] 纯文本查看 复制代码$['addResponseInterceptor'](function(N, W, M) {//N为响应体,W为响应状态(W: "success"), M为响应对象
const O = {
'oBkKu': function(B, Y) {
return B(Y);
},
'vTVrB': function(B, Y) {
return B + Y;
},
'ZKPee': function(B, Y) {
return B + Y;
},
'icMcT': 'Function(arguments[0]+"',
'dCZBj': '\x22)()',
'rfDiy': function(B, Y) {
return B === Y;
},
'QaFPy': function(B, Y) {
return B === Y;
},
'zoxoK': 'success',
'mpQsI': 'KgyiJ'
};
if (O['rfDiy'](M['statusText'], 'OK') || O['QaFPy'](M['statusText'], O['zoxoK'])) {//成功
const B = O['oBkKu'](xxxxoooo, N['r'])//xxxxoooo(N['r'])
, Y = x1['parse'](B);
return Y;
} else
return O['mpQsI'] !== 'qXkuF' ? N : function(I) {
return O['oBkKu'](B, O['vTVrB'](O['ZKPee'](O['icMcT'], I), O['dCZBj']));
}(O);
}));
2、分析addRequestInterceptor:
发现
O = window['eeee']('xialuo' + M)
N['headers']['m'] = O
而m是4f71501042d2261fc28586a63d564f03,刚好32位(我这里直接猜测是Md5)
去控制台中验证一下,window['eeee']("1")刚好是1的md5,猜测正确
如果没有猜到的话,可以在控制台中运行window['eeee']定位到window['eeee']的定义,然后再分析(我这里就不分析了),附图:

验证是否为md5(控制台定位函数位置).png (98.02 KB, 下载次数: 3)
下载附件
2025-8-18 12:48 上传
而x使用了SHA256加密,TS就是毫秒级时间戳
3、分析addResponseInterceptor:
同样,解混淆其中用到的xxxxoooo
[JavaScript] 纯文本查看 复制代码let kkkk = dd['a'][Oc(0x22d, 'Fi6K')][R4(0x2a6)][y(0x16c, '!mtX')]('xxxxxxxxoooooooo')
, iiii = dd['a'][Oc(0x190, 's2]y')]['Utf8'][R4(0x14a)]('0123456789ABCDEF');
function xxxxoooo(M) {
const O = {};
O['SZjeT'] = function(w, A) {
return w + A;
}
,
O['YRYfz'] = 'ypt';
const B = O;
let Y = CryptoJS['enc']['Hex']['parse'](M);
const x = {};
x['ciphertext'] = Y;
let I = CryptoJS['AES']['decrypt'](x, kkkk, {
'mode': CryptoJS['mode']['CBC'],
'padding': CryptoJS['pad']['Pkcs7'],
'iv': iiii
});
return I['toString'](CryptoJS['enc']['Utf8']);
}
然后换为python:
[Python] 纯文本查看 复制代码def xxxxoooo(M: str) -> str:
key = b'xxxxxxxxoooooooo'# 16 字节 = AES-128
iv = b'0123456789ABCDEF'
cipher_bytes = binascii.unhexlify(M)
cipher = AES.new(key, AES.MODE_CBC, iv)
plain = unpad(cipher.decrypt(cipher_bytes), AES.block_size)
return plain.decode('utf-8')
四、代码及结果展示
[Python] 纯文本查看 复制代码import time, json, requests, sys
from base64 import b64decode
from Crypto.Cipher import AES# need pip install pycryptodome
from Crypto.Util.Padding import pad, unpad
import binascii, hashlib, urllib.parse
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
'Accept': "application/json, text/javascript, */*; q=0.01",
'Accept-Encoding': "gzip, deflate, br, zstd",
'ts': "1755485636981",
'sec-ch-ua-platform': "\"Windows\"",
'sec-ch-ua': "\"Not;A=Brand\";v=\"99\", \"Google Chrome\";v=\"139\", \"Chromium\";v=\"139\"",
'sec-ch-ua-mobile': "?0",
'm': "fd57ed91587d2779cb67c7daadd05003",
'x-requested-with': "XMLHttpRequest",
'sec-fetch-site': "same-origin",
'sec-fetch-mode': "cors",
'sec-fetch-dest': "empty",
'referer': "https://mashangpa.com/problem-detail/7/",
'accept-language': "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6",
'priority': "u=1, i",
'Cookie': "xxx"
}
def decrypt(M: str) -> str:
key = b'xxxxxxxxoooooooo'# 16 字节 = AES-128
iv = b'0123456789ABCDEF'
cipher_bytes = binascii.unhexlify(M)
cipher = AES.new(key, AES.MODE_CBC, iv)
plain = unpad(cipher.decrypt(cipher_bytes), AES.block_size)
return plain.decode('utf-8')
if __name__ == '__main__':
for i in range(1, 21):
#print(i)
_ts=int(time.time()*1000)
headers['ts']=str(_ts)
headers['m']=hashlib.md5(b"xialuo" + str(_ts).encode()).hexdigest()
x=urllib.parse.quote(hashlib.sha256((headers['m']+'xxoo').encode('utf-8')).hexdigest(), safe='')
url = f"https://mashangpa.com/api/problem-detail/7/data/?page={i}&x={x}"
#response = requests.get(url, headers=headers, verify=False)
response = requests.get(url, headers=headers)
for number in json.loads(decrypt(response.json()['r']))["current_array"]:
num+=number
print(num)
结果图:

成功1.png (11.63 KB, 下载次数: 3)
下载附件
2025-8-18 12:49 上传

成功2.png (165.78 KB, 下载次数: 4)
下载附件
2025-8-18 12:49 上传
成功!!!
最后,留一下js文件
js文件.zip
(55.37 KB, 下载次数: 4)
2025-8-18 13:15 上传
点击文件名下载附件
下载积分: 吾爱币 -1 CB