Files
TankIC/sockets/ws_hook.js
T
2026-07-28 01:16:15 +08:00

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) 导出单个消息');
})();