Idle Process
逛 Steam 的时候发现了这款游戏,看起来很适合拿来 CE 练手的样子
https://store.steampowered.com/app/2685770/Idle_Progress
首先发现可以轻松使用 Cheat Engine 进行数值修改,保守起见我将各项值修改为 Int32 最大值 2147483647
后来我发现有一些值可以通过游戏行为变化到更大,但如果我使用 CE 修改他们到 2147483648 后却变成了 -2147483648
???
先观察下内存
FF FF FF 7F 00 00 00 00 00 00 00 00 00 00 00 00 // 2147483647
01 00 00 00 00 00 00 00 E0 84 47 23 00 00 00 00 // -2147483648
??????????????????
随便动了几个值后并没有找到规律
我猜测也许使用了某些大数的存储方案,通过游戏初始画面我看到了是使用 Unity 制作的,应该是 .NET
我在 CE 里面找到了 Mono 相关的功能
发现游戏没有混淆,于是顺着包结构找到了 game_core 类
原来是 System.Numerics.BigInteger 实现的大数存储
都是平铺的静态字段,没有各种类引用,这样后续会方便很多
有了数据结构,只需要找到一个地址,其他的地址就可以通过 offset 来计算,不用再抓了
去搜了下 CE System.Numerics.BigInteger 相关的文章,找到了 .NET Decimal 类型的 CE 插件 但并不适用 BigInteger
于是去研究了下 BigInteger 的存储方式
https://stackoverflow.com/questions/21856753/what-is-the-limit-of-the-value-type-biginteger-in-c
// https://referencesource.microsoft.com/#System.Numerics/System/Numerics/BigInteger.cs,38
// For values int.MinValue < n <= int.MaxValue, the value is stored in sign
// and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits
internal int _sign;
internal uint[] _bits;
// https://referencesource.microsoft.com/#mscorlib/system/int32.cs,41
public const int MaxValue = 0x7fffffff;
public const int MinValue = unchecked((int)0x80000000);
很明显 _sign 指的是正负,_bits 是存储的数据
在一个 Int32 范围内时数据直接存储到 _sign , _bits 为 null。超出范围时 _sign 为 +1 或者 -1,_bits 负责存储数据
花了几个小时学习和研究构建出了下面的表
看 _bits DUMP 时发现总是以 0xE8 C6 45 70 开头,观察附近变化的内存,发现实际数据在后面。.NET 的 Array 实现并不像 C 那样只是一个指针指向第一个元素。经过查阅后发现还有 Object Header和 Method Table 前缀,接着是一个 Length,最后才是 Payload。
经过几次进位尝试,发现数组是动态增长的。所以修改的时候需要注意不要越界。修改一个临界值,让游戏对数组进行扩容。当然最好是使用 JIT 方式调用方法进行修改和扩容
但我不会 JIT 😛