热血传奇刷金漏洞复盘:服务器数据一致性设计与事务防护拆解

来源: 作者: 点击:
一、经典刷金漏洞的真实触发链
早期《热血传奇》代表性刷金事件集中在背包→仓库、金币→金条、任务奖励、赌局返还四类操作上,共同特征是服务端未把“扣减—生成—落库”做成原子步骤。

1. 白日门金条复制(虎卫传说版本)
玩家背包放 1002000 金币,在仓库NPC执行“捆金条”(100万金币+2000手续费→1金条)。旧逻辑流程:
① 客户端发捆扎请求
② 服务端先向仓库写入新金条记录
③ 延迟扣除背包金币
④ 在③执行前用小退/断网/切换地图中断连接
结果:金条已生成、背包金币未扣,重复操作指数级复制。根源是跨容器转移未加分布式锁,也未做“预扣后生成”原子校验。

2. 红名村蟹任务刷金
送道具给NPC返2000金币,正常应“道具消失+金币到账”同笔完成。漏洞期:与另一玩家交易点击确认的同时点NPC递交,服务端判定金币发放成功但道具删除指令未提交,形成无限循环。

3. 赌场/骰子NPC返还异常
对话协议未校验请求唯一性,玩家在结算瞬间强制中断并重发赌注请求,服务端重复执行返还分支,金币正向累加。

4. 数值下限保护误判
捆金条脚本设“背包金币不得低于阈值”,恰好持有 1002000 时,扣2000会触保护机制→取消扣款但保留生成指令,凭空产金条。该缺陷随老版服务端代码泄露被移植进衍生版本,长期残留。

二、漏洞背后的服务端架构缺陷
经典传奇C/S分层:LoginSrv→LoginGate→RunGate/SelGate→M2(GameSvr)→DBServer→持久库(早期HeroDB/DBC2000,后期SQL/Access)。刷金本质发生在 M2内存态 与 DBServer持久态 的同步缝隙里:

- 事务原子性缺失:物品/金币跨容器变动未包成 ACID 事务,先写目标容器再扣源容器,中断即不一致。
• 操作非幂等:同一请求(交易确认、奖励发放、赌局返还)可重放多次,服务端无 Nonce/序列号/会话绑定校验。

- 并发锁粒度粗:多线程处理同一背包/仓库容器时缺细粒度互斥,检查余额与扣减非原子,竞态下双重执行。
• 客户端信任过度:封包内数量字段(含负数、极大值)未做服务端重算与边界校验,改包即刷。

- 内存与DB回写时间差:M2改内存→通知客户端成功→异步写DB;若DB超时丢写且无比对日志,重启后以DB为准或反之,产生复制/消失。
• 存档节点竞态:在定时存盘瞬间做交易/下线,卖家重登复制装备、买家持“幽灵物品”,重启后才暴露。

三、数据一致性设计的正确闭环
正规MMO经济系统必须让服务端始终权威,所有货币/物品变动走统一管线:

1. 原子事务 + 预扣后生成
伪序:BEGIN TX → 扣背包金币 → 校验余额≥0 → 生成仓库金条 → 写事务日志 → COMMIT → 回包客户端;任一步失败整体 ROLLBACK,内存与DB回到操作前状态。

2. 操作幂等性
关键动作(交易确认、奖励领取、兑换)绑唯一 token/自增序列号,M2与DB双层去重,重放包直接丢弃。

3. 细粒度容器锁
对玩家背包、仓库、行会仓库、交易临时容器加对象级锁(临界区/CIntLock),检查—扣减—生成在同一锁内完成,禁止中途让出线程。

4. 预写日志(WAL)+ 对账回滚
所有物品变动先落 TransLog(谁/何时/前后量/操作类型),DB确认提交后才清标记;写失败按日志回滚内存,定时任务扫日志与DB快照对账。

5. 服务端权威校验
客户端只发意图(“请求捆金条”),数量、手续费、余额、物品GUID由M2按 StdItems/Setup 重算;负数、溢出、超频、越权全部拒绝;装备带唯一 GUID 联合主键防复制。

6. 存盘与断线策略
定时存盘 + 关键操作即时落库双通道;断线时不立即提交跨容器变更,重连走未提交事务恢复或整体撤销,杜绝“小退卡点”。

7. 经济监控与熔断
金价波动、单账号单位时间产金、同IP多号联动超阈值即冻兑换/交易/赌局,配合回档与异常账追溯。

四、修复与运维动作对照
官方当年应对白日门事件:20小时维护 + 回滚至漏洞前快照 + 后台日志追缴复制金条 + 改为预扣后生成与跨场景分布式锁。
现代传奇系引擎(GOM/GEE/Hero商业版)在 DBServer 加交易验证、装备 GUID 主键、Nonce 防重放、HMAC 包签名、读写分离主从库,老版“小退卡仓库”链路在标准编译版已不可复现。

刷金漏洞不是单点BUG,而是“服务端权威 + 原子事务 + 幂等校验 + 锁粒度 + 日志回滚”任意一环断裂的产物;架构上把货币视作数据库资产而非内存变量,所有变动走事务与对账,经济系统才不会出现凭空产金。