Improve: client/server - stable client ID via MachineGuid+path (V2)

This commit is contained in:
yuanyuanxiang
2026-05-06 21:32:06 +02:00
parent 0aa75882d1
commit 2c5b5ad628
5 changed files with 282 additions and 30 deletions

View File

@@ -214,6 +214,54 @@ static std::string getUsername()
return user ? std::string(user) : "unknown";
}
// 读取 IOKit 维护的 IOPlatformUUID与 Windows MachineGuid 等价)
// 这是主板/系统级 UUID由 IOPlatformExpertDevice 服务提供,重装系统通常不变。
// 对应Windows HKLM\Software\Microsoft\Cryptography\MachineGuid
// Linux /etc/machine-id
static std::string getMachineId()
{
std::string result;
io_service_t platformExpert = IOServiceGetMatchingService(
kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert != IO_OBJECT_NULL) {
CFTypeRef uuidProperty = IORegistryEntryCreateCFProperty(
platformExpert, CFSTR(kIOPlatformUUIDKey),
kCFAllocatorDefault, 0);
if (uuidProperty != nullptr) {
if (CFGetTypeID(uuidProperty) == CFStringGetTypeID()) {
CFStringRef uuidStr = (CFStringRef)uuidProperty;
char buf[64] = {};
if (CFStringGetCString(uuidStr, buf, sizeof(buf), kCFStringEncodingUTF8)) {
result = buf;
}
}
CFRelease(uuidProperty);
}
IOObjectRelease(platformExpert);
}
return result;
}
// 路径归一化macOS 版):解析符号链接 + 转小写
// realpath 把 /Applications/foo/../bar 之类折回规范形式;
// 小写化保持与 Windows/Linux 跨端一致。macOS HFS+/APFS 默认大小写不敏感,
// 转小写不改变文件标识、但避免路径串大小写差异引起 ID 不同。
static std::string normalizeExePathLower(const std::string& path)
{
char resolved[PATH_MAX] = {};
std::string out;
if (realpath(path.c_str(), resolved) != nullptr) {
out = resolved;
} else {
out = path; // 解析失败:用原值
}
for (auto& c : out) {
if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
}
return out;
}
// Get screen resolution
static std::string getScreenResolution()
{
@@ -552,16 +600,30 @@ static void fillLoginInfo(LOGIN_INFOR& info)
std::string resolution = getScreenResolution();
info.AddReserved(resolution.c_str());
// 17. Client ID (calculated from system info, same algorithm as server)
// Format: pubIP|hostname|os|cpu|path
char cpuStr[32];
snprintf(cpuStr, sizeof(cpuStr), "%uMHz", info.dwCPUMHz);
std::string idInput = (pubIP.empty() ? "?" : pubIP) + "|" +
hostname + "|" +
osVer + "|" +
cpuStr + "|" +
exePath;
g_myClientID = XXH64(idInput.c_str(), idInput.length(), 0);
// 17. Client ID
// V2 算法IOPlatformUUID + 归一化路径
// - 同机同程序路径永远同 ID不依赖 IP/hostname/os/CPU 漂移)
// - IOPlatformUUID 主板级,重装系统通常不变;多机各不相同
// - 读取失败时退化到老算法pubIP|hostname|os|cpu|path保兼容
std::string machineId = getMachineId();
if (!machineId.empty()) {
std::string normPath = normalizeExePathLower(exePath);
std::string idInput = machineId + "|" + normPath;
g_myClientID = XXH64(idInput.c_str(), idInput.length(), 0);
NSLog(@"ClientID(v2): %llu (machineId=%s, path=%s)",
g_myClientID, machineId.c_str(), normPath.c_str());
} else {
// 老算法兜底
char cpuStr[32];
snprintf(cpuStr, sizeof(cpuStr), "%uMHz", info.dwCPUMHz);
std::string idInput = (pubIP.empty() ? "?" : pubIP) + "|" +
hostname + "|" +
osVer + "|" +
cpuStr + "|" +
exePath;
g_myClientID = XXH64(idInput.c_str(), idInput.length(), 0);
NSLog(@"ClientID(v1 fallback): %llu (IOPlatformUUID 读取失败)", g_myClientID);
}
info.AddReserved(std::to_string(g_myClientID).c_str());
NSLog(@"LOGIN_INFOR filled: OS=%s, Host=%s, CPU=%dMHz, PubIP=%s, ClientID=%llu",