Fix: reissue client_id on clone collision and harden V1 file-transfer command

撞 ID 的克隆机(同镜像 MachineGuid 相同导致 client_id 相同)此前会被服务端暂存为
替补、永远无法转正,从而不显示在主机列表。现在服务端检测到「client_id 相同但主机名
不同」时,主动下发 COMMAND_REGEN_CLIENT_ID,客户端在旧 ID 基础上掺随机 UUID 重算后
重连,以新 ID 正常上线;触发条件仅看主机名,排除 IP 漂移与纯用户名变化,避免误伤
正常重连的机器。

同时加固 V1 服务端→客户端文件传输:COMMAND_GET_FILE 指令的一次性 md5 令牌绑定登记
时的客户端源 IP(PutCmd/PopCmd 均带 peerIP),防止异常客户端冒用;并对指令包增加边界
校验(len 与 NUL 结尾),杜绝无 NUL 包导致的越界读。

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-17 09:29:33 +02:00
1 parent d3a339247b
commit e6b4fe3a85
7 files changed
+96 -13

No files matched your search

+12
View File
@@ -838,6 +838,18 @@ VOID CKernelManager::OnReceive(PBYTE szBuffer, ULONG ulLength)
m_cfg->SetStr("settings", "group_name", group);
break;
}
case COMMAND_REGEN_CLIENT_ID: {
// 撞 ID 的克隆机:服务端已判定「主机名不同却撞 client_id」,让本机重生成 ID。
// 新 ID = 旧 ID + 随机 UUID 再哈希,写回注册表后主动断开主连接,重连循环
// 会用新 ID 重新登录,脱离撞 ID 状态(脱离替补、正常显示在主机列表)。
uint64_t newID = RegenerateClientID(m_conn->clientID);
m_cfg->SetStr("settings", "client_id", std::to_string(newID));
Mprintf("[regen-client-id] 重生成 client_id: %llu -> %llu,即将重连\n",
m_conn->clientID, newID);
if (m_ClientObject)
m_ClientObject->Disconnect();
break;
}
case COMMAND_TLS_ENABLE: {
// 服务端下发信任锚证书:payload = PEM 正文(szBuffer+1 .. ulLength-1)。
+29
View File
@@ -289,6 +289,35 @@ uint64_t CalcalateIDv2(const std::string& machineGuid, const std::string& normal
return XXH64(s.c_str(), s.length(), 0);
}
// 撞 ID 的克隆机重生成 client_id:在旧 ID 基础上掺一个一次性随机 UUID 再哈希。
// 只在服务端判定「主机名不同却撞 ID」后下发 COMMAND_REGEN_CLIENT_ID 触发,不影响
// 正常(首次)登录的 CalcalateIDv2 算法;唯一性由随机 UUID 保证,同批克隆机各自
// 随机,重算后必然不同。
uint64_t RegenerateClientID(uint64_t oldID)
{
// 16 字节随机数充当一次性 UUID(RtlGenRandom = advapi32 SystemFunction036)。
unsigned char rnd[16] = {};
if (!RtlGenRandom(rnd, (ULONG)sizeof(rnd))) {
// 极少数环境 RtlGenRandom 可能失败:退化为时间/进程/线程/性能计数混合的 LCG,
// 保证仍能产出与旧 ID 不同的新 ID(不追求密码学强度,只需撞车概率足够低)。
uint64_t mix = GetTickCount64() ^ ((uint64_t)GetCurrentProcessId() << 32)
^ (uint64_t)GetCurrentThreadId();
LARGE_INTEGER pc;
QueryPerformanceCounter(&pc);
mix ^= (uint64_t)pc.QuadPart;
for (int i = 0; i < 16; ++i) {
mix = mix * 6364136223846793005ULL + 1442695040888963407ULL;
rnd[i] = (unsigned char)(mix >> 56);
}
}
char hex[33];
for (int i = 0; i < 16; ++i)
sprintf_s(hex + i * 2, 3, "%02X", (unsigned)rnd[i]);
hex[32] = '\0';
std::string seed = std::to_string(oldID) + "|" + hex;
return XXH64(seed.c_str(), seed.size(), 0);
}
BOOL IsAuthKernel(std::string &str) {
BOOL isAuthKernel = FALSE;
std::string pid = std::to_string(GetCurrentProcessId());
+1
View File
@@ -7,6 +7,7 @@
std::string GetMachineGuidWindows();
uint64_t CalcalateIDv2(const std::string& machineGuid, const std::string& normalizedPath, bool isAuth = false);
uint64_t RegenerateClientID(uint64_t oldID);
BOOL IsAuthKernel(std::string& str);
LOGIN_INFOR GetLoginInfo(DWORD dwSpeed, CONNECT_ADDRESS &conn, const std::string& expiredDate, bool isAuth);
DWORD CPUClockMHz();