Dheeraj Kumar

Ruby, Golang, Scala, Javascript

Hacking Sneaky Sneaky

| Comments

I recently came across a game called Sneaky Sneaky while browsing around Steam, and on watching a few videos, decided to purchase it. It’s like a 2D Assassin’s Creed, with Zelda-esque graphics and a few RPG elements.

I played it on my PC, but it supports only mouse clicks, which led me to guess that it’s probably a port of a mobile game. It turned out to be correct, as Sneaky Sneaky is originally for iOS.

It took me a few hours to finish the game. It’s so much fun to play, and I loved it. It even gave me an idea for a new game! I was, however, annoyed by the AI and the controls – two areas where the game could have done better. In some levels, Sneaky wouldn’t turn or stop fast enough to hide from the enemies, leading to unnecessary fights. The combat animations were quite slow, the enemies quite strong in the second half of the game, and I found myself getting bored & my character killed very quicky.

So, I decided to hack the game and cheat a bit. I just wanted an infinite health hack so that I could at least not be penalized for any control issues, and find a bush to hide in. However, I ended up finding so much more…

I started by locating the game’s executable inside the Steam directory, and running Exeinfo PE on it:

No packing, that’s pretty good! I don’t know if that’s a Steam limitation. Let’s look into it some more:

Debug info is present! that’s a surprise, but I’m not complaining!

Everything else seemed pretty standard, so I fired up Cheat Engine and attached it to the game. Let’s get hacking!

I noticed that damages were in counts of half-hearts, so I guessed that’s how the health was being stored. So if Sneaky had 3 hearts, it was being stored as 3 * 2 half-hearts = 6. So I searched for 6, got hit once, searched for 4, got hit once more, searched, used a potion, etc. I was left with one address for the health. That was simple!

The address I found was clearly dynamic, so the next time I played the game, or even in the next stage/room, I could have a different address, which I’ll have to find again. This is called Dynamic Memory Allocation (DMA), which needs to be defeated. There are several ways to do this, and I started with the simplest – pointers.

I wanted to find a series of pointers, that will lead me to the correct address, regardless of the room/stage I’m in, or even a different instance of a game altogether.

The first time I scanned for a level 5 pointer, I found 48 million pointers, which I rescanned multiple times, trying out various moves to invalidate as many results as possible – moving to a new room, moving to a new stage, quitting and starting a new game instance…

Eventually, I ended up with zero pointers left.

One possible reason for this is – the game could have used an embedded scripting language like Lua, which has a VM to manage its memory. Given that it’s also on iOS, it’s a good possibility. I didn’t want to spend time investigating, so I moved on to the next weapon in my arsenal – debugging.

I used a Cheat Engine feature to find out what opcodes accessed the current health address, and then got hit by an enemy.

The first one, with the 2904 count (and counting!), had to be a part of the game loop. I let a slime hit me thrice, which resulted in the remaining 4 opcodes.

I know that for a simple infinite health hack, all I have to do is to patch the game loop, and write a high-enough number to my health’s address. However, there might be complications. What if the same opcodes handled both the player’s and the enemies’ health addresses?

I first had to confirm if that was the case. I was reasonably confident, since most games are OOP systems, which lead to monsters and players sharing the same base classes.

To do this, I used another Cheat Engine feature to find what addresses were accessed by a particular opcode. I used this on the opcode inside the game loop. This is what I got:

So I was right – four addresses were being accessed, and I had four entities on screen – three slimes and Sneaky.

Now, I had to figure out how to distinguish an enemy from a player. There are again several ways to do this, but in this case, I needed to analyze the data structure for an entity.

All the opcodes I got dealt with [something + A4], so it was logical that something be the base address for that entity. I used Cheat Engine’s Structure dissection utility, and started marking the important offsets. I did this for about 4 types of monsters, and ended up with these:

As you can see, I ended up with a few more addresses than just how to differentiate between entity types. I searched for actual values to find the arrows & items offsets, but the max health was found from another source. We’ll get there in a moment. The important thing is, I managed to find two offsets which were zero for the player, but had values for the monsters. That’s the differentiator we need! Now, I had everything I needed to make the infinite health hack.

I mentioned earlier that the monsters and the slow combat annoyed me, and now that I know their health addresses, I wanted to experiment with a one-hit kill. I started by setting their health to zero.

I seemed to have ended up with zombie enemies! Clearly, the monsters’ removal doesn’t respond to changes in the health. They would happen together when they are attacked. That makes sense, and I could hit them just once for them to die, but I still had to go through the slow combat scene. What if I could just kill them automatically?

To do this, I looked at the opcodes that were executed when some damage was incurred:

1
2
3
4
5
6
7
8
9
10
11
; Read the current health value
sneaky.exe+47BC - 0FBE BE A4000000  - movsx edi,byte ptr [esi+000000A4]

; Write the health back, after subtracting the damage
sneaky.exe+47ED - 88 86 A4000000  - mov [esi+000000A4],al

; Read the new health value again?
sneaky.exe+47F7 - 0FBE 86 A4000000  - movsx eax,byte ptr [esi+000000A4]

; Check if the entity is dead
sneaky.exe+4828 - 80 BE A4000000 00 - cmp byte ptr [esi+000000A4],00

I saw the distance between the 4 opcodes, and they look like they are a part of a single routine. This was clearly the ‘attack’ routine of the game. To find how to call it for enemies, I could have used Cheat Engine, but I felt another tool would be more useful. So I fired up OllyDBG 2.0.

I tried doing this with Olly 1.10, but v2’s analysis capabilities are much better than the first. It was able to recognize some routines in the game that v1.10 couldn’t.

I loaded sneaky.exe inside Olly, let it analyze the code, went to the routine, found the entry point and set a breakpoint. I then hit a monster in-game and watched it break:

Looking at the stack trace at the bottom right, I saw that there were three parameters passed to this routine. Arg3 was clearly the damage I dealt – 2 hearts represented as 4 half-hearts. I had to understand what the other 2 arguments where.

I first thought that it would be the two parties involved in the exchange – the attacker and the victim. In that case, however, the victim’s address wouldn’t be passed using ECX. Also, the addresses of the data structures passed were too distant from the ones I found previously.

After playing around some more, I noticed that Arg1 was zero for melee attacks (like in the screenshot), but was a pointer for arrow attacks. To figure this out, I went back to Cheat Engine and tried its structure dissection feature on the address from the stack.

I couldn’t figure out what this exactly is, but I guessed it was some sort of attack object, along with the sprites & animations involved. Now I had the extra work of finding these addresses and passing it to the method. I didn’t want to do that, so I went through the code to understand how they were being used.

The entire routine is too large (540 bytes) to explain, so I’ve only shown the important parts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
$+472F      /$  mov     eax, sneaky.00201101               ; Entry point
$+4734      |.  call    sneaky.001EDC70                    ; Setup a SEH
$+4739      |.  sub     esp, 10                            ; This method uses 4 local vars
$+473C      |.  push    ebx                                ; Prologue
$+473D      |.  push    esi                                ; Prologue 
$+473E      |.  push    edi                                ; Prologue
$+473F      |.  mov     esi, ecx                           ; Pointer to entity is passed around via ECX
$+4787      |.  mov     eax, dword ptr [ebp+8]             ; Read Arg1
$+478E      |.  test    eax, eax
$+4790      |.- jz      short sneaky.0012479E              ; It checks for null! woohoo!
$+4795      |.- jnz     short sneaky.0012479E
$+479C      |.  call    dword ptr [edx]                    ; Call animations?
$+479E      |>  mov     eax, dword ptr [ebp+0C]            ; Read Arg2
$+47A5      |.  test    eax, eax
$+47A7      |.- jz      short sneaky.001247B5              ; It checks for null! woohoo!
$+47AC      |.- jnz     short sneaky.001247B5
$+47B3      |.  call    dword ptr [edx]                    ; Call animations?
$+47BC      |>  movsx   edi, byte ptr [esi+0A4]            ; Read the current health value
$+47C3      |.  movsx   ecx, byte ptr [esi+94]             ; Read the max health value
$+47CA      |.  mov     eax, edi
$+47CC      |.  sub     eax, dword ptr [ebp+10]            ; Subtract the damage from Arg3
$+47D4      |.  cmp     eax, ebx
$+47DF      |.- jg      short sneaky.001247E4              ; Check if the current health is > 0.
$+47E4      |>  cmp     ecx, dword ptr [eax]
$+47E6      |.- jge     short sneaky.001247EB              ; Check if the max health >= current health
$+47E8      |.  lea     eax, [ebp-14]                      ; If not, make current health = max health
$+47EB      |>  mov     al, byte ptr [eax]
$+47ED      |.  mov     byte ptr [esi+0A4], al             ; Write the current health value back to memory
$+47F3      |.  cmp     edi, ebx                           ; This pattern is a while loop, most likely for painting the UI, and visually displaying each heart/half a heart being lost.
$+47F5      |.- jle     short sneaky.00124825
$+47F7      |>  /movsx   eax, byte ptr [esi+0A4]
$+481B      |.  |call    sneaky.001C17A0                   ; Paint UI?
$+4820      |>  |inc     ebx
$+4821      |.  |cmp     ebx, edi
$+4823      |.- \jl      short sneaky.001247F7
$+4828      |.  cmp     byte ptr [esi+0A4], 0              ; Actual death checks start here. 
$+482F      |.- jg      sneaky.00124982                    ; Exit the routine if healthy
$+....      |.-                                            ; More death checks
$+....      |.-                                            ; More calling animations on the attack object(s)
$+4A0E      \.  retn    0C                                 ; 12 bytes were passed as arguments to this method.

That was quite straightforward to follow. The important thing I got out of this, was that the attack objects could be null pointers! That simplified everything. Now, writing a hack was trivial. The easiest way was an Auto Assembler script in Cheat Engine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// ; Boilerplate begins.

// ; Game loop address to hook into. 
define(address, "sneaky.exe"+2836)
define(bytes, 0F BE 86 A4 00 00 00)

[ENABLE]

// ; Make sure the game versions match
assert(address, bytes)
// ; Allocate some memory for our hack
alloc(newmem,$ 1000)

label(code)
label(return)

registersymbol(GodModeEnabled)
registersymbol(AutoKillEnabled)

label(Hacks)

label(GodModeEnter)
label(GodModeExit)
label(AutoKillEnter)
label(AutoKillExit)

label(DetectPlayer)
label(SetPlayer)
label(SetEnemy)

label(GodModeEnabled)
label(AutoKillEnabled)
label(PlayerDetected)

newmem:
// ; Define a bunch of variables 
PlayerDetected:
 dd 0

GodModeEnabled:
 dd 0

AutoKillEnabled:
 dd 0

code:
  // ; Are we dealing with the player or the enemy?

  DetectPlayer:
    cmp [esi+1C], 00
    jne SetEnemy

  SetPlayer:
    mov [PlayerDetected], 01
    jmp Hacks

  SetEnemy:
    mov [PlayerDetected], 00
    jmp Hacks

  Hacks:

  GodModeEnter:
    // ; Bunch of checks to enable god mode
    cmp [GodModeEnabled], 01
    jne GodModeExit

    cmp [PlayerDetected], 01
    jne GodModeExit

    // ; Refill health by replacing it by max health. 
    mov eax, [esi+00000094]
    mov [esi+000000A4], eax

  GodModeExit:

  AutoKillEnter:
    // ; Bunch of checks to auto kill
    cmp [AutoKillEnabled], 01
    jne AutoKillExit

    cmp [PlayerDetected], 01
    je AutoKillExit

    // ; Let's send out an attack!

    // ; No idea what the current ECX is, better be safe.
    push ecx

    // ; The first piece of data the attack method needs - a pointer to the entity in ECX
    mov ecx, esi

    // ; The two attack objects
    push 0
    push 0

    // ; The second piece of data the method needs - the damage done. Let's do damage equal to the enemy's max health. I could have just sent in a large number, but this is safer and less hackier.
    movsx eax, byte ptr [esi+00000094]
    push eax

    call "sneaky.exe"+472F

    // ; Restore ECX
    pop ecx

  AutoKillExit:

  // ; This was the original instruction
  movsx eax, byte ptr [esi+000000A4]

  jmp return

address:
  jmp code
  nop
  nop

return:

[DISABLE]

address:
  db bytes
  // ; movsx eax, byte ptr [esi+000000A4]

dealloc(newmem)

unregistersymbol(GodModeEnabled)
unregistersymbol(AutoKillEnabled)

This is not a clean implementation, and can be refactored a bit. However, for a quick & dirty hack, it works perfectly!

When enabled, the autokill hack makes all enemies in the current room kill themselves. I could call it the suicide hack, but autokill sounds fancier, and more importantly, much less morbid!

I wouldn’t implement this in a trainer in this form, for it destroys all the fun in the game. Cheats are for reducing the difficulty level by providing an unfair advantage to the player, but not for ruining the entire gameplay experience. What good is a stealth-based game, if there is nobody to hide from?

I’d definitely implement the god mode. As I mentioned above, it’d keep Sneaky alive enough to find a bush to hide in. I would probably bind the autokill hack to a hotkey, instead of letting it kill everything in sight. This way, if the player finds a room too difficult to pass, they could clear it easily with the press of a button. I could even kill just one enemy with every invocation, instead of all of them.

Also, I’ve marked the arrows & items’ offsets in the entity structure above, but considering how easy they are to come across, I wouldn’t hack them at all.

I spent about 4 hours finishing the game, 3 hours hacking the game, but 8 hours writing, refining & annotating this post. I suppose it would be easier if I were more regular in writing.

If you find the game interesting (or the hacking, but who am I kidding?), you can pick it up on Steam (with a 10% discount till 2nd January 2015!)

Comments