The Day I Reverse Engineered a Gameboy Advance Game (Coding Portugal meetup)

The Day I Reverse Engineered a Gameboy Advance Game (Coding Portugal meetup)

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.

F380535da59d6cdd5754e2e31bda8a0e?s=128

Bruno Macabeus

October 30, 2019
Tweet

Transcript

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

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

  3. soooo…

  4. None
  5. This is me

  6. This is my friend

  7. This is US PLAYING KLONOA

  8. This is US PLAYING KLONOA

  9. This is US PLAYING KLONOA

  10. But we were bored

  11. soooo…

  12. I want MORE LEVELS TO PLAY WITH

  13. WANTED DIFFERENT LEVELS

  14. soooo…

  15. soooo…

  16. soooo…

  17. soooo…AND THIS IS KLONNOA

  18. soooo…AND THIS IS KLONNOA

  19. None
  20. None
  21. None
  22. http://bit.ly/klo-gba-js-repo

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

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

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

  26. None
  27. None
  28. 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.
  29. This tells us where the tile is stored in the

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

  31. 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.
  32. Our bridge starts here!

  33. Our bridge starts here!

  34. And now we grow it bigger!

  35. And now we grow it bigger!

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

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

    ADVANCE GAME
  38. None
  39. None
  40. VRAM pulls the tilemap
 containing exactly what the player has

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

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

  43. 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
  44. https://www.coranac.com/tonc/text/regbg.htm tldr; a faster copy and paste

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

    0600F000 80000200 DMA3: 03004800 07000000 84000048
  46. 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).
  47. 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.
  48. None
  49. Our bridge starts here!

  50. Stretching it…

  51. Stretching it… …It works! And…

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

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

  54. None
  55. None
  56. Our extra tiles disappear when out of the player vision

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

    GAMEBOY ADVANCE GAME
  58. Object Attribute Memory

  59. OAM

  60. None
  61. This is the address in which Klonoa is stored on

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

    0600F000 80000200 DMA3: 03004800 07000000 8400003E
  63. 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).
  64. 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).
  65. None
  66. None
  67. None
  68. None
  69. None
  70. 1 2

  71. None
  72. None
  73. None
  74. None
  75. None
  76. None
  77. None
  78. None
  79. None
  80. None
  81. None
  82. Here is where Y fixes Klonoa’s position

  83. Here is where Y fixes Klonoa’s position Here is where

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

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

  87. None
  88. None
  89. Let’s find the tilemap on rom THE DAY I REVERSE

    ENGINEERED A GAMEBOY ADVANCE GAME
  90. Cartridge > > > > The data flow

  91. Cartridge Slow WRAM > > > > The data flow

  92. Cartridge Fast WRAM Slow WRAM > > > > The

    data flow
  93. Cartridge Fast WRAM Slow WRAM VRAM > > > >

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

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

    > Our research flow
  96. None
  97. swi 11h what?

  98. Swi

  99. SoftWare Interrupt

  100. SoftWare Interrupt = Bios calls

  101. None
  102. BIOS is a firmware that is intended to initialise the

    physical components of the system
  103. 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
  104. 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
  105. Input: How division works on ARM Assembly swi 0x06 Output:

  106. Input: How division works on ARM Assembly swi 0x06 R0


    numerator R1
 denominator Output:
  107. Input: How division works on ARM Assembly swi 0x06 R0


    numerator R1
 denominator R0
 numerator / denominator R1
 numerator % denominator R3
 abs(numerator / denominator) Output:
  108. None
  109. None
  110. None
  111. None
  112. None
  113. None
  114. 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
  115. 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
  116. 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
  117. 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
  118. 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 =
  119. Let’s paint the tilemap THE DAY I REVERSE ENGINEERED A

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

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

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

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

  124. H · W = 25023
 H,W ∈ ℕ

  125. None
  126. x x x x a x x x x x

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

    x x x b x x x x I’m on tile a
  128. 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
  129. None
  130. Breakpoint here

  131. …is located here This byte…

  132. None
  133. As I change this byte…

  134. …it reflects right here

  135. None
  136. Changing these bytes…

  137. …we drop Klonoa one level below

  138. None
  139. Changing these bytes… …we found the B tile!

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

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

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

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

    { return 0x00000000 } return 0xFFFFFFFF }
  144. 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') })
  145. None
  146. None
  147. None
  148. None
  149. None
  150. let’s see the project THE DAY I REVERSE ENGINEERED A

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

  152. Stack

  153. React.js ✈

  154. Former-Kit ✈

  155. Ramda.js ✈

  156. architecture

  157. ✂ Brush Scissors

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

  159. export default { rom: { tilemap: [0x1B27FC, 0x1B36F3], customTilemap: [0x367700,

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

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

    0x3686AF], objects: [0xE2B90, 0xE2F59], portals: [0xD48C8, 0xD48EF], }, tilemap: { totalStages: 3, height: 60, width: 420, scheme: [ { name: 'grass', ids: [0x7D, 0x80, 0x81, 0x82, 0x8E, 0x8F, 0x90],
  162. { 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,
  163. { name: 'spike', ids: [0x3D], }, ], },

  164. Web assembly

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

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

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

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

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

  170. 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)
  171. 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)
  172. const huffmanDecode = (buffer) "# { FS.writeFile('file', buffer) huffmanModule._HUF_Decode() try

    { return huffmanModule.FS.readFile('file', { encoding: 'binary' }) } catch (e) { throw new HuffmanDecodeError(e) } }
  173. 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
  174. 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
  175. None
  176. const extractFullTilemap = (romBuffer, [addressStart, addressEnd]) "# romBuffer.slice(addressStart, addressEnd) |>

    huffmanDecode |> lzssDecode
  177. Saving the new tilemap

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

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

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

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

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

    > Customised First Level > Changes in the first level
  183. None
  184. ARM Thumb

  185. ARM Thumb Faster to execute Instructions demands less memory

  186. ARM Thumb Faster to execute Instructions demands less memory Can

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

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

    . . . . Very big hole at the end of cartridge First Level__ Second Level__ Third Level__ 081B27FC 081B3E5C 081B50AC Instructions to load a level
  189. 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
  190. 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
  191. 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
  192. 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
  193. 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
  194. 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
  195. 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
  196. 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
  197. 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
  198. 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
  199. 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
  200. 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
  201. 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
  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 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
  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 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
  204. 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
  205. 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
  206. 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
  207. 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
  208. 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
  209. None
  210. Result THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE GAME

  211. None
  212. None
  213. None
  214. None
  215. None
  216. None
  217. WE ARE... THE DAY I REVERSE ENGINEERED A GAMEBOY ADVANCE

    GAME
  218. MACABEUS macabeus ____ www.macalogs.com.br _ _ Software Engineer Always creating

    crazy projects
  219. MATHEUS ALBUQUERQUE YTHECOMBINATOR www.ythecombinator.space LAND@YTHECOMBINATOR.SPACE Software Engineer,
 Front-End addicted to

    emojis, memes and beer
  220. Benjamin Stauffer kcowolf TRANSLATOR

  221. Raul peres behance.net/Kniksis illustrator

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

    GAME
  223. http://bit.ly/balccon-gba http://bit.ly/theconf-gba

  224. http://bit.ly/klo-gba-js-repo

  225. http://bit.ly/gba-posts

  226. Obrigado THANKS @macabeus

  227. São Paulo Lisbon

  228. None
  229. soccer, caipirinha and carnival

  230. soccer, caipirinha and carnival ✘

  231. None
  232. None
  233. None
  234. ✔ Display tiles and OAM ✔ Allow the user to

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

  236. None
  237. 1st load: ˜3 seconds! 2nd load: ˜13 seconds!

  238. After some React.js performance work…

  239. 1st load

  240. 1st load ˜1.8 seconds!

  241. 1st load 2nd load

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

  243. After switching from Canvas to WebGL…

  244. 1st load

  245. 1st load ˜1.6 seconds!

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

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

  248. After doing more optimizations on WebGL…

  249. 1st load

  250. 1st load ˜625 ms!

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

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

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

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

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

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

    3.6 2.8 0.536 seconds! Perf recap: React → WebGL
  257. None
  258. None
  259. DMA3: 03000900 0600E000 80000400 DMA3: 03001100 0600E800 80000400 DMA3: 03004DB0

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

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

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

  263. None
  264. None
  265. None
  266. None
  267. None
  268. Global OAM

  269. Global OAM

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

  271. None
  272. ROM OAM

  273. None
  274. None
  275. None
  276. None
  277. Global OAM OAM > ROM OAM >

  278. None
  279. ROM OAM of the first level 08367700 083686B0 08369380

  280. ROM OAM of the first level 08367700 083686B0 08369380 ROM

    OAM of the third level ROM OAM of the second level Hole Hole . . .
  281. 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);
  282. 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); ✘
  283. 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
  284. None
  285. None