现象
放置类项目,后台监控到离线收益结算异常:一批账号到手的产出明显高于服务端按规则应发的量,且集中在几个高价值道具上。
先看协议层,排除最常见的两种情况。抓包比对上报请求,包签名正确、ts 在窗口内、nonce 无重复,说明既不是链路篡改也不是请求重放:签名保证包没被中间改过,nonce 保证同一个包没被重复提交,但这两样都不涉及包体内容本身的真伪。问题出在上报的数据是假的,而校验一路放行。
排查过程
第一层:值域校验
服务端已有值域校验,拿玩家面板快照算理论上限,超限丢弃:
maxDamage = atk × critMulCap × hitCap
但日志显示异常账号的上报值普遍卡在上限的 90%~95%,没有触发任何丢弃。定位到两个问题:
一是校验用的阈值来源。伤害系数、暴击上限等参数写在随包下发的数值表里,客户端可解包获取,作弊者据此把上报值压在阈值之下。二是校验失败的返回行为:值域不通过时接口返回明确的错误码,等价于向客户端暴露了一个判定 oracle,通过二分探测即可逼近阈值边界。
第二层:自洽校验
补充字段间的物理约束校验,检查单个请求内部是否自相矛盾。例如同一技能的释放次数与其冷却时间、战斗总时长之间存在硬约束:
// 同一技能释放 n 次,至少要跨 (n-1) 个 CD
foreach (var g in req.SkillCasts.GroupBy(c => c.Id)) {
long need = (g.Count() - 1) * SkillTable[g.Key].CooldownMs;
if (need > req.DurationMs) {
Flag(uid, "cd_overflow"); // 打标,返回照常成功
}
}
上线后拦截到一批伪造请求,但仍有一部分账号的上报数据完全自洽,产出却对不上。复现分析后确认:这部分作弊不修改协议包,而是修改客户端运行时——内存改写攻击力、变速工具放大帧率,正常完成整局战斗后如实上报。战斗过程在客户端真实发生,因此数据在值域和自洽两个维度都无懈可击。值域与自洽校验到此失效。
第三层:服务端重演
对上述情况,唯一可验证的方式是服务端重算,即上报输入而非结果:客户端提交随机种子与操作序列,服务端用同一套战斗逻辑重跑一遍,比对结果 hash 是否一致。
var sim = new BattleSim(snapshot, req.Seed); // 与客户端同一份战斗核
foreach (var act in req.Actions)
sim.Step(act);
if (sim.ResultHash != req.ResultHash)
Flag(uid, "replay_mismatch");
Settle(sim.Result); // 一律以服务端重演结果结算
落地前提是双端计算结果逐位一致,否则无法比对。主要有两点:浮点运算跨端不一致,战斗核需改为定点数实现;随机数不能使用引擎自带的 UnityEngine.Random,改用自带种子的确定性实现,且双端每一步的调用次数严格对齐。该改动在项目早期成本可控,系统成型后再改动代价很高。
一个容易误判的坑
重演上线初期,replay_mismatch 打标量会异常偏高,容易被误判为大规模作弊。实际排查后发现,绝大多数是客户端与服务端逻辑未对齐导致,例如技能结算顺序相差一帧、某处随机调用次数不一致,真实作弊占比很低。
因此重演上线阶段应仅打标观察,不接入封号或拦截结算,待双端对齐、mismatch 率降至个位数后再启用处置逻辑,否则首轮误伤的多为正常玩家。
部署与成本
全量重演还是抽样,取决于品类的单局输入规模:
- 回合制、卡牌、放置:单局输入仅几十字节,重跑是纯逻辑运算,可每局全量重演;
- 实时动作、MOBA、射击:每帧都是输入,单局数据量与重跑开销高出两个数量级,只能抽样,另对榜单 Top、高价值掉落、已打标账号做触发式全量。
处置策略
数据判定为异常不等于直接封号。弱网重传、断线补包、低端机 RTC 跳变等正常情况都会产生"不合理"的数据,单条信号误伤率高。采用分级处置:先打标,再限制变现路径(交易、提现、榜单结算),多维信号累积到阈值后再人工介入。
检查项小结
| 检查项 | 常见疏漏 | 排查要点 |
|---|---|---|
| 值域校验 | 阈值随数值表下发客户端 | 校验用阈值服务端单独存,不随包下发 |
| 失败返回 | 校验不过返回明确错误码 | 照常返回成功,异步打标,不暴露判定 |
| 自洽校验 | 只查单字段,不查字段间约束 | 校验技能次数×CD、消耗与命中等硬约束 |
| 上报粒度 | 上报客户端聚合后的均值 | 上报原始采样序列,判定放服务端 |
| 服务端重演 | 直接采信客户端结果 | 上报种子+操作序列,双端一致后重跑 |
| 重演上线 | 一上线即接封号 | 先打标观察,mismatch 降下来再处置 |
| 处置策略 | 单条信号即封号 | 分级:打标→限制变现→人工 |
排查到最后,校验该做到哪一层,本质是成本问题:投入与这份数据的变现价值匹配即可。纯本地、数据只影响自身显示的,值域足够;涉及交易与提现的,服务端重演的工程成本再高也需要覆盖。
完整的分层判断逻辑另有整理,可搜索「字节暗面 服务端校验」。

222

被折叠的 条评论
为什么被折叠?



