Menu Close Back

Cheating is Easy: How to Prevent Mobile Game Memory Tampering

Cheating is Easy: How to Prevent Mobile Game Memory Tampering

By: Jan Seredynski - Security Researcher

In this blog post, we will illustrate how easily attackers can use memory-tampering techniques to cheat in mobile games, and how game developers can employ simple security and prevention techniques to prevent them. 

While the attack vectors vary, both jailbroken and non-jailbroken devices are at risk for memory tampering. Below we will demonstrate several of these exploits on a real game from the top trending iOS apps in 2020, My Tamagotchi Forever. While no game development experience is required to follow along, a perfunctory understanding and ability to read code snippets will be helpful.

If you aren’t familiar, a “Tamagotchi” is a virtual pet that must be kept clean, entertained and fed. In its original incarnation, it was a small, handheld device popular with kids in the ‘90s. In its reincarnation as a mobile game, My Tamagotchi Forever, coins are used to purchase basic supplies, like food and toys, but premium items require currency of greater value: diamonds. 

It is nearly impossible to get free diamonds in the game, but users can purchase them in-app via ApplePay. This is where cheating often happens… 

Cheating on a jailbroken device couldn’t be easier

Using a jailbroken iPhone is a convenient path to cheat in mobile games. There are countless hacks and open-source tools available online that can be used to manipulate the game’s memory on a jailbroken iOS device. Hackers can use these tools to artificially increase their diamond inventory, effectively circumventing the need for in-app purchases. One example of such a tool is “iGameGod,” a popular and free in-memory modifier. It’s available through Cydia and can be enabled in the system settings after installation.

iGameGod can scan through the app’s memory for a specified value and list all of the memory addresses containing it. Then, it can overwrite the values stored at these addresses with any value of our choice. For example, we can search for our current diamonds count (12,345) and overwrite it with 99,999.

iGameGod in action

Once iGameGod is installed, a pink gear icon appears on the screen of your game (look at the fridge on the screenshot below). Tapping this icon opens the iGameGod search bar, which we can use to find any value that is stored in the game’s memory.

In our case, iGameGod returned only one memory address, containing the value “12345.” Tapping on the search result lets us modify the value at the corresponding address. If iGameGod returns multiple search results, we can overwrite all the occurrences at once, but this will likely cause the app to crash. A better approach is to use iGameGod’s filter feature to compare the values before and after a specific action, such as a diamond purchase. 

At this point, the diamond value is changed in memory, but the number of diamonds displayed on the screen is not refreshed yet. We can simply restart the app to refresh the GUI.

Hooray, we just hacked the game without any significant technical skills! We can now grow our Tamagotchi faster than other players and avoid paying for extras.

(A note: To better understand how tools like iGameGod work, have a look at the appendix. It describes how memory is scanned at runtime. This method is widely used in many cheat engines. We also included a snippet that you can use for similar results using LLDB / Xcode on a non-jailbroken device.)

Hacking on a non-jailbroken device

So far we’ve used a jailbroken device to change the amount of diamonds in the Tamagotchi game. Now let’s look at how this kind of cheat works on a non-jailbroken device.

iOS prevents third-party apps, like iGameGod, from controlling applications downloaded from the App Store. However, these restrictions do not apply to applications signed with a developer certificate. Therefore, we need to dump an application from a jailbroken device, resign it with a developer certificate, and install it on a non-jailbroken device. 

This process is called application repackaging, and we can do it in four steps:

  1. Retrieve Tamagotchi’s *.ipa file from a jailbroken device
  2. Sign the downloaded *.ipa with our own developer certificate
  3. Install the development *.ipa on a jailed device
  4. Modifying memory with our debugger, LLDB

An alternative approach involves modifying the memory with Frida. Frida is a runtime instrumentation tool that provides a JavaScript API for modifying the behavior and data of an app at runtime.

1. Retrieve Tamagotchi’s *.ipa file from a jailbroken device

After we install an app from the Apple App Store, we can dump it from a jailbroken device to a computer. There are many tools available online that can handle this, such as bakbak and frida-ios-dump

2. Sign the dumped *.ipa with a developer certificate

The dumped *.ipa is signed with an App Store certificate, which prevents us from modifying the application or attaching a debugger. We can use iOS App Signer to resign the application with our own developer certificate and provisioning profile, both of which can easily be obtained using your AppleID.

3. Install the development *.ipa on a jailed device 

Next, we use Xcode’s “Devices” to install the resigned *.ipa on the device.

Now, we can open the app and play My Tamagotchi Forever as we normally would. 

4. Modifying memory with a debugger

Because we signed the application with our developer certificate, we are now able to attach the debugger. In Xcode, open the “Debug” tab and use the  “Attach to Process” option to select “mytamagotchiforever.”

On successful attachment, pause the application with the debugger. 

We want to increase the number of coins in Tamagotchi from 1340 to 9999, so we need to scan the memory for the integer 1340. We can do that with the following commands:

(lldb) script import lldb.macosx.heap
(lldb) script int(1340).to_bytes(8, 'little')
(lldb) cstr_refs <result of the previous command>

Let’s break down these commands: 

  • script import lldb.macosx.heap imports iOS tools to operate on the heap, i.e. the memory of the game.

  • int(1340).to_bytes(8, 'little') converts the integer1340 into its little endian 64bit hex representation.

  • cstr_refs searches for all occurrences of the integer1340 on the heap. The integer is given in a hexadecimal little endian representation.

It will take a few seconds for cstr_refs to scan the whole memory. Once finished, it will list all of the memory addresses containing the 64bit integer 1340.

There are only a few occurrences, so we will manually overwrite them one by one. Use the command below to overwrite the value at each memory address.

(lldb) memory write 0x00000002801059ac --size 8 9999

Once we have overwritten the respective memory addresses, we can resume and restart the application, which will update our number of coins to 9999.

Alternate Approach: Modifying memory with Frida

An alternate approach to using a debugger is a tool like Frida. Frida’s dynamic code instrumentation toolkit provides an API for injecting arbitrary code into running applications. Once we have Frida installed and configured for a non-jailbroken device, we can spawn the app by using the following command: frida -U -f com.jan-tamagotchi.test --no-pause 

Using Frida’s JavaScript API, we can create a script to overwrite all occurrences of a given value.

 

Let’s divide the script into three parts:

1. Enumerating all memory regions in the game’s process”: 

Process.enumerateRangesSync('rw-').forEach(r =>
  console.log("Found a RW- memory region at" + r.base);
);

2. Searching for all 64bit values that are equal to 1340: 

var res = Memory.scanSync(r.address, r.size, value);
if (res.length > 0){
  console.log("Found the value");
}

3. Overwriting the value for each found memory address:

res[i].writeU64(9999)

The complete script is shown below.

Process.enumerateRangesSync('rw-').forEach(m => {
  // Pattern is a value of `1340` in little endian format
  var pattern = '3C 05 00 00 00 00 00 00';
  try {
    var res = Memory.scanSync(m.base, m.size, pattern);
    for(i=0; i<res.length;i++){
      res[i].address.writeU64(9999)
    }
  } catch (e) {
    continue;
  }
});

How to prevent mobile game tampering

As you can see, it doesn’t take years of reverse engineering experience to hack mobile games. For many mobile games, cheating only requires familiarity with a set of widely available and easy to use tools, like iGameGod or Frida. 

That is why it is more important than ever for game developers to code defensively. The exploits discussed in this article can be easily prevented with a few anti-tampering techniques. Here are a few recommendations to make your mobile games more secure. 

  1. Implement jailbreak checks

Games without jailbreak protection can easily be hacked just by using widely available tools like iGameGod. Even a simple jailbreak check like: 

if FileManager.default.fileExists(atPath: "/Applications/Cydia.app"){
  // A jailbroken device detected
}

makes this approach significantly more difficult. For better jailbreak resistance, we recommend implementing additional anti-tampering checks. Some basic ones are available for free at the Resiliency Against Reverse Engineering page of OWASP-mstg.

  1. Use debugging and repackaging detection

All techniques shown on non-jailbroken devices were possible due to a lack of debugging and repackaging detection. Once the attacker is able to resign the app with its own developer certificate, he can tamper with the app’s memory and patch its behavior. Such apps are often cracked and redistributed with unlocked premium content. OWASP-mstg also offers a handful of free techniques to protect against application repackaging.

  1. Validate resources server-side

Ideally, a player’s resources should be validated on the server side. However, if the game is developed to work offline, you should at least enforce the validation of the most critical features, such as purchasing premium items (e.g diamonds).

  1. Use code obfuscation

Tools like iGameGod scan the memory for a certain value. Therefore, with some basic manual obfuscation, you can efficiently hide it from memory scanners. Examples could include splitting values into two parts, or xor’ing it when reading/writing the value.

Don’t let cheaters game the system

For mobile game developers, much of the revenue comes from in-app purchases such as the diamonds in this Tamagotchi example. Put simply, you can’t afford to let hackers and cheaters get away with exploits like the ones described in this post. As you can see, it’s relatively simple for cheaters to use readily available tools and minimal technical know-how to beat games that are not well-protected. Fortunately, protecting games does not have to be complicated. By taking a layered approach to mobile app security that includes both static and dynamic protections, as described above, you can prevent a large percentage of cheaters from succeeding. Game on!

 

Check out the appendix to see how memory is scanned at runtime >