The Day I Reverse Engineered a Gameboy Advance Game (Revisited)

97d21da8e0ffa8f81218a293482c253a?s=47 Matheus
September 14, 2019

The Day I Reverse Engineered a Gameboy Advance Game (Revisited)

Gameboy Advance was one of the most popular video game platforms of its time and, because of it, many people worked together as a community to study and to document its architecture and develop ROM hacking and other interesting tools.

It turns out that this video game is a fantastic way to start studying reverse engineering: its architecture is very well documented and simpler if compared to the current game console generation – and, of course, it is very fun to work in a game-related project.

So... what do you think of learning reverse engineering through this challenge: developing a level editor for a GBA game called "Klonoa: Empire of Dreams"?
We need to understand the architecture behind ARM hardware, apply reverse engineering in order to discover how the logic of the game works, and then use our front-end knowledge (JS + React.js) to build a level editor.

97d21da8e0ffa8f81218a293482c253a?s=128

Matheus

September 14, 2019
Tweet

Transcript

  1. The day I reverse engineered a Gameboy Advance game With

    @ythecombinator AND @macabeus REVISITED
  2. WTF?! THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE GAME

  3. """

  4. None
  5. soooo…

  6. None
  7. This is macabeus

  8. This is MATHEUS

  9. This is US PLAYING KLONOA

  10. AND THIS IS US BORED

  11. soooo…

  12. WANTED MORE LEVELS TO PLAY WITH

  13. WANTED DIFFERENT LEVELS

  14. soooo…

  15. soooo…

  16. soooo…AND THIS IS KLONNOA

  17. None
  18. None
  19. http://bit.ly/balccon-gba-code

  20. Stretching the bridge THE DAY I REVERSE ENGINEERED A GAMEBOY

    ADVANCE GAME
  21. https://www.nogba.com

  22. https://www.hex-rays.com/products/ida

  23. None
  24. None
  25. This is the tile ID. In this example, all tiles

    with the ID 9B are exactly the bridge part, which is the left corner. 9A is the ID of the continuous part of the bridge.
  26. This tells us where the tile is stored in the

    memory. In this example, it is at 0600F1AD.
  27. https://www.coranac.com/tonc/text/hardware.htm

  28. https://www.coranac.com/tonc/text/hardware.htm The address found is expected 0600F1AD given that everything

    between 0600 and 0601 is part of the section known as VRAM.
  29. Our bridge starts here!

  30. And now we grow it bigger! WTF??!

  31. Understanding the DMA THE DAY I REVERSE ENGINEERED A GAMEBOY

    ADVANCE GAME
  32. VRAM pulls the tilemap
 containing exactly what the player has

    to see from somewhere else that has the entire thing.
  33. https://www.coranac.com/tonc/text/regbg.htm

  34. https://www.coranac.com/tonc/text/regbg.htm Direct Memory Access

  35. https://www.coranac.com/tonc/text/regbg.htm It is a feature of computer systems that allows

    certain hardware subsystems to access main system memory (random- access memory), independent of the CPU
  36. https://www.coranac.com/tonc/text/regbg.htm tldr; a faster copy and paste

  37. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 84000048
  38. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 84000048 The closest to the bytes of our bridge (0600F1AD).
  39. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 84000048 This address is the VRAM data source. It’s located in a region called Fast WRAM.
  40. None
  41. Our bridge starts here!

  42. Stretching it… …It works! And…

  43. Stretching it… …It works! And… WTF??!

  44. Our extra tiles disappear when out of the player vision

    range.
  45. LET’S TALK ABOUT PHYSICS THE DAY I REVERSE ENGINEERED A

    GAMEBOY ADVANCE GAME
  46. Object Attribute Memory

  47. OAM

  48. None
  49. This is the address in which Klonoa is stored on

    the memory
  50. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 8400003E
  51. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 8400003E We can see exactly the byte that writes on Klonoa’s object (03004800).
  52. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 8400003E We can see exactly the byte that writes on Klonoa’s OAM (03004800).
  53. None
  54. None
  55. None
  56. 1 2

  57. None
  58. None
  59. None
  60. None
  61. None
  62. Here is where Y fixes Klonoa’s position Here is where

    which decides if should fix or not Klonoa’s position
  63. None
  64. https://www.coranac.com/tonc/text/hardware.htm

  65. https://www.coranac.com/tonc/text/hardware.htm

  66. None
  67. Let’s find the tilemap on rom THE DAY I REVERSE

    ENGINEERED A GAMEBOY ADVANCE GAME
  68. Cartridge Fast WRAM Display Slow WRAM VRAM > > >

    > The data flow
  69. Cartridge Fast WRAM Display VRAM Slow WRAM > > >

    > Our research flow
  70. swi 11h what?

  71. Swi

  72. SoftWare Interrupt

  73. SoftWare Interrupt = Bios calls

  74. BIOS is a firmware that is intended to initialise the

    physical components of the system In GBA, its BIOS exposes many functions often used in games, including data compression and decompression Each function has an associated numeric code, which must be used as a parameter in the SWI statement
  75. Input: How division works on ARM Assembly swi 0x06 R0


    numerator R1
 denominator R0
 numerator / denominator R1
 numerator % denominator R3
 abs(numerator / denominator) Output:
  76. None
  77. Huffman + lz77 = deflate

  78. None
  79. None
  80. None
  81. None
  82. 6E 6E 6E B0 B1 BD 77 AB B3 AB

    B3 7C 6E 6E 6E 6E 6E 6E 6E 6E 7C 6E 6E 6E 6E 7C 6E 6E
  83. 6E 6E 6E B0 B1 BD 77 AB B3 AB

    B3 7C 6E 6E 6E 6E 6E 6E 6E 6E 7C 6E 6E 6E 6E 7C 6E 6E 6E 6E 6E B0 B1 BD 77 AB B3 AB B3 7C 6E 6E 6E 6E 6E 6E 6E 6E 7C 6E 6E 6E 6E 7C 6E 6E
  84. 6E 6E 6E B0 B1 BD 77 AB B3 AB

    B3 7C 6E 6E 6E 6E 6E 6E 6E 6E 7C 6E 6E 6E 6E 7C 6E 6E 6E 6E 6E B0 B1 BD 77 AB B3 AB B3 7C 6E 6E 6E 6E 6E 6E 6E 6E 7C 6E 6E 6E 6E 7C 6E 6E =
  85. Let’s paint the tilemap THE DAY I REVERSE ENGINEERED A

    GAMEBOY ADVANCE GAME
  86. 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F

  87. 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x1A 0x1B 0x1C 0x1D

    0x1E 0x1F 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F
  88. H · W = 25023
 H,W ∈ ℕ

  89. x x x x a x x x x x

    x x x b x x x x I’m on tile a
  90. x x x x a x x x x x

    x x x b x x x x I’m on tile a x x x x a x x x x x x x x b x x x x From the tile a to b there are 9 bytes (= 9 tiles), so the length of the tilemap is exactly 9
  91. None
  92. Breakpoint here

  93. …is located here This byte…

  94. None
  95. As I change this byte…

  96. …it reflects right here

  97. None
  98. Changing these bytes…

  99. …we drop Klonoa one level below

  100. None
  101. Changing these bytes… …we found the B tile!

  102. 020085DB The length of the level is 420! - 02008437

    = 1A4 (420)
  103. https://github.com/oliver-moran/jimp

  104. const Jimp = require('jimp') const { drop } = require('ramda')

    const fs = require('fs') const data = drop(3, fs.readFileSync('dump/level-2/tilemap'))
  105. const getPixelColor = hexTile "# { if (hexTile === 0)

    { return 0x00000000 } return 0xFFFFFFFF }
  106. new Jimp(300, 600, (err, image) "# { let x =

    0 let y = 0 for (let i = 0; i < data.length; i += 1) { const value = data[i] x += 1 if (x === 300) { y += 1 x = 0 } let color = getPixelColor(value) image.setPixelColor(color, x, y) } image.write('image.png') })
  107. None
  108. None
  109. None
  110. None
  111. None
  112. Let’s find and paint the objects THE DAY I REVERSE

    ENGINEERED A GAMEBOY ADVANCE GAME
  113. None
  114. None
  115. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 8400003E Update the OAM
  116. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 8400003E Update the OAM
  117. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

    0600F000 80000200 DMA3: 03004800 07000000 8400003E Update the OAM 0300480A:0300480B ✘
  118. C

  119. None
  120. None
  121. None
  122. Global OAM

  123. > > > > 08051440 08003CD4 0800B602 0800B634 0804505C

  124. None
  125. ROM OAM

  126. None
  127. None
  128. Global OAM OAM > ROM OAM >

  129. None
  130. ROM OAM of the first level 08367700 083686B0 08369380 ROM

    OAM of the third level ROM OAM of the second level Hole Hole . . .
  131. const [ xFirstPosition, yFirstPosition, xSecondPosition, ySecondPosition, xThirdPosition, yThirdPosition, xFourthPosition, yFourthPosition,

    xFifthPosition, yFifthPosition, kind, ] = qunpack.unpack('v2 x4 v2 x4 v2 x4 v2 x4 v2 x5 c', bytes);
  132. const [ xFirstPosition, yFirstPosition, xSecondPosition, ySecondPosition, xThirdPosition, yThirdPosition, xFourthPosition, yFourthPosition,

    xFifthPosition, yFifthPosition, kind, ] = qunpack.unpack('v2 x4 v2 x4 v2 x4 v2 x4 v2 x5 c', bytes); ✘
  133. const { xStage1, yStage1, xStage2, yStage2, xStage3, yStage3, xStage4, yStage4,

    xStage5, yStage5, kind, } = binary.parse(memory) .word16lu('xStage1').word16lu('yStage1') .skip(4) .word16lu('xStage2').word16lu('yStage2') .skip(4) .word16lu('xStage3').word16lu('yStage3') .skip(4) .word16lu('xStage4').word16lu('yStage4') .skip(4) .word16lu('xStage5').word16lu('yStage5') .skip(5) .word8lu('kind') .vars
  134. None
  135. let’s see the project THE DAY I REVERSE ENGINEERED A

    GAMEBOY ADVANCE GAME
  136. Coding THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE GAME

  137. soooo…

  138. Stack

  139. React.js

  140. Webpack

  141. Ramda.js

  142. architecture

  143. ✂ Brush Scissors

  144. klo-gba.js/scissors/src/visions/

  145. export default { rom: { tilemap: [0x1B27FC, 0x1B36F3], oam: [0xE2B90,

    0xE2F59], portals: [0xD48C8, 0xD48EF], }, tilemap: { totalStages: 3, height: 60, width: 420, scheme: [ { name: 'grass', ids: [0x7D, 0x80, 0x81, 0x82, 0x8E, 0x8F, 0x90], },
  146. export default { rom: { tilemap: [0x1B27FC, 0x1B36F3], oam: [0xE2B90,

    0xE2F59], portals: [0xD48C8, 0xD48EF], }, tilemap: { totalStages: 3, height: 60, width: 420, scheme: [ { name: 'grass', ids: [0x7D, 0x80, 0x81, 0x82, 0x8E, 0x8F, 0x90], },
  147. export default { rom: { tilemap: [0x1B27FC, 0x1B36F3], oam: [0xE2B90,

    0xE2F59], portals: [0xD48C8, 0xD48EF], }, tilemap: { totalStages: 3, height: 60, width: 420, scheme: [ { name: 'grass', ids: [0x7D, 0x80, 0x81, 0x82, 0x8E, 0x8F, 0x90], },
  148. { name: 'rock', ids: [0x54, 0x53, 0x55, 0x56, 0x57, 0x58,

    0x59, }, { name: 'darkRock', ids: [0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, }, { name: 'wood', ids: [0x52, 0x94, 0x98, 0x99, 0x9A, 0x9B, 0x9D, }, { name: 'bridgeRope', ids: [0x04, 0x05, 0x0B, 0x0C, 0x11, 0x13, 0x14,
  149. { name: 'spike', ids: [0x3D], }, ], },

  150. Web assembly

  151. Tilemap compressed Old C code to uncompress it Decompressed Tilemap

    > >
  152. Tilemap compressed Old C code to uncompress it Decompressed Tilemap

    > > Can’t run C code on browser!
  153. Tilemap compressed Old C code to uncompress it Decompressed Tilemap

    > >
  154. Tilemap compressed Old C code to uncompress it Decompressed Tilemap

    > > >Emscripten >WebAssembly
  155. huffman.c lzss.c > > Emscripten huffman.js lzss.js huffman.wasm lzss.wasm +

  156. class HuffmanDecodeError extends Error { constructor (e) { super(`Error when

    tried to use huffman decoder: ${e}`) this.name = 'HuffmanDecodeError' } } huffmanModule.onRuntimeInitialized = () "# {} const huffmanDecode = (buffer) "# { FS.writeFile('file', buffer)
  157. class HuffmanDecodeError extends Error { constructor (e) { super(`Error when

    tried to use huffman decoder: ${e}`) this.name = 'HuffmanDecodeError' } } huffmanModule.onRuntimeInitialized = () "# {} const huffmanDecode = (buffer) "# { FS.writeFile('file', buffer)
  158. const huffmanDecode = (buffer) "# { FS.writeFile('file', buffer) huffmanModule._HUF_Decode() try

    { return huffmanModule.FS.readFile('file', { encoding: 'binary' }) } catch (e) { throw new HuffmanDecodeError(e) } }
  159. function docker_run_emscripten { local filename="$1" echo "Compiling $filename$$." docker run

    \ --rm -it \ -v $(pwd)/scissors/src/wasm:/src \ trzeci/emscripten \ emcc -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"FS\"] -s EXPORT_NAME=\"$filename\" -o ./$filename.js $filename.c } docker_run_emscripten huffman docker_run_emscripten lzss
  160. function docker_run_emscripten { local filename="$1" echo "Compiling $filename$$." docker run

    \ --rm -it \ -v $(pwd)/scissors/src/wasm:/src \ trzeci/emscripten \ emcc -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"FS\"] -s EXPORT_NAME=\"$filename\" -o ./$filename.js $filename.c } docker_run_emscripten huffman docker_run_emscripten lzss
  161. const extractFullTilemap = (romBuffer, [addressStart, addressEnd]) "# romBuffer.slice(addressStart, addressEnd) |>

    huffmanDecode |> lzssDecode
  162. Tilemap

  163. None
  164. ✔ Display tiles and OAM ✔ Allow the user to

    edit it
  165. https://github.com/konvajs/konva

  166. None
  167. 1st load: ˜3 seconds! 2nd load: ˜13 seconds!

  168. After some React.js performance work…

  169. 1st load

  170. 1st load ˜1.8 seconds!

  171. 1st load 2nd load

  172. 1st load 2nd load ˜3.6 seconds!

  173. After switching from Canvas to WebGL…

  174. 1st load

  175. 1st load ˜1.6 seconds!

  176. 1st load ˜1.6 seconds! 2nd load

  177. 1st load ˜1.6 seconds! 2nd load ˜2.8 seconds!

  178. After doing more optimizations on WebGL…

  179. 1st load

  180. 1st load ˜625 ms!

  181. 1st load 2nd load ˜625 ms!

  182. 1st load 2nd load ˜536 ms! ˜625 ms!

  183. 1st load: ˜3 seconds! 2nd load: ˜13 seconds! Perf recap:

    No one is gonna play with this
  184. 1st load: ˜3 1.8 seconds! 2nd load: ˜13 3.6 seconds!

    Perf recap: Removing wasted renders and other React optimisation stuff.
  185. 1st load: ˜3 1.8 1.6 seconds! 2nd load: ˜13 3.6

    2.8 seconds! Perf recap: Canvas → WebGL
  186. 1st load: ˜3 1.8 1.6 0.625 seconds! 2nd load: ˜13

    3.6 2.8 0.536 seconds! Perf recap: React → WebGL
  187. Saving the new tilemap

  188. Each level is stored continually in the memory First Level

    Second Level Third Level
  189. First Level Second Level Third Level > Changes in the

    first level
  190. First Level Second Level Third Level > Customised First Level

    > Changes in the first level
  191. Second Level Third Level First Level Second Level Third Level

    > Customised First Level > Changes in the first level
  192. Second Level Third Level First Level Second Level Third Level

    > Customised First Level > Changes in the first level
  193. None
  194. ARM Thumb Faster to execute Instructions demands less memory Can

    perform instructions more complex
  195. The switch between ARM and Thumb must be done by

    bx instruction. And this instruction only can read a register
  196. Instructions to load a level . . . . .

    . . . . Very big hole space at the end of cartridge First Level__ Second Level__ Third Level__ 081B27FC 081B3E5C 081B50AC Instructions to load a level
  197. First Level__ Second Level__ Third Level__ . . . .

    . . . . . 081B27FC 081B3E5C 081B50AC Instructions to load a level Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380
  198. Instructions to load a level 08367620 FC 27 1B 08

    08367624 00 77 36 08 08367628 5C 3E 1B 08 0836762C B0 86 36 08 08367620 AC 50 1B 08 08367634 80 93 36 08 First Level__ Second Level__ Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380
  199. Instructions to load a level 08367620 FC 27 1B 08

    08367624 00 77 36 08 08367628 5C 3E 1B 08 0836762C B0 86 36 08 08367620 AC 50 1B 08 08367634 80 93 36 08 First Level__ Second Level__ Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380
  200. Instructions to load a level 08367620 FC 27 1B 08

    08367624 00 77 36 08 08367628 5C 3E 1B 08 0836762C B0 86 36 08 08367620 AC 50 1B 08 08367634 80 93 36 08 First Level__ Second Level__ Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380
  201. Instructions to load a level 08367620 FC 27 1B 08

    08367624 00 77 36 08 08367628 5C 3E 1B 08 0836762C B0 86 36 08 08367620 AC 50 1B 08 08367634 80 93 36 08 First Level__ Second Level__ Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380
  202. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 Original loader Patched loader 08043B0A mov r4,r0 08043B0C bl 08367610h 08043B10 bl 0805143Ch 08043B0A mov r4,r0 08043B0C add r0,r5,4 08043B0E mov r1,r4 08043B10 bl 0805143Ch
  203. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 R0 is the pointer to where is the tilemap that will load Original loader Patched loader 08043B0A mov r4,r0 08043B0C bl 08367610h 08043B10 bl 0805143Ch 08043B0A mov r4,r0 08043B0C add r0,r5,4 08043B0E mov r1,r4 08043B10 bl 0805143Ch
  204. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 Original loader Patched loader 08043B0A mov r4,r0 08043B0C bl 08367610h 08043B10 bl 0805143Ch 08043B0A mov r4,r0 08043B0C add r0,r5,4 08043B0E mov r1,r4 08043B10 bl 0805143Ch
  205. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 08367610 mov r0,r15 08367612 add r0,3Ch 08367614 bx r0
  206. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 08367650 add r0,r4,4h 08367654 ldr r4,[r15,#-3Ch] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-40h] 08367654 ldr r4,[r15,#-40h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-44h] 08367654 ldr r4,[r15,#-44h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-48h] 08367660 mov r4,r1 08367664 bx r14
  207. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 08367650 add r0,r4,4h 08367654 ldr r4,[r15,#-3Ch] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-40h] 08367654 ldr r4,[r15,#-40h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-44h] 08367654 ldr r4,[r15,#-44h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-48h] 08367660 mov r4,r1 08367664 bx r14
  208. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 08367650 add r0,r4,4h 08367654 ldr r4,[r15,#-3Ch] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-40h] 08367654 ldr r4,[r15,#-40h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-44h] 08367654 ldr r4,[r15,#-44h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-48h] 08367660 mov r4,r1 08367664 bx r14
  209. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 08367650 add r0,r4,4h 08367654 ldr r4,[r15,#-3Ch] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-40h] 08367654 ldr r4,[r15,#-40h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-44h] 08367654 ldr r4,[r15,#-44h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-48h] 08367660 mov r4,r1 08367664 bx r14
  210. Instructions to load a level (patched) First Level__ Second Level__

    Third Level__ . . . . . . . . . 081B27FC 081B3E5C 081B50AC Customised First Level__ Customised level loader Constant addresses map table Hole Customised Second Level__ Hole Customised Third Level__ Hole Hole 08367700 083686B0 08369380 08367650 add r0,r4,4h 08367654 ldr r4,[r15,#-3Ch] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-40h] 08367654 ldr r4,[r15,#-40h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-44h] 08367654 ldr r4,[r15,#-44h] 08367658 cmp r0,r4 0836765C ldreq r0,[r15,#-48h] 08367660 mov r4,r1 08367664 bx r14
  211. 1. Redirect to the patched code 2. Check if the

    R0 is on the table 3. If yes, load the associated value 4. Return to the original loader
  212. None
  213. None
  214. None
  215. None
  216. Result THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE GAME

  217. None
  218. None
  219. None
  220. None
  221. None
  222. THE Future THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE

    GAME
  223. None
  224. None
  225. WE ARE... THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE

    GAME
  226. MACABEUS macabeus ____ www.macalogs.com.br _ _ Software Engineer, full-stack @CASAFARI

    Always creating crazy projects
  227. MATHEUS ALBUQUERQUE YTHECOMBINATOR www.ythecombinator.space LAND@YTHECOMBINATOR.SPACE Software Engineer, Front- End @STRV

    addicted to emojis, memes and beer
  228. Benjamin Stauffer kcowolf TRANSLATOR

  229. Raul peres behance.net/Kniksis illustrator

  230. This talk THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE

    GAME
  231. http://bit.ly/balccon-gba-keynote

  232. http://bit.ly/balccon-gba-code

  233. http://bit.ly/balccon-gba-posts

  234. None
  235. We got stickers!

  236. Hvala THANKS @ythecombinator AND @macabeus