目录
一、核心原理:int 等固定长度整数的 “容量有限”
二、正溢出(公式溢出 / 正式溢出)
例子(32 位 int)
三、负数溢出(下溢 / 反向溢出)
例子(32 位 int)
四、为什么游戏能被利用?(代码设计弱点)
五、一句话总结
先把名词对齐一下: 你说的公式溢出、负数溢出、正式溢出,在游戏开发里一般统称为整数溢出(Integer Overflow),其中:
- 公式溢出:计算伤害、金币、经验等公式时,结果超出类型上限;
- 负数溢出:数值向下溢出(小于类型最小值),或正溢出后变成负数;
- 正式溢出:应该是 “正溢出(Positive Overflow)”,即超过正数上限。
它们本质都利用了同一个底层原理:固定位宽的整数类型用补码存储,超出范围会 “回绕”,且游戏代码默认不做溢出校验。
一、核心原理:int 等固定长度整数的 “容量有限”
游戏里伤害、金币、属性、经验,大多用32 位有符号整数(int32)存储:
- 范围:-2³¹ ~ 2³¹−1即-2147483648 ~ 2147483647
- 二进制:最高位是符号位(0 = 正,1 = 负)
可以想象成只有 10 位的里程表:
- 最大:9999999999
- 再加 1 → 变成 0000000000(直接回绕)
计算机里是二进制回绕,而且会把 “进位” 丢掉,只保留低 32 位。
二、正溢出(公式溢出 / 正式溢出)
表现:数值太大 → 突然变很小,或变负数。
例子(32 位 int)
- 最大值:
2147483647(二进制:0111...1111) - 加 1:
- 数学:
2147483648 - 实际二进制:
1000...0000 - 解释:符号位变成 1→ 被解释为负数-2147483648
- 数学:
游戏里怎么用:
- 伤害公式:
攻击 × 倍率 × Buff叠太高 → 溢出变负数 - 逻辑:游戏判断
if (伤害 > 0)才扣血 →负数伤害变成回血 - 金币 / 经验溢出 → 变成负数,绕过 “上限检查”,甚至直接刷满
三、负数溢出(下溢 / 反向溢出)
表现:数值太小(比最小值还小)→ 突然变很大正数。
例子(32 位 int)
- 最小值:
-2147483648(二进制:1000...0000) - 减 1:
- 数学:
-2147483649 - 实际二进制:
0111...1111 - 解释:符号位变成 0→ 被解释为正数2147483647
- 数学:
游戏里怎么用:
- 把血量、金币故意减到极小→ 下溢变超大正数
- 例如:
血量 = 1 - 1000000→ 下溢 → 血直接变成 21 亿,无敌
四、为什么游戏能被利用?(代码设计弱点)
默认不校验溢出
- C/C++/C# 等默认unchecked,溢出不报错,直接回绕。
- 开发觉得 “玩家打不出这么高伤害”,省了校验代码。
用了有符号类型(signed int)
- 无符号(uint)溢出是从大到小(255→0),不会变负;
- 有符号会正负翻转,最容易出 BUG。
校验逻辑只判断 “正数上限”
- 常见写法:
if (gold > MAX_GOLD) gold = MAX_GOLD; - 负数完全不拦→ 溢出成负数后,绕过上限,甚至可以再溢出刷满。
- 常见写法:
公式叠乘、递归翻倍
- 技能、Buff、装备多次 “×2” → 指数增长,很快顶破 int 上限。
五、一句话总结
- 公式 / 正式溢出:正数超出
2147483647→变负数(利用:回绕 + 符号位翻转); - 负数溢出:负数低于
-2147483648→变超大正数(利用:反向回绕); - 本质:补码存储 + 固定位宽 + 无溢出校验 + 有符号类型共同造成的 “数值绕圈”。