工具链(3DTank/tools/):extract(模块提取)、mine_evidence/apply_names(命名证据与别名回填)、extract_codec_fields(字段表 + 全库槽位类型 + PrimClassWire/ResourceClasses)、mine_downlink(下行字段表)、extract_space_init/rebase_space_init/audit_space_init(空间模板与记录切分审计)、extract_space_session(把真实会话切成按上行触发的回放模板)、pair_flow(请求→应答配对)、probe_containers、ingest/decrypt_frames(抓包入库与解密)、serve/fetch_assets(本地站点镜像)、import_db(抓包对象仓库导入 MySQL)。 服务器(3DTank/tools/server/):link/entry/codec/container/dispatch/world/space/semantic(协议层与 lt/pt/zt 三个容器布局)、session(会话回放,顺序推进)、db + schema.sql(MySQL 连接层与对象仓库表结构)、store/responder(查库组帧与定向应答;接管范围默认只开 ba.lcr)。 同时修 pair_flow 的时间轴偏移:timeline_c2s 与 timeline_s2c 的 0 点相差 144~4453ms,改前配对整体偏约 2 秒(实测 space-100073070 偏 2084ms),改后 22.4% 的触发者被纠正。 src/、src_named/、packets/、3DTank/local/(含数据库口令)不进库,可用上述工具重新生成。
85 lines
2.9 KiB
JavaScript
85 lines
2.9 KiB
JavaScript
// wss_test.js - 本地中继自测客户端:验证「明文 → 加密」实时解码链路
|
||
// 用法: node wss_test.js [--url ws://localhost:9091/] [--key 42]
|
||
// 流程: 先发一条明文帧(应被识别为明文),再发 3 条用同一密钥加密的帧,
|
||
// 观察中继日志应出现「明文段结束 → 密钥已破解 K=42 → [密] 命令 ID=...」
|
||
const path = require('path');
|
||
|
||
const ROOT = path.resolve(__dirname, '..', '..');
|
||
function RequireWs() {
|
||
try {
|
||
return require('ws');
|
||
} catch (Err) {
|
||
return require(path.join(ROOT, 'sockets', 'node_modules', 'ws'));
|
||
}
|
||
}
|
||
const Ws = RequireWs();
|
||
|
||
const Argv = process.argv.slice(2);
|
||
const Arg = (Name, Def) => {
|
||
const I = Argv.indexOf('--' + Name);
|
||
return I >= 0 && Argv[I + 1] ? Argv[I + 1] : Def;
|
||
};
|
||
const URL = Arg('url', 'ws://localhost:9091/');
|
||
const KEY = Number(Arg('key', '42'));
|
||
|
||
function EncodeFrame(Payload, Nullmap) {
|
||
const Nm = Nullmap || Buffer.from([0x00]);
|
||
const Length = Nm.length + Payload.length;
|
||
if (Length < 0x4000) return Buffer.concat([Buffer.from([(Length >> 8) & 0xff, Length & 0xff]), Nm, Payload]);
|
||
return Buffer.concat([
|
||
Buffer.from([((Length >>> 24) & 0xff) | 0x80, (Length >>> 16) & 0xff, (Length >>> 8) & 0xff, Length & 0xff]),
|
||
Nm,
|
||
Payload,
|
||
]);
|
||
}
|
||
|
||
// 与中继侧同构的自变体 XOR 流密码(编码方向:密钥表再 ^87)
|
||
class Cipher {
|
||
constructor(K) {
|
||
this.Slot = new Int32Array(8);
|
||
for (let I = 0; I < 8; I++) this.Slot[I] = ((K ^ (I << 3)) ^ 87) & 0xff;
|
||
this.Idx = 0;
|
||
}
|
||
Apply(Buf) {
|
||
const Out = Buffer.alloc(Buf.length);
|
||
for (let I = 0; I < Buf.length; I++) {
|
||
const Plain = Buf[I] & 0xff;
|
||
Out[I] = (Plain ^ this.Slot[this.Idx]) & 0xff;
|
||
this.Slot[this.Idx] = Plain;
|
||
this.Idx = this.Idx ^ (Plain & 7);
|
||
}
|
||
return Out;
|
||
}
|
||
}
|
||
|
||
const Socket = new Ws(URL);
|
||
Socket.binaryType = 'nodebuffer';
|
||
|
||
Socket.on('open', () => {
|
||
// 1) 明文帧:命令 ID=1(Hash Request 形态,内容随意)
|
||
const PlainFrame = EncodeFrame(Buffer.concat([Buffer.from([1]), Buffer.from('0000000005f691cc', 'hex')]), Buffer.from([0x00]));
|
||
Socket.send(PlainFrame);
|
||
console.log('已发送明文帧 ' + PlainFrame.length + ' 字节');
|
||
// 2) 加密帧:命令 ID=7 / 19 / 42,用同一密钥
|
||
const Cipher1 = new Cipher(KEY);
|
||
for (const Id of [7, 19, 42]) {
|
||
const Payload = Buffer.concat([Buffer.from([Id]), Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22])]);
|
||
Socket.send(Cipher1.Apply(EncodeFrame(Payload, Buffer.from([0x00]))));
|
||
}
|
||
console.log('已发送 3 条加密帧(密钥 K=' + KEY + ')');
|
||
});
|
||
|
||
Socket.on('message', (Data) => console.log('<<< ' + Buffer.from(Data).length + ' 字节'));
|
||
Socket.on('close', (Code) => {
|
||
console.log('连接关闭 code=' + Code);
|
||
process.exit(0);
|
||
});
|
||
Socket.on('error', (Err) => {
|
||
console.log('错误: ' + Err.message);
|
||
process.exit(1);
|
||
});
|
||
setTimeout(() => {
|
||
Socket.close();
|
||
process.exit(0);
|
||
}, 4000);
|