864 字
2 分钟
某音乐解密流程
浏览器控制台日志爆炸问题解决方案
主要是针对某音乐解密那一类的,密钥buffer几万的长度,解密的过程中就能把浏览器爆炸了。
解密没啥好说的,拿到日志就行,他就是固定的密钥,然后长度是21位。
用buffer里的数字对长度取模拿到索引值,然后做异或操作得到真实数据。
我们主要来聊一下日志的问题吧。
问题背景
在 JS 逆向分析过程中,对 VM/混淆代码进行插桩时,日志量可能达到数万甚至数十万条。直接在浏览器控制台输出会导致:
- 浏览器卡顿/死机:console.log 输出过多,浏览器渲染线程被阻塞
- 内存溢出:大量日志堆积在控制台缓冲区
- 调试器无响应:DevTools 无法处理海量日志
核心解决方案:WebSocket 远程日志传输
将日志从浏览器实时传输到本地 Python 服务器,避免浏览器内存占用。
架构设计
┌─────────────────┐ WebSocket ┌─────────────────┐│ 浏览器插桩代码 │ ──────────────────► │ Python 日志服务器 ││ (sign插桩.js) │ 顺序发送日志 │ (log_server_ws.py) │└─────────────────┘ └─────────────────┘ │ │ ▼ ▼ console.log(少量) 批量写入 vm_logs.txt服务端实现(Python)
关键代码
import asyncioimport websockets
LOG_FILE = "vm_logs.txt"BATCH_SIZE = 100 # 每 100 条批量写入
async def handle_log(websocket): batch = [] try: with open(LOG_FILE, 'a', encoding='utf-8') as f: async for message in websocket: batch.append(message)
# 批量写入优化:减少 IO 次数 if len(batch) >= BATCH_SIZE: f.write('\n'.join(batch) + '\n') f.flush() batch = []
# 发送确认,保证顺序 await websocket.send("ok") except websockets.exceptions.ConnectionClosed: if batch: with open(LOG_FILE, 'a', encoding='utf-8') as f: f.write('\n'.join(batch) + '\n')
async with websockets.serve(handle_log, "127.0.0.1", 9999): await asyncio.Future()服务端启动
python log_server_ws.py浏览器端实现
核心策略
- 限制日志总量:设置
MAX_LOGS上限 - 顺序发送:每条日志发送后等待服务端确认,再发送下一条
- 队列缓冲:网络延迟时使用队列暂存
- 重连机制:连接断开后自动重连
关键代码
const MAX_LOGS = 5000; // 限制最多输出 5000 条const WS_SERVER = 'ws://127.0.0.1:9999';
let wsSocket = null;let wsQueue = [];let wsConnected = false;let logCounter = 0;
function initWebSocket() { wsSocket = new WebSocket(WS_SERVER);
wsSocket.onopen = () => { wsConnected = true; flushWsQueue(); // 发送队列中的日志 };
wsSocket.onmessage = (event) => { if (event.data === 'ok') { wsQueue.shift(); // 确认后移除已发送的日志 flushWsQueue(); // 发送下一条 } };}
function vmLog(...args) { if (logCounter >= MAX_LOGS) return; // 限制日志数量
const logLine = `[${++logCounter}] ${args.join(' ')}`;
// 方式1:少量控制台输出(可选) // console.log(logLine);
// 方式2:WebSocket 发送到服务端 wsQueue.push(logLine); if (wsConnected) flushWsQueue();}
function flushWsQueue() { if (!wsConnected || wsQueue.length === 0) return; wsSocket.send(wsQueue[0]); // 只发送第一条,等待确认}完整配置流程
步骤 1:启动日志服务器
cd "D:\大胖狗的学习\逆向\胖狗逆向集\扣扣音乐纯算"python log_server_ws.py步骤 2:在浏览器控制台运行插桩代码
确保插桩代码中 USE_WS = true,浏览器会自动连接到 ws://127.0.0.1:9999。
步骤 3:触发目标函数
执行加密/解密操作,日志会自动传输到服务端并写入 vm_logs.txt。
步骤 4:查看日志文件
# 实时查看日志tail -f vm_logs.txt
# 搜索关键信息grep "key" vm_logs.txt关键优化点
1. 日志数量限制
const MAX_LOGS = 5000;
function vmLog(...args) { if (logCounter >= MAX_LOGS) return; // 达到上限后停止输出 // ...}2. 数组内容完整显示
当需要查看完整密钥数组时,修改 pretty 函数:
function pretty(value) { if (Array.isArray(value)) { // 完整显示所有元素,不截断 return '[' + value.map(x => typeof x === 'number' ? x : pretty(x)).join(', ') + ']'; } // ...}3. 批量写入优化
服务端每 100 条写入一次文件,减少磁盘 IO:
if len(batch) >= BATCH_SIZE: f.write('\n'.join(batch) + '\n') f.flush() batch = []4. 顺序保证
使用确认机制确保日志顺序正确:
wsSocket.onmessage = (event) => { if (event.data === 'ok') { wsQueue.shift(); flushWsQueue(); }};常见问题
Q1: WebSocket 连接失败
- 确保 Python 服务器已启动
- 检查端口是否被占用(默认 9999)
- 浏览器控制台查看连接状态
Q2: 日志不完整
- 检查
MAX_LOGS是否设置过低 - 确保页面关闭前等待日志发送完毕
- 服务端会在连接断开时写入剩余日志
Q3: 日志顺序混乱
- 确保每条日志发送后等待确认
- 不要并发发送多条日志
Q4: 浏览器仍然卡顿
- 关闭
console.log输出,仅保留 WebSocket 传输 - 进一步降低
MAX_LOGS值 - 使用更轻量的日志格式
对比方案
| 方案 | 优点 | 缺点 |
|---|---|---|
| 直接 console.log | 简单 | 日志爆炸,浏览器卡死 |
| WebSocket 传输 | 无内存压力,可存储 | 需要启动服务端 |
| 本地文件下载 | 无需服务端 | 需要等待执行完成才能下载 |
| 内存缓存后下载 | 简单 | 大量日志导致内存溢出 |
推荐配置
针对不同场景的配置建议:
快速调试(少量日志)
const MAX_LOGS = 1000;// 仅控制台输出,不启用 WebSocket详细分析(中等日志)
const MAX_LOGS = 5000;// 启用 WebSocket,控制台同步输出完整追踪(大量日志)
const MAX_LOGS = 50000;// 启用 WebSocket,关闭控制台输出// 服务端批量写入部分信息可能已经过时









