One of my first contacts with ‘hacking’ was when I got an action replay (cheating device) for my ds a long time ago. I remember spending countless hours looking for these magic codes on the internet, and entering them on the small screen by hand. For hours I would compare multiple cheat codes together, I would play around with the values, I would look for similarities and differences between them trying to understand how they worked. This was one of the first steps that introduced me to the world of reverse engineering. During the follwing years I continued honing my skills in reverse engineering by doing side projects and CTF competitions. But even though I grasped more concepts I still had this voice at the back of my head telling me: “I bet you can create your own cheats”. But it changed recently after reading this article from cturt where he goes through the process of fixing a GBA game. I decided that it was time to try my hand at hacking some old school games and see how far I could go. For this experiment I will be using the game “The Legend of Zelda: A Link to the past” on GBA (md5: 3287ca66e5cc285a9fe3a922051e84c6).

But before we start let’s have an overview of the system we are targeting. The GBA is an ARM cpu that uses extensively the THUMB mode, a reduced subset of ARM. If you are already familiar with assembly the code will not be hard to understand as the instructions are straightforward in THUMB. But If you are not familiar with arm I encourage you to read the guide made by Azeria here.

Goal

This project will not be a tutorial on “how to create X cheat” but will be more of a exploration log. The goal is more to enjoy the journey experimenting with various techniques and understanding the code than making cheats. In that regard they are more means to an end. With that said, let’s get reversing !

Look ma’ ! I’m invincible !

Being invincible is always the first cheat that comes to mind when talking about cheat codes. On old games it is also one of the easiest to make as the health is almost always stored at a fixed address in RAM. The most common way of finding this value is to do a memory search. By looking at changing values when our character is hurt/heals, we can quickly narrow down the search to a handful of addresses. After some debugging it is easy to find our health.

This method is the classic way of finding interesting values. It is easy to do, and most of the time gives good results. But here we are not interested in easy techniques and that is why we are going to take a completly different path ;);

Doing static analysis or debugging directly on the rom is too complex as there is too much code and we don’t know what is being executed. We have to find a technique to narrow down our search to code that is useful while skipping everything else. A simple way of doing this is through code coverage analysis

Code coverage ?

Code coverage is a metric giving out how much code is used by a program during its execution. This coverage is computed through execution traces that are gathered either by binary instrumentation (drcov/valgrind) or by hardware tracers (such as Intel pt). Most of the time developers use this metric to see how much of their code is tested through their unit tests, but it can also be used for other purposes.

These tracers collect the addresses of basic blocks. In machine code a basic block (or block for short) is a list of instructions that ends with a branching instruction. This is the smallest code structure over the single instruction. By using these basic block addresses and debug informations, the code coverage tools can tell which lines of code were executed. Unfortunately these tools will not work in our situation as our device is exotic, which means that we have to build our own tools. We need to create two different components. The first one is the tracer and the second one the viewer.

For the tracer I chose to modify the very good mGBA emulator. I added logging code on ARM and THUMB branching instructions as well as two new debugger commands to record traces:

coverage-start : To start recording

: To start recording coverage-stop <output.cov> : To stop recording and save the trace

For the viewer I wanted something similar to lighthouse. I implemented it as a quick and dirty binary ninja plugin, but it should easily be portable to other tools. Its most basic features are basic block highlighting, function report and basic block report. I chose to add the basic block report as this granularity will prove to be useful in our situation. You can see the result below:

The graph view:

Function report:

BB Report:

The attack plan

Having all these tools is great but what do we do with them ? If we capture a trace and feed it directly to the tool we will be greeted by a few dozen functions inside our report, and maybe a few hundred blocks. So before starting manual analysis we will need to cut down as much as possible the amount of blocks in traces as we don’t care about code that is always running, such as timers, NPC movements or rendering routines.

A simple way to get rid of this useless code is to take two traces. We take a first one doing everything but the event we are targeting, and then we take a second one triggering our target event. In the end we substract the two traces together and we get only the basic blocks related to our event.

Another benefit we get from this approach is that we can target abstract events. By relying solely on RAM watching we can target values such as the health or money. But when we get to more abstract concepts such as user <-> world interaction, it gets harder to find anything of value here.

Before recording our event we have to record some noise. To do this we record a normal gameplay session triggering every possible event except the one we target. The goal is to record as much unrelated code as possible to simplify our next trace. For the second trace we have to make our target block recognisable. The easiest way to trigger code manipulating Link’s health is to get attacked by a NPC. At one point Link’s health must decrease via a substraction, so our goal is to find a block with a subs instruction. The binja plugin can show us the amount of hits on a specific basic block. To cut through the noise and detect our code more easily we can trigger our event a specific number of times. For this example I got Link hit 4 times, which resulted in a trace with 54 different blocks with 4 hits.

After a few minutes searching for our subs instruction we find the block at address 0x809dae2.

To verify that this is the right block we can start the debugger and take a few hits:

> break/t 0x0809dafe > c Hit breakpoint at 0x0809DAFE r0: 00000018 r1: 00000004 r2: 00000ECE r3: 00000960 r4: 03002230 r5: 0200234D r6: 030039DA r7: 03002230 r8: 03007EC6 r9: 04000000 r10: 04000200 r11: 00000000 r12: 00000005 r13: 03007E70 r14: 0812C8A1 r15: 0809DB00 cpsr: 0000003F [------T] 0809DAFE: BE00 bkpt > c Hit breakpoint at 0x0809DAFE r0: 00000014 r1: 00000004 r2: 00000ECE r3: 00000960 r4: 03002230 r5: 0200234D r6: 030039DA r7: 03002230 r8: 03007EC6 r9: 04000000 r10: 04000200 r11: 00000000 r12: 00000005 r13: 03007E70 r14: 0812C8A1 r15: 0809DB00 cpsr: 0000003F [------T] 0809DAFE: BE00 bkpt >

The game save I am using for testing is during the tutorial. In this first section of the game Link has 3 hearts and the NPCs deal half a heart of damage. If we consider the register r0 to contain the health and r1 to contain the damage then everything seems to work. 0x18 divided by 3 equals 8, which is exactly twice as big as r1. We appear to have found the correct block ! The value inside r0 was loaded from address 0x200234d, a global variable. We can rename it as it can prove to be useful in the future. If we were to make a AR/Gameshark code we could stop here and call it a day. But why not patching the game code instead ?

From our previous findings we know that a heart is 8 health units. As there is a maximum of 20 hearts in Zelda we can compute that the maximum is 0xa0 health units. For the patch we simply replace the subs r0, r0, r1 instruction by a movs r0, #0xa0 (a nop would also have been possible). We can do the patch using radare2 with following command:

$ r2 -w -b 16 -qc "wx a020 @ 0x0809dafe" <your rom>

Instakill

Right now Link is invincible, but to save the kingdom of Hyrule he will need more firepower. Right know he can kill its opponents in two hits. Why not granting him the ability to instantly kill them ?

We are using the same process as previously and start by recording a nop trace. After recording Link hitting NPCs 6 times we open the trace. We are greeted with 126 entries with 6 hits, which is a lot but still doable by hand. As for the last trace we are looking for a subs instruction. In a few minutes we stumble upon an interesting block at address 0x80c22a4.

Let’s put a breakpoint on 0x080c22b8 and give a few hits:

> b/t 0x080c22b8 > c Hit breakpoint at 0x080C22B8 r0: 00000004 r1: 00000002 r2: 03003252 r3: 00000960 r4: 030030E5 r5: 00000000 r6: 03002230 r7: 030031D2 r8: 00000000 r9: 00000419 r10: 00000419 r11: 00000000 r12: 00000000 r13: 03007E50 r14: 080C1EC5 r15: 080C22BA cpsr: 0000003F [------T] 080C22B8: BE00 bkpt > c Hit breakpoint at 0x080C22B8 r0: 00000002 r1: 00000002 r2: 03003252 r3: 00000960 r4: 030030E5 r5: 00000000 r6: 03002230 r7: 030031D2 r8: 00000000 r9: 0000041E r10: 0000041E r11: 00000000 r12: 00000000 r13: 03007E50 r14: 080C1EC5 r15: 080C22BA cpsr: 0000003F [------T] 080C22B8: BE00 bkpt >

r0 is two time bigger than r1, so this function seems to be the right one (in two hits r0 would reach 0). The only way to be the sure is to patch the code. We can either set r0 to 0 or set r1 to a huge value. I chose to do the former here. We will replace ldrb r1, [r4] by mov r1, #0xff . The radare2 command for patching is this one:

$ r2 -w -b 16 -qc "wx ff21 @ 0x080c22b6" <your rom>

Unlimited power !

Link is now invincible and incredibly strong. But he is still lacking in the magic department, so let’s give him a little push.

In Link to the past the magic gauge is important as all special and powerful objects use it. It is a scarce resource that must be used wisely. Naturally we don’t want to be limited in our actions so we will fix that. We use the same technique as before. After recording the nop trace we use our Lantern 7 times (as it uses magic) and load the trace in the tool. We have 93 corresponding entries, and after looking around we find the following couple of blocks:

We have our subs instruction in the middle block. We can suppose that r0 is our current amount of magic and that r1 is the cost. We can verify using the debugger.

> b/t 0x80a3b98 > c Hit breakpoint at 0x080A3B98 r0: 00000048 r1: 00000004 r2: 03002230 r3: 0200234E r4: 00000008 r5: 00000000 r6: 03003948 r7: 03002BE2 r8: 03007EC6 r9: 04000000 r10: 04000200 r11: 00000000 r12: 0300276B r13: 03007E48 r14: 080A2B29 r15: 080A3B9A cpsr: 2000003F [--C---T] 080A3B98: BE00 bkpt > c Hit breakpoint at 0x080A3B98 r0: 00000044 r1: 00000004 r2: 03002230 r3: 0200234E r4: 00000008 r5: 00000000 r6: 03003948 r7: 03002BE2 r8: 03007EC6 r9: 04000000 r10: 04000200 r11: 00000000 r12: 0300276B r13: 03007E48 r14: 080A2B29 r15: 080A3B9A cpsr: 2000003F [--C---T] 080A3B98: BE00 bkpt >

The increments of the magic gauge are small so a value as huge as 0x48 is not shocking. After the subs the result is stored into r1 before being written to 0x200234e (current_magic global variable). Unfortunately when we set r1 to a high value we are greeted with a nasty “not enough magic” dialog. The reason may be that this value must obey to a few other constraints and cannot be any value. For now we can just fiddle with the value until we find one that works. For this patch we are going to use 0x7c as it gives us a full magic gauge. We patch the subs instruction with movs r1, #0x7c :

$ r2 -w -b 16 -qc "wx 7c21 @ 0x080a3b98" <your rom>

Bonus: Weird values

By trying to understand the NPC damage function I found a few pretty interesting blocks inside.

These two blocks are executed before the block used for instakill (the block 0x080c21d4 is its parent). This code tries to match on interesting values of r1 (the damage), and if they are not found the instakill block is executed. The comparison shows that these values go through different codepaths than “normal” damage, what could they be ? From this code we can determine that 0xfd is a special value and that (0x7b, 0x7c, 0x7e, 0x7f) are different special values. As always, let’s patch the code and see:

For damage = 0xfd:

For damage = 0xfb, 0xfc, 0xfe, 0xff:

These specific values are used to inflict status effects on entities !

Fire patch r2 -w -b 16 -qc "wx fd20 @ 0x080c218e" <your rom>

Paralysis patch r2 -w -b 16 -qc "wx ff20 @ 0x080c218e" <your rom>

Conclusion

The cheats presented here are more than fun gimmicks, they are the stepping stones to future research. During their discovery we indirectly discovered interesting facts from the game such as the purpose of some global variables, functions, and even game mechanics (health system and special damage values). By exploring the related functions we should be able to make sense of the related code. By repeating this process we can progressively recover the purpose of more and more code.