Reversing and patching Megaman Battle Network 6 in assembly – GBA ROM

Megaman Battle Network 6 – Cyberbeast Gregar takes me back to my childhood. It’s a shame they discontinued the series. For some reason, none of the cheat tables for emulators worked on the money/zenny in this game. Basic memory editing of zenny always seemed to set it to 0. In my frustration, I reverse engineered the ROM and patched it so any money/zenny you gain in game is doubled.

If you just care about getting the ROM and not the technical details, here it is. The only change was acquiring double money/zenny. You can play this ROM on your favorite GBA emulator like mGBA.

Megaman Battle Network 6 – Cyberbeast Gregar GBA ROM patch 1:

https://mega.nz/file/o8oVCDLT#U_aK2P3SFNTGdRfj33G-xpXLkT1U8-VvF-971ttbzcI

UPDATE:

Since the original patch I’ve made a new one with some more features. This new patch of the game includes:

  • Doubles the zenny you find.
  • Unlimited use of sub-items so you can heal outside of combat and use sneaker run to avoid low level monsters.
  • Doubles any HP memory you find. Making it much easier to naturally reach 1000 hp.
  • Increases the amount of megachips you can carry to 19 and gigachips to 5.

Megaman Battle Network 6 – Cyberbeast Gregar GBA ROM patch 2:

https://mega.nz/file/x9wCmYTL#0Dsp92Tyt9gPvqcxUQETMeQqkCuSPSc7OwlQQtqM1Nc

For the details of how I did the patching, see below.

I reviewed the ARM assembly code using Ghidra with the language set to ARM:LE:32:v4t.

The current zenny you have in game is stored at the memory address 0x02001BDC. Modifying it directly ends up setting it to 0. I was able to find the memory address of the zenny by repeatedly searching a list of values in memory. I’d start off with a search of 0, gain 100 zenny, search 100, gain another 100, search for 200, etc. Eventually I ended up with one address that matched my current zenny in game.

I attached a debugger to mGBA and set a watchpoint on the memory address which showed me that the assembly operations below were being accessed when zenny was modified. Every time I gained zenny, I would see the watchpoint trigger a change on the memory address that stores the total zenny I have.

Here’s the assembly code (unpatched) that I found controls zenny gain. This code runs when I find zenny or acquire it after killing a virus/monster.


0803cfcc f0 b5           push       {r4,r5,r6,r7,lr}
0803cfce c9 f7 d3 ff     bl         FUN_08006f78                                     
0803cfd2 11 d1           bne        LAB_0803cff8
0803cfd4 52 46           mov        r2,r10
0803cfd6 d2 6b           ldr        r2,[r2,#0x3c]
0803cfd8 d1 6d           ldr        r1,[r2,#0x5c]
0803cfda 08 4c           ldr        r4,[DAT_0803cffc]                              
0803cfdc 01 23           mov        r3,#0x1
0803cfde a1 42           cmp        r1,r4
0803cfe0 05 d0           beq        LAB_0803cfee
0803cfe2 00 23           mov        r3,#0x0
0803cfe4 09 18           add        r1,r1,r0
0803cfe6 a1 42           cmp        r1,r4
0803cfe8 01 dd           ble        LAB_0803cfee
0803cfea 02 23           mov        r3,#0x2
0803cfec 21 1c           add        r1,r4,#0x0
LAB_0803cfee
0803cfee d1 65           str        r1,[r2,#0x5c]
0803cff0 18 1c           add        r0,r3,#0x0
0803cff2 c9 f7 af ff     bl         FUN_08006f54                                  
0803cff6 f0 bd           pop        {r4,r5,r6,r7,pc}
LAB_0803cff8
0803cff8 01 20           mov        r0,#0x1
0803cffa f0 bd           pop        {r4,r5,r6,r7,pc}
SUB_0803cffc+2
DAT_0803cffc
0803cffc 3f 42 0f 00     undefined4 000F423Fh

The interesting assembly code starts at address 0803cfd8.

0803cfd8 d1 6d           ldr        r1,[r2,#0x5c]

Here, we are reading a value at register r2 + the offset of 5c, the memory address ends up being the zenny location in memory, 0x02001BDC. Then, we load the dereferenced value in register r1. Thus, we are loading the current zenny from memory into the CPU register r1.

One thing you don’t see in the above code is that register r0 contains the zenny we just found. Register r0 is not modified in this function code, it’s set before the function is called. This was revealed to me when I set the watchpoint on the zenny memory address. After I killed a monster, register r0 had the acquired zenny.

At this point we know that register r1 contains our current zenny, and register r0 contains the zenny we are going to acquire.

A bit further down in the assembly code you’ll see that these two registers are added up, and the result is saved in register r1.

    0803cfe4 09 18           add        r1,r1,r0

The new zenny value is saved in r1, but not in memory yet. Next, we take this branch of code below because r1 is always less than or equal to 999k.

        0803cfe6 a1 42           cmp        r1,r4
        0803cfe8 01 dd           ble        LAB_0803cfee

The first instruction in that branch of code writes the new total zenny that’s in register r1 into memory. The memory address used is the same one that the code read from earlier, 0x02001BDC.

    0803cfee d1 65           str        r1,[r2,#0x5c]

So now we understand how the zenny is loaded into memory, added, and then saved back into memory. We’re ready to patch the code.

We have two options with patching the code. We can either modify an existing line, or we can expand the ROM size and add operations. The former is a much simpler option, so we’ll do that.

In the assembly code, we reviewed, I noticed this branch is never used.

    0803cfe0 05 d0           beq        LAB_0803cfee

It’s only ever executed if our zenny is 999k. It’s a perfect candidate to replace. To make the game a little more enjoyable, I decided I wanted more zenny, but not max. I figured I could just double the zenny I gained in the register r0 and let the rest of the code handle the work. So I replaced the branch line above with the below code.

    0803cfe0 00 18           add        r0,r0,r0

The code above adds r0 to r0 and stores in r0.

Here’s how I replaced the code in Ghidra.

image 4

Once replaced it looks like this.

image 5

I then export the raw binary file in Ghidra and rename it from .bin to .gba.GA5Raj9P7fG4X+3m5GlAAAAAElFTkSuQmCC

Let’s test it by running the game in mGBA. Here’s a new game where I have 0 zenny.

image

A few minutes in and I find something… Oh hey, look! Zenny on the floor!

image 1

And there’s 600 zenny inside!

image 2

When we look at the zenny we have in our inventory, it’s double what we found, 1200!

image 3

There you have it. We patched the assembly code in Megaman Battle network 6 to double all of the zenny we found.

Here’s the link again if you want to download and play it.

https://mega.nz/file/o8oVCDLT#U_aK2P3SFNTGdRfj33G-xpXLkT1U8-VvF-971