Mercurial > vba-clojure
view org/total-control.org @ 620:1b52b14868d3 tip
merge.
author | rlm <rlm@aurellem.org> |
---|---|
date | Sun, 07 Feb 2016 23:16:04 -0800 |
parents | 90575d3a64d1 a79e5a852347 |
children |
line wrap: on
line source
1 #+title: Pokemon Yellow Total Control Hack2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: Taking over Pokemon Yellow from the inside.5 #+keywords: pokemon, pokemon yellow, rom, gameboy, assembly, hex, pointers, clojure6 #+SETUPFILE: ../../aurellem/org/setup.org7 #+INCLUDE: ../../aurellem/org/level-0.org8 #+BABEL: :exports both :noweb yes :cache no :mkdirp yes9 #+OPTIONS: num:212 Full Source : http://hg.bortreb.com/vba-clojure15 #+BEGIN_HTML html16 <div class="figure">17 <p>18 <iframe width="420" height="315"19 src="https://www.youtube.com/embed/p5T81yHkHtI"20 frameborder="0" allowfullscreen></iframe>21 <p>22 #+END_HTML23 Youtube Video w/ Visual Keypresses: http://www.youtube.com/watch?v=p5T81yHkHtI24 #+BEGIN_HTML25 </div>26 #+END_HTML28 Special Thanks to:30 - http://tasvideos.org/2913S.html for the save corruption hack which31 is used at the start of this run.32 - http://www.everyponysings.com/ for providing the midi file I used33 to create the song at the end.34 - http://www.zophar.net/ for the terminal font.37 * Introduction39 Think of pokemon yellow as creating a little universe with certain40 rules. Inside that universe, you can buy items, defeat rival trainers,41 and raise your pokemon. But within that universe, you are bound by the42 rules of pokemon. You can't build new buildings, or change the music,43 or change your clothes.. There are some games (like chess), where it44 is not possible to alter the rules of the game from within the45 game. No matter what moves you make in chess, you can never change the46 rules of the game so that it becomes checkers or basketball. The point47 of this run is to show that you CAN change the rules in pokemon48 yellow. There is a certain sequence of valid actions (like walking49 from one place to another or buying items) that will allow you to50 transform pokemon yellow into Pacman, or Tetris, or Pong, or a MIDI51 player, or anything else you can imagine.53 * Background55 [[http://tasvideos.org/2913S.html][This speedrun]] by Felipe Lopes de Freitas (p4wn3r), beats pokemon56 yellow in only 1 minute and 36 seconds. It does it by corrupting the57 in-game item list so that he can advance the list past its normal58 limit of 20 items. The memory immediately after the item list includes59 the warp points for the current map, and by treating that data as60 items and switching and dropping them, he can make the door from his61 house take him directly to the end of the game.63 When I first saw that speedrun, I was amazed at how fast pokemon64 yellow could be beaten, and that it was possible to manipulate the65 game from the inside, using only the item list. I wondered how far I66 could extend the techniques found in p4wn3r's run.68 The gameboy is an 8 bit computer. That means that ultimately, anything69 that happens in pokemon is a result of the gameboy's CPU reading a70 stream of 8 bit numbers and doing whatever those numbers mean. For71 example, in the gameboy, the numbers:73 [62 16 37 224 47 240 37 230 15 55]75 mean to check which buttons are currently pressed and copy that result76 into the "A" register. With enough numbers, you can spell out an77 interactive program that reads input from the buttons and allows you78 to write any program you want to the gameboy. Once you have assembled79 such a program and forced the game to run it, you have won, since you80 can use that program to write any other program (like Tetris or81 Pacman) over pokemon yellow's code. I call a program that allows you82 to write any other program a "bootstrapping program". So, the goal is83 to somehow get a bootstrapping program into pokemon yellow and then84 force yellow to run that program instead of its own.86 How can we spell out such a program? Everything in the game is87 ultimately numbers, including all items, pokemon, levels, etc. In88 particular, the item list looks like:90 #+begin_example91 item-one-id (0-255)92 item-one-quantity (0-255)93 item-two-id (0-255)94 item-two-quantity (0-255)95 .96 .97 .98 #+end_example100 Let's consider the button measuring program [37 62 16 37 224 37 240101 37 230 15 55] from before. Interpreted as items and item quantities, it is103 #+begin_example104 lemonade x16105 guard spec. x224106 leaf stone x240107 guard spec. x230108 parlyz heal x55109 #+end_example111 So, if we can get the right items in the right quantities, we can112 spell out a bootstrapping program. Likewise, when writing the113 bootstrapping program, we must be careful to only use numbers that are114 also valid items and quantities. This is hard because there aren't115 many different items to work with, and many machine instructions116 actually take 2 or even 3 numbers in a row, which severely restricts117 the types of items you can use. I ended up needing about 92 numbers to118 implement a bootstrap program. Half of those numbers were elaborate119 ways of doing nothing and were just there so that the entire program120 was also a valid item list.122 The final part of the hack is getting pokemon yellow to execute the123 new program after it has been assembled with items. Fortunately,124 pokemon keeps a number called a function pointer within easy reach of125 the corrupted item list. This function pointer is the starting point126 (address) of a program which the game runs every so often to check for127 poison and do general maintenance. By shifting an item over this128 function pointer, I can rewrite that address to point to the129 bootstrapping program, and make the game execute it. Without this130 function pointer, it would not be possible to take over the game.132 * The Run134 I start off and name my rival Lp/k. These characters will eventually be135 treated as items and shifted over the function pointer, causing it to136 execute the bootstrapping program that will soon be constructed. I137 start the run the same as p4wn3r's and restart the game while saving,138 so that the pokemon list is corrupted. By switching the 8th and 10th139 pokemon, I corrupt the item list and can now scroll down past the 20th140 item. I shift items around to increase the text speed to maximum and141 rewrite the warp point of my house to Celadon Dept. Store. (p4wn3r142 used this to go directly to the hall of fame and win the game in his143 run.) I deposit many 0x00 glitch items into the PC from my corrupted144 inventory for later use. Then, I withdraw the potion from the145 PC. This repairs my item list by overflowing the item counter from146 0xFF back to 0x00, though the potion is obliterated in the process. I147 then take 255 glitch items with ID 0x00 from the computer into my148 personal items.150 Leaving my house takes me directly to Celadon Dept. store, where I151 sell two 0x00 items for 414925 each, giving myself essentially max152 money. I hit every floor of the department store, gathering the153 following items:155 #+begin_example156 +-------------------+----------+157 |##| Item | Quantity |158 +--+----------------+----------+159 |1 | TM02 | 98 |160 |2 | TM37 | 71 |161 |3 | TM05 | 1 |162 |4 | TM09 | 1 |163 |5 | burn-heal | 12 |164 |6 | ice-heal | 55 |165 |7 | parlyz-heal | 99 |166 |8 | parlyz-heal | 55 |167 |9 | TM18 | 1 |168 |10| fire-stone | 23 |169 |11| water-stone | 29 |170 |12| x-accuracy | 58 |171 |13| guard-spec | 99 |172 |14| guard-spec | 24 |173 |15| lemonade | 16 |174 |16| TM13 | 1 |175 +--+----------------+----------+176 #+end_example178 After gathering these items, I deposit them in the appropriate order179 into the item PC to spell out my bootstrapping program. Writing a full180 bootstrap program in one go using only items turned out to be too181 hard, so I split the process up into three parts. The program that I182 actually construct using items is very limited. It reads only from the183 A, B, start, and select buttons, and writes 4 bits each frame starting184 at a fixed point in memory. After it writes 200 or so bytes, it jumps185 directly to what it just wrote. In my run, I use this program to write186 another bootstrapping program that can write any number of bytes to187 any location in memory, and then jump to any location in memory. This188 new program also can write 8 bits per frame by using all the189 buttons. Using this new bootstrap program, I write a final190 bootstrapping program that does everything the previous bootstrapping191 program does except it also displays the bytes it is writing to memory192 on the screen.194 After completing this bootstrapping program, I go to the Celadon195 mansion, because I find the metaness of that building to be196 sufficiently high to serve as an exit point for the pokemon197 universe. I corrupt my item list again by switching corrupted pokemon,198 scroll down to my rival's name and discard until it is equal to the199 address of my bootstrapping program, and then swap it with the200 function pointer. Once the menu is closed, the bootstrapping program201 takes over, and I write the payload....203 * Infrastructure205 The entire video was completely produced by bots --- I didn't manually206 play the game at all to produce this speedrun. Here is a brief account207 of the infrastructure I built to make the video. The entire source of208 the project is available at http://hg.bortreb.com/vba-clojure210 The first step was to build a programmatic interface to pokemon211 yellow. So, I downloaded vba-rerecording from212 http://code.google.com/p/vba-rerecording/. After repairing their213 broken auto-tools scripts so that it would compile on GNU/Linux, I214 added a low level C interface that I could call from Java via215 JNI. This C interface gives me basic control over the emulator: I can216 step the emulator either one clock cycle or one frame, and I can get217 the contents of any memory location or register. The interface also218 allows me to freeze the state of the emulator, save it to a Java219 object, and reload that state again at any time.221 I built a layer of [[http://clojure.org/][clojure]] code on top of the JNI bindings to get an222 entirely functional interface to vba-rerecording. This interface223 treats state of the emulator as an immutable object, and allows me to224 do everything I could do with the lower level C interface in a225 functional manner. Using this functional code, I wrote search programs226 that take a particular game-state and try out different combinations227 of button presses to get any desired effect. By combining different228 styles of search with different initial conditions, I created high229 level functions that could each accomplish a certain general task,230 like walking and buying items. For example, here is some actual code:232 #+begin_src clojure233 (defn-memo viridian-store->oaks-lab234 ([] (viridian-store->oaks-lab235 (get-oaks-parcel)))236 ([script]237 (->> script238 (walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓239 ← ← ← ← ← ← ← ← ←240 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓241 ← ←242 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓243 ↓ ↓ ↓ ↓ ↓ ↓ ↓244 → → → → → → → →245 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓246 ← ← ← ← ←247 ↓ ↓ ↓ ↓248 ])249 (walk-thru-grass250 [↓ ↓ ↓ ↓ ↓ ↓ ↓])251 (walk [↓ ↓ ← ↓ ↓ ↓ ←252 ↓ ↓ ↓ ↓ ↓ ↓253 → → → ↑])255 (do-nothing 1))))256 #+end_src258 This script walks from the Viridian City pokemon store to Oak's259 Lab in the most efficient way possible. The walk-thru-grass function260 guarantees that no wild battles will happen by manipulating the game's261 random number generator.263 #+begin_src clojure264 (defn-memo hacking-10265 ([] (hacking-10 (hacking-9)))266 ([script]267 (->> script268 begin-deposit269 (deposit-held-item 17 230)270 (deposit-held-item-named :parlyz-heal 55)271 (deposit-held-item 14 178)272 (deposit-held-item-named :water-stone 29)273 (deposit-held-item 14 32)274 (deposit-held-item-named :TM18 1)275 (deposit-held-item 13 1)276 (deposit-held-item 13 191)277 (deposit-held-item-named :TM02 98)278 (deposit-held-item-named :TM09 1)279 close-menu)))280 #+end_src282 This script calculates the fastest sequence of key presses to deposit283 the requested items into a PC, assuming that the character starts out284 in front of a computer.286 I also wrote functions that could grovel through the game's memory and287 present the internal data structures in usable ways. For example, the288 function =print-inventory= returns the current inventory in a human289 readable format.291 #+begin_src clojure :results output :exports both292 (com.aurellem.gb.items/print-inventory)293 #+end_src295 #+results:296 #+begin_example297 +-------------------+----------+298 |##| Item | Quantity |299 +--+----------------+----------+300 |0 | poke-ball | 14 |301 |1 | TM28 | 1 |302 |2 | TM11 | 1 |303 |3 | TM45 | 1 |304 |4 | nugget | 1 |305 |5 | s.s.ticket | 1 |306 |6 | helix-fossil | 1 |307 |7 | moon-stone | 1 |308 +--+----------------+----------+310 #+end_example313 Armed with these functions, I constructed a bootstrapping program that314 could be expressed as items. This is particularly hard, since many315 useful opcodes do not correspond any item, and the item quantities316 must all be less than 99.318 Here is the first bootstrapping program in all its glory.320 #+begin_src clojure321 (defn pc-item-writer-program322 []323 (let [;;limit 75324 limit 201 ;; (item-hack 201 is the smallest I could make this.)325 [target-high target-low] (disect-bytes-2 pokemon-list-start)]326 (flatten327 [[0x00 ;; (item-hack) no-op (can't buy repel (1E) at celadon)328 0x1E ;; load limit into E329 limit330 0x3F ;; (item-hack) set carry flag no-op332 ;; load 2 into C.333 0x0E ;; C == 1 means input-first nybble334 0x04 ;; C == 0 means input-second nybble336 0x21 ;; load target into HL337 target-low338 target-high339 0x37 ;; (item-hack) set carry flag no-op341 0x00 ;; (item-hack) no-op342 0x37 ;; (item-hack) set carry flag no-op344 0x00 ;; (item-hack) no-op345 0xF3 ;; disable interrupts346 ;; Input Section348 0x3E ;; load 0x20 into A, to measure buttons349 0x10351 0x00 ;; (item-hack) no-op352 0xE0 ;; load A into [FF00]353 0x00355 0xF0 ;; load 0xFF00 into A to get356 0x00 ;; button presses358 0xE6359 0x0F ;; select bottom four bits of A360 0x37 ;; (item-hack) set carry flag no-op362 0x00 ;; (item-hack) no-op363 0xB8 ;; see if input is different (CP A B)365 0x00 ;; (item-hack) (INC SP)366 0x28 ;; repeat above steps if input is not different367 ;; (jump relative backwards if B != A)368 0xED ;; (literal -19) (item-hack) -19 == egg bomb (TM37)370 0x47 ;; load A into B372 0x0D ;; dec C373 0x37 ;; (item-hack) set-carry flag374 ;; branch based on C:375 0x20 ;; JR NZ376 23 ;; skip "input second nybble" and "jump to target" below378 ;; input second nybble380 0x0C ;; inc C381 0x0C ;; inc C383 0x00 ;; (item-hack) no-op384 0xE6 ;; select bottom bits385 0x0F386 0x37 ;; (item-hack) set-carry flag no-op388 0x00 ;; (item-hack) no-op389 0xB2 ;; (OR A D) -> A391 0x22 ;; (do (A -> (HL)) (INC HL))393 0x1D ;; (DEC E)395 0x00 ;; (item-hack)396 0x20 ;; jump back to input section if not done397 0xDA ;; literal -36 == TM 18 (counter)398 0x01 ;; (item-hack) set BC to literal (no-op)400 ;; jump to target401 0x00 ;; (item-hack) these two bytes can be anything.402 0x01404 0x00 ;; (item-hack) no-op405 0xBF ;; (CP A A) ensures Z407 0xCA ;; (item-hack) jump if Z408 target-low409 target-high410 0x01 ;; (item-hack) will never be reached.412 ;; input first nybble413 0x00414 0xCB415 0x37 ;; swap nybbles on A417 0x57 ;; A -> D419 0x37 ;; (item-hack) set carry flag no-op420 0x18 ;; relative jump backwards421 0xCD ;; literal -51 == TM05; go back to input section422 0x01 ;; (item-hack) will never reach this instruction424 ]425 (repeat 8 [0x00 0x01]);; these can be anything427 [;; jump to actual program428 0x00429 0x37 ;; (item-hack) set carry flag no-op431 0x2E ;; 0x3A -> L432 0x3A435 0x00 ;; (item-hack) no-op436 0x26 ;; 0xD5 -> L437 0xD5438 0x01 ;; (item-hack) set-carry BC440 0x00 ;; (item-hack) these can be anything441 0x01443 0x00444 0xE9 ;; jump to (HL)445 ]])))447 #+end_src449 I use the glitch items 0x00 and 0xFF to great effect in my run. 0x00450 sells for almost half of maximum money --- I use just 3 of them to451 finance the purchase of all the other items I need. 0x00 is also a452 NO-OP in the gameboy's machine language, which means that I can stick453 them anywhere where I need to break up an otherwise illegal pair of454 opcodes. 0xFF is also extremely useful because it is the end-of-list455 sentinel. Normally, the game will "compact" your items whenever you456 make a purchase or deposit. For example, if you deposit a pokeball,457 then deposit another pokeball, the item list looks like:459 #+begin_example460 pokeball x2461 #+end_example463 instead of:465 #+begin_example466 pokeball x1467 pokeball x1468 #+end_example470 However, the compaction stops after the first 0xFF item, so if there471 is an 0xFF item at the beginning of the list, it will "shield" all the472 items below it from compaction. It the beginning of the run, I stick473 an 0xFF item at the top of the PC item list, allowing me to put items474 in with impunity. At the end, I toss the 0xFF away to reveal the475 completed bootstrap program.477 The final payload program is multiple programs. I created a reduced478 form of MIDI and implemented it in gameboy machine language. Then I479 translated a midi file from http://www.everyponysings.com/ into this480 reduced MIDI language. The payload program contains both the music481 data and the MIDI interpreter to play that data. The picture works in482 a similar way. There is code to translate a png file into a form that483 can be displayed on a gameboy, and other code to actually display that484 image. Both the image and the display code are also written by the485 final bootstrapping program. Even though my final payload is rather486 simple, you can write any program at all as the payload. The source487 for the sound and image displaying code is at488 http://hg.bortreb.com/vba-clojure.