Android Game Hacking Series - Level 2

Posted by VulturSec Team, on 14 Nov, 2023

Introduction to Android Game Hacking and its techniques. Hello everyone! Here is our second post on Android Game Hacking.

Blog Image

Android Game Hacking Series - Level 2

Introduction to Android Game Hacking and its techniques.

Hello everyone! Here is our second post on Android Game Hacking. In the previous blogpost we worked on a hack related to memory edition and how to make the hack reliable. If you are interested in that one you can check it here: Android Game Hacking - Level 1

In this blogpost we will explore a new type of hack that will require patching the binary or rather the in-memory version of the binary, in order to make the game behave in a different way. In practice this kind of technique is much more powerful and gets more interesting results. Memory edition has its limits…

In this blogpost I’ll be using the rogue-like game: Hyperrogue

The objective for this hack is to create a god-mode cheat so our character won’t lose ever. A character looses when at least two attackers surrounds it, like in the following case:

Mobile Game Hacking: Loose condition

Setup

We will work with the following setup:

Mobile Game Hacking Setup: Applications used to hack Android games

We will use Genymotion as the emulator, Ghidra to execute the reverse engineering of the application, Frida to help trace the functions that calculate the condition of game over and Game Guardian to patch the application.

Step 1: Starting with an hypothesis

Whenever we have to generate a cheat that requires patching the binary we can opt generally with two alternatives:

  1. Find a memory address which holds some variable (in the case of a god-like cheat it could be the HP variable), check what writes there and analyze the methods that points there in order to find the one that makes health 0 (following the case of god-like cheat).
  2. As in our case, there is no visual variable related to health so you need to find an indirect way of getting to the condition of GAME OVER. Here we can have multiple ways to approach this, like trying to understand the structure of the Player character in order to find which variable holds the health or checking some other condition triggered when the player dies.

In the first place I took the approach of finding the “GAME OVER” text that is shown whenever the user dies:

Mobile Game Hacking: Game Over pop up

I used Ghidra to do the binary reverse engineering. In this case I executed a string search for “GAME OVER” as shown in the pictures:

Mobile Game Hacking: Search for string in Ghidra

Luckily I found only one match for it, so I went on analyzing the reversed code generated by Ghidra to understand what were the conditions required to show the GAME OVER message:

Mobile Game Hacking: Analizing the reversed source

I found out that an important condition in order to check if the character was dead or not was to have the canmove variable in 0. So I tracked where this variable was being written. This can be done dynamically with a memory editor like cheat engine or statically with Ghidra’s capabilities to track the source code and infer where it is being used.

The dynamic way is better because it monitors the memory, so you could get the exact source code that reads or writes it even when the address is generated in a complex way.

The static way executes some analysis on the assembler and tries to link the instructions to the variables already found. It can be inaccurate or even miss some points.

In this case I wanted to use the static way in order to exercise some reversing skills. Also at the moment of checking the uses of the canmove variable it was written just once:

Mobile Game Hacking: Setting canmove variable

Step 2: Patching the application

So I had to patch the memory region by switching the strb instruction to anything that could be inoquous, like the nop instruction or a mov one that enforced the value of canmove.

In order to do this in Game Guardian we have to search in memory in the same way we search for a specific value, but instead of looking for an integer or a float we search for a hex value that can be taken from Ghidra:

004fb664 3f 01 00 39     strb       wzr,[x9]=>hr::canmove                            = 01h

When I looked for this instruction I found lots of coincidences (more than 500), so I extended the search for more instructions from the part of the binary I wanted to patch:

89 00 00 34     cbz        w9,LAB_004fb668
a9 31 00 d0     adrp       x9,0xb31000
29 a1 44 f9     ldr        x9,[x9, #0x940]=>->hr::canmove                   = 00ba54c4
3f 01 00 39     strb       wzr,[x9]=>hr::canmove                            = 01h

Then I changed the instruction to:

004fb664 29 00 80 d2     mov        x9,#0x1

But it didn’t work (which is why the subtitle of this section was hypothesis).

Step 3: Finding other path with Frida

After the first failure I decided to follow other path style. I looked in Ghidra for methods related to the string that is printed when the character tries to move when he is surrounded: “You would be killed”.

Then I’ll go through a dynamic validation of which methods are being called by using frida-trace like in the following case, because I found multiple places where the String could be used and the function that prints it was triggered:

frida-trace -U -i "*killMonster*" -i "*showMission*" -i "*count_status*" -i "*checkmove*" -i "*turn*" -i "*remission*" -i "*movepckeydir*" -i "*movevrdir*" -i "*mousemovement*" -i "*handleKeyNormal*" -i "*handleCompass*" -i "*attack*" -i "*Attack*" -i "*monstersnear*" -i "*kill*" HyperRogue

Whenever the action of movement after the user was killed is triggered the following trace could be seen:

Frida Trace: Print of result of Frida Trace

Then I proceeded to read the methods and the tree of calls found and analyze if I could understand where the death condition was enforced. After reading around some reversed code I found that if the method monstersnear returns 0, the application won’t execute the remaining attack method which internally calls the wouldkill, which is the method that triggers the “You would be killed” message.

A fast way to test it without going through the binary patching is using Frida. The following script will change the behaviour of the monstersnear function to return always 0:

Interceptor.attach(Module.findExportByName("libhyper.so","_ZN2hr16monstersnear_auxEv"), {
    onEnter: function(args) {
      //AggregateErrorconsole.log("entra");
    },
    onLeave: function(retval) {
      //console.log(retval);// simply replace the value to be returned with 0
      retval.replace(0);
    }
});

And after testing the function, it worked! So the next step would be to create a Game Guardian script that would patch the binary whenever the script is loaded. In this case the following hex code:

        005075a4 fc 6f 4d a9     ldp        x28,x27,[sp, #local_60]
        005075a8 00 01 00 12     and        w0,w8,#0x1
        005075ac ff c3 04 91     add        sp,sp,#0x130
        005075b0 c0 03 5f d6     ret

would be changed for the following one:

        005075a4 fc 6f 4d a9     ldp        x28,x27,[sp, #local_60]
        005075a8 00 00 80 52     mov        w0,#0x0
        005075ac ff c3 04 91     add        sp,sp,#0x130
        005075b0 c0 03 5f d6     ret

Creating a script based on this condition is pretty straightforward, but If you do not want to think, I have here the script for you:

gg.searchNumber("h FC 6F 4D A9 00 01 00 12 FF C3 04 91 C0 03 5F D6", gg.TYPE_BYTE)
gg.getResults(200)
gg.editAll("h FC 6F 4D A9 00 00 80 52 FF C3 04 91 C0 03 5F D6", gg.TYPE_BYTE)
gg.clearResults()
gg.toast("Done")

An alternative to this script would be to patch the application to add the Frida script, and run it when the application starts.

If you want to know how to do it, please read the following link: Permanent Frida Hacks

If you want us to create a video, reach out on our social networks and we will create the content for you :)

If you are a mobile game developer and you want to have an assessment on your mobile game, reach us, and we can help you to make your game hacker-proof!