196 lines
6.5 KiB
JavaScript
196 lines
6.5 KiB
JavaScript
// ============================================================
|
|
// WebSocket 流量捕获注入脚本
|
|
// ============================================================
|
|
// 在游戏加载前注入,捕获所有 WebSocket 通信
|
|
// 数据保存到 window.__ws_capture__ 数组
|
|
// 使用 window.__download_ws_capture__() 下载捕获数据
|
|
// ============================================================
|
|
|
|
(function () {
|
|
if (window.__ws_hooked__) return;
|
|
window.__ws_hooked__ = true;
|
|
|
|
window.__ws_capture__ = [];
|
|
var captureDir = 'ws_capture_' + Date.now();
|
|
|
|
function toHex(buffer) {
|
|
var bytes = new Uint8Array(buffer);
|
|
var hex = [];
|
|
for (var i = 0; i < bytes.length; i += 16) {
|
|
var chunk = bytes.slice(i, Math.min(i + 16, bytes.length));
|
|
var hexPart = Array.from(chunk).map(function (b) {
|
|
return b.toString(16).padStart(2, '0');
|
|
}).join(' ');
|
|
var asciiPart = Array.from(chunk).map(function (b) {
|
|
return (b >= 32 && b < 127) ? String.fromCharCode(b) : '.';
|
|
}).join('');
|
|
hex.push(i.toString(16).padStart(8, '0') + ' ' + hexPart.padEnd(48) + ' ' + asciiPart);
|
|
}
|
|
return hex.join('\n');
|
|
}
|
|
|
|
function toArrayBuffer(data) {
|
|
if (data instanceof ArrayBuffer) return data;
|
|
if (ArrayBuffer.isView(data)) return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
if (typeof data === 'string') return new TextEncoder().encode(data).buffer;
|
|
return data;
|
|
}
|
|
|
|
// 保存原始 WebSocket
|
|
var OriginalWebSocket = window.WebSocket;
|
|
|
|
function HookedWebSocket(url, protocols) {
|
|
var ws = protocols ? new OriginalWebSocket(url, protocols) : new OriginalWebSocket(url);
|
|
|
|
console.log('[WS Hook] 连接:', url);
|
|
|
|
var wsId = window.__ws_capture__.length;
|
|
var wsInfo = {
|
|
id: wsId,
|
|
url: url,
|
|
openTime: null,
|
|
messages: []
|
|
};
|
|
window.__ws_capture__.push(wsInfo);
|
|
|
|
// Hook send
|
|
var originalSend = ws.send.bind(ws);
|
|
ws.send = function (data) {
|
|
var ab = toArrayBuffer(data);
|
|
var msgInfo = {
|
|
direction: 'send',
|
|
time: Date.now(),
|
|
size: ab.byteLength,
|
|
hex: toHex(ab),
|
|
buffer: ab
|
|
};
|
|
wsInfo.messages.push(msgInfo);
|
|
console.log('[WS Hook] #' + wsId + ' >>> 发送 ' + ab.byteLength + ' 字节:');
|
|
console.log(toHex(ab));
|
|
return originalSend(data);
|
|
};
|
|
|
|
// Hook onmessage
|
|
var originalOnMessage = null;
|
|
Object.defineProperty(ws, 'onmessage', {
|
|
get: function () { return originalOnMessage; },
|
|
set: function (fn) {
|
|
originalOnMessage = fn;
|
|
// Wrap the handler
|
|
ws.addEventListener('message', function (event) {
|
|
var ab = toArrayBuffer(event.data);
|
|
var msgInfo = {
|
|
direction: 'recv',
|
|
time: Date.now(),
|
|
size: ab.byteLength,
|
|
hex: toHex(ab),
|
|
buffer: ab
|
|
};
|
|
wsInfo.messages.push(msgInfo);
|
|
console.log('[WS Hook] #' + wsId + ' <<< 接收 ' + ab.byteLength + ' 字节:');
|
|
console.log(toHex(ab));
|
|
});
|
|
}
|
|
});
|
|
|
|
// Also hook addEventListener for 'message'
|
|
var originalAddEventListener = ws.addEventListener.bind(ws);
|
|
ws.addEventListener = function (type, listener, options) {
|
|
if (type === 'message') {
|
|
var wrappedListener = function (event) {
|
|
var ab = toArrayBuffer(event.data);
|
|
var msgInfo = {
|
|
direction: 'recv',
|
|
time: Date.now(),
|
|
size: ab.byteLength,
|
|
hex: toHex(ab),
|
|
buffer: ab
|
|
};
|
|
wsInfo.messages.push(msgInfo);
|
|
console.log('[WS Hook] #' + wsId + ' <<< 接收 ' + ab.byteLength + ' 字节:');
|
|
console.log(toHex(ab));
|
|
listener(event);
|
|
};
|
|
return originalAddEventListener(type, wrappedListener, options);
|
|
}
|
|
return originalAddEventListener(type, listener, options);
|
|
};
|
|
|
|
ws.addEventListener('open', function () {
|
|
wsInfo.openTime = Date.now();
|
|
console.log('[WS Hook] #' + wsId + ' 连接成功');
|
|
});
|
|
|
|
ws.addEventListener('close', function (event) {
|
|
console.log('[WS Hook] #' + wsId + ' 连接关闭 (code=' + event.code + ')');
|
|
});
|
|
|
|
ws.addEventListener('error', function (event) {
|
|
console.log('[WS Hook] #' + wsId + ' 连接错误:', event);
|
|
});
|
|
|
|
return ws;
|
|
}
|
|
|
|
// 复制静态属性
|
|
HookedWebSocket.CONNECTING = OriginalWebSocket.CONNECTING;
|
|
HookedWebSocket.OPEN = OriginalWebSocket.OPEN;
|
|
HookedWebSocket.CLOSING = OriginalWebSocket.CLOSING;
|
|
HookedWebSocket.CLOSED = OriginalWebSocket.CLOSED;
|
|
HookedWebSocket.prototype = OriginalWebSocket.prototype;
|
|
|
|
window.WebSocket = HookedWebSocket;
|
|
|
|
// 下载捕获数据
|
|
window.__download_ws_capture__ = function () {
|
|
var data = window.__ws_capture__.map(function (ws) {
|
|
return {
|
|
id: ws.id,
|
|
url: ws.url,
|
|
openTime: ws.openTime,
|
|
messages: ws.messages.map(function (m) {
|
|
// Convert buffer to base64 for JSON serialization
|
|
var bytes = new Uint8Array(m.buffer);
|
|
var binary = '';
|
|
for (var i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
return {
|
|
direction: m.direction,
|
|
time: m.time,
|
|
size: m.size,
|
|
hex: m.hex,
|
|
base64: btoa(binary)
|
|
};
|
|
})
|
|
};
|
|
});
|
|
|
|
var blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
var url = URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'ws_capture_' + Date.now() + '.json';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
console.log('[WS Hook] 已下载捕获数据,共 ' + data.length + ' 个连接');
|
|
};
|
|
|
|
// 导出单个消息的二进制
|
|
window.__export_ws_binary__ = function (wsId, msgIdx) {
|
|
var ws = window.__ws_capture__[wsId];
|
|
if (!ws) { console.log('未找到 WS #' + wsId); return; }
|
|
var msg = ws.messages[msgIdx];
|
|
if (!msg) { console.log('未找到消息 #' + msgIdx); return; }
|
|
var blob = new Blob([msg.buffer], { type: 'application/octet-stream' });
|
|
var url = URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = msg.direction + '_' + wsId + '_' + msgIdx + '.bin';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
};
|
|
|
|
console.log('[WS Hook] WebSocket 捕获脚本已加载');
|
|
console.log('[WS Hook] 使用 __download_ws_capture__() 下载所有捕获数据');
|
|
console.log('[WS Hook] 使用 __export_ws_binary__(wsId, msgIdx) 导出单个消息');
|
|
})();
|