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:
1 parent
d3a339247b
commit
e6b4fe3a85
7 files changed
+96
-13
No files matched your search
@@ -1771,6 +1771,16 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
|
||||
" 已在线: 主机名=%s IP=%s 用户名=%s\n",
|
||||
id, strPCName.GetString(), data[ONLINELIST_IP].GetString(), newUser.GetString(),
|
||||
oldName.GetString(), oldIP.GetString(), oldUser.GetString());
|
||||
// 主机名不同 = 确定是不同物理机(克隆机撞 ID,而非同一台的 IP 漂移/重连)。
|
||||
// 主动下发「重生成 client_id」命令:克隆机掺随机 UUID 重算后重连,以新 ID
|
||||
// 重新上线脱离撞 ID 状态。纯 IP 不同、纯用户名不同不下发,避免误伤正常重连
|
||||
// 的机器(其 ID 合法,重生成反而丢备注/授权/历史)。
|
||||
if (strPCName.CompareNoCase(oldName) != 0) {
|
||||
BYTE bToken = COMMAND_REGEN_CLIENT_ID;
|
||||
ContextObject->Send2Client(&bToken, sizeof(BYTE));
|
||||
Mprintf("下发重生成 client_id 命令给撞 ID 克隆机 %llu(主机名=%s)\n",
|
||||
id, strPCName.GetString());
|
||||
}
|
||||
}
|
||||
Mprintf("上线消息 - 主机重复上线,暂存替补 [2]: %llu. IP= %s. Path= %s\n", id, data[ONLINELIST_IP], path);
|
||||
// 网络抖动导致客户端用新连接重复上线:这里不立即替换/关闭旧连接,
|
||||
@@ -6108,15 +6118,33 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
||||
}
|
||||
case COMMAND_GET_FILE: {
|
||||
// 发送文件
|
||||
std::string dir = (char*)(szBuffer + 1);
|
||||
char* ptr = (char*)szBuffer + 1 + dir.length() + 1;
|
||||
auto md5 = CalcMD5FromBytes((BYTE*)ptr, len - 2 - dir.length());
|
||||
if (!m_CmdList.PopCmd(md5)) {
|
||||
// 边界校验:dir 必须在 len 内以 NUL 结尾,否则 std::string 越界读,且
|
||||
// len - 2 - dir.length() 会下溢导致后续越界读。异常/恶意客户端可能构造无 NUL 的包。
|
||||
if (len < 2) {
|
||||
Mprintf("【警告】文件传输指令包过短: %u\n", len);
|
||||
ContextObject->CancelIO();
|
||||
break;
|
||||
}
|
||||
const char* pDir = (const char*)(szBuffer + 1);
|
||||
const char* pDirEnd = (const char*)memchr(pDir, '\0', len - 1);
|
||||
if (!pDirEnd) {
|
||||
Mprintf("【警告】文件传输指令 dir 未以 NUL 结尾,丢弃\n");
|
||||
ContextObject->CancelIO();
|
||||
break;
|
||||
}
|
||||
size_t dirLen = pDirEnd - pDir;
|
||||
size_t listLen = (len - 1) - dirLen - 1; // 文件列表字节数,已由 memchr 保证 >= 0
|
||||
std::string dir(pDir, dirLen);
|
||||
const char* ptr = pDirEnd + 1;
|
||||
auto md5 = CalcMD5FromBytes((const BYTE*)ptr, (DWORD)listLen);
|
||||
if (!m_CmdList.PopCmd(md5, ContextObject->GetPeerName())) {
|
||||
Mprintf("【警告】文件传输指令非法或已过期: %s\n", md5.c_str());
|
||||
ContextObject->CancelIO();
|
||||
break;
|
||||
}
|
||||
auto files = *ptr ? ParseMultiStringPath(ptr, len - 2 - dir.length()) : std::vector<std::string> {};
|
||||
// listLen==0 时 ptr 可能越过缓冲区末尾(空文件列表无独立结尾 NUL),先判 listLen>0
|
||||
// 再解引用 *ptr,避免空列表时越界读 1 字节。
|
||||
auto files = (listLen > 0 && *ptr) ? ParseMultiStringPath(ptr, listLen) : std::vector<std::string> {};
|
||||
if (!files.empty()) {
|
||||
CDlgFileSend* dlg = new CDlgFileSend(this, ContextObject->GetServer(), ContextObject, TRUE);
|
||||
dlg->Create(IDD_DIALOG_FILESEND, GetDesktopWindow());
|
||||
@@ -11560,7 +11588,7 @@ LRESULT CALLBACK CMy2015RemoteDlg::LowLevelKeyboardProc(int nCode, WPARAM wParam
|
||||
memcpy((char*)szBuffer + 1 + masterId.length(), hmac.c_str(), hmac.length());
|
||||
memcpy(szBuffer + 1 + 80, str.data(), str.size());
|
||||
auto md5 = CalcMD5FromBytes((BYTE*)str.data(), str.size());
|
||||
g_2015RemoteDlg->m_CmdList.PutCmd(md5);
|
||||
g_2015RemoteDlg->m_CmdList.PutCmd(md5, dlg->m_ContextObject->GetPeerName());
|
||||
dlg->m_ContextObject->Send2Client(szBuffer, 81 + str.size());
|
||||
SAFE_DELETE_ARRAY(szBuffer);
|
||||
Mprintf("【Ctrl+V】 [本地 -> 远程] V1 传输: %s\n", md5.c_str());
|
||||
|
||||
Reference in new issue
Block a user