So, I was testing a (closed source) single-player offline game recently and thought this exercise might be fun to document. I didn’t want to spend any time actually earning in-game money since I’d played it before and I wanted to just skip ahead to other aspects of the game. I was curious how straight-forward adjusting my cash might be. So, noting the in-game “bank account number” of 219393 and account balance of 3000, I dived right in.
First up, what’s the memory layout of the heap look like? I looked at the brk and the mmap regions without a mapped library or file, marked with “w” in the permissions column, from /proc/PID/maps
:
0827e000-08282000 rw-p 00000000 00:00 0
0a22e000-0b08a000 rw-p 00000000 00:00 0 [heap]
efa59000-efd00000 rw-p 00000000 00:00 0
efd00000-efd21000 rw-p 00000000 00:00 0
Knowing these, I could use gdb’s “find” command, after attaching to the process:
$ gdb /some/cool/game
…
(gdb) attach PID
…
(gdb) find /w 0×0827e000, 0×08282000, 219393
(gdb) find /w 0×0a22e000, 0×0b08a000, 219393
0xaf03d08
0xaf06ca8
No hits in the first region, but I see two hits for the account number value in the second region. Let’s start there and see what’s near them…
(gdb) x/8x 0xaf03d08
0xaf03d08: 0×00035901 0×00000000 0×00000000 0×0af06ce0
0xaf03d18: 0×0af06be0 0×00000059 0×0af03d98 0×0af041e8
(gdb) x/8x 0xaf06ca8
0xaf06ca8: 0×00035901 0×00000bb8 0×00000bb8 0×0820b148
0xaf06cb8: 0×00000001 0×00000000 0×00000000 0×00000000
In that second hit, I see the value 0xBB8, which is 3000, and matches our account balance. Let’s see what happens if we just change both of those to add a bit a few orders of magnitude above the current value…
(gdb) set var *0xaf06cac = 0×00100bb8
(gdb) set var *0xaf06cb0 = 0×00100bb8
(gdb) x/32x 0xaf06cac
0xaf06cac: 0×00100bb8 0×00100bb8 0×0820b148 0×00000001
(gdb) continue
And presto, clicking on the bank account details in-game shows a huge account balance of 1051576 now. No need to reverse-engineer any saved games, whew.