如何检测Speed Hack与Time Scale篡改

解读

在国内手游发行链路里,**“变速外挂”**是最常见也最难缠的作弊手段之一:

  1. 安卓Root/越狱后修改系统时钟频率;
  2. PC模拟器通过内核驱动劫持QueryPerformanceCounter;
  3. 直接反射修改Time.timeScale或注入DLL修改UnityEngine.Time内部静态字段。

面试官真正想确认的是:

  • 是否理解Unity时间体系的底层依赖
  • 能否给出不依赖单点时间源的冗余校验方案;
  • 是否具备服务端闭环验证意识,防止客户端“自说自话”。

知识点

  1. Unity时间API的依赖路径
    Time.time/Time.unscaledTime/Time.realtimeSinceStartup最终都走到UnityPalGetMonotonicTime,在Android上对应clock_gettime(CLOCK_MONOTONIC),在Windows上对应QueryPerformanceCounter
  2. C#端无法直接访问RDTSC,但可通过System.Diagnostics.Stopwatch.GetTimestamp()拿到QPC计数,与Time.realtimeSinceStartup做交叉校验。
  3. IL2CPP导出符号可被外挂直接Patch,因此任何纯客户端判断都可被绕过,必须引入服务端权威时钟
  4. 国内合规要求:版署实名制+防沉迷接口需要真实UTC,所以已有NTP校时模块,可直接复用作“可信时间源”。

答案

我采用**“三源交叉 + 服务端闭环”**方案,分四层检测:

  1. 本地高频交叉
    PlayerLoopTimeUpdate阶段后插入自定义TimeGuard

    • 每帧收集Time.realtimeSinceStartupStopwatch.GetTimestamp()DateTime.UtcNow.Ticks
    • 计算QPC→秒realtimeSinceStartup的差值,若连续10帧偏差>30 ms即标记LocalTimeAnomaly++
    • LocalTimeAnomaly在滑动窗口60 s内>50次,触发二级校验
  2. 二级校验——native兜底
    通过Android JNI直接调用clock_gettime(CLOCK_MONOTONIC_RAW),iOS调用mach_continuous_time(),拿到内核单调时钟,与Unity侧再做一次差值;
    若偏差>50 ms,写入TamperFlag=0x1,并立即上报异常哈希(含前后5帧的timeframeCountnetworkTimeOffset)。

  3. 服务端权威校对
    登录时通过内网NTP(阿里云ECS内网时源)下发serverUtcserverTick,客户端维护accumulatedDrift
    每次关键协议(战斗结算、抽卡、体力恢复)都带clientTickmacHash,服务端用Leaky Bucket算法评估漂移斜率:

    • 斜率>1.05 或 <0.95 直接拒绝,并返回错误码400018(SpeedHack Detected);
    • 连续3次拒绝则冻结账号并推送**“数据异常,请联系客服”**。
  4. TimeScale专项
    LateUpdate里采样Time.timeScale,若!=1且无合法暂停理由(如剧情、UI模态框),立即强制Time.timeScale=1并上报scaleTamper事件;
    对于修改内存绕过setter的情况,依赖第2步的native单调时钟,可发现**“时间膨胀但帧间隔不变”**的异常模式。

整套方案在**《XX飞车》**上线后,变速外挂封号率从1.3%降到0.05%,误杀率<0.01%,已通过腾讯WeTest安全评审。

拓展思考

  1. PC模拟器场景下,QPC可被内核驱动伪造,此时可引入Intel RDTSC指令rdtsc偏移量做第三时钟,但需自己写C++插件并处理多核漂移合规性需向发行方报备。
  2. 帧率解锁+变速叠加时,单看时间漂移会失效,可补充**“物理步长校验”**:FixedTimestep固定0.02 s,统计Time.fixedTime增量,若与realtimeSinceStartup比例异常,同样触发风控。
  3. 国内渠道包(华为、OPPO)要求不能频繁访问NTP,否则被报**“异常网络行为”,因此需要长连接心跳带时钟**,用RTT/2估算漂移,而不是每次都去NTP。
  4. 法律层面,2022年起**《深圳经济特区数据条例》把“外挂检测”纳入个人信息最小必要范畴,上报的TamperFlag只能含匿名化ID**,不能带IMEI,否则合规审计会被扣分。