Mercurial > vba-clojure
view org/total-control.org @ 617:aeb4b676ba8b
license was messed up by wget; corrected.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 26 Feb 2013 14:12:24 +0000 |
parents | b531d490859c |
children | a79e5a852347 90575d3a64d1 |
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-clojure14 Youtube Video w/ Visual Keypresses: http://www.youtube.com/watch?v=p5T81yHkHtI16 Special Thanks to:18 - http://tasvideos.org/2913S.html for the save corruption hack which19 is used at the start of this run.20 - http://www.everyponysings.com/ for providing the midi file I used21 to create the song at the end.22 - http://www.zophar.net/ for the terminal font.25 * Introduction27 Think of pokemon yellow as creating a little universe with certain28 rules. Inside that universe, you can buy items, defeat rival trainers,29 and raise your pokemon. But within that universe, you are bound by the30 rules of pokemon. You can't build new buildings, or change the music,31 or change your clothes.. There are some games (like chess), where it32 is not possible to alter the rules of the game from within the33 game. No matter what moves you make in chess, you can never change the34 rules of the game so that it becomes checkers or basketball. The point35 of this run is to show that you CAN change the rules in pokemon36 yellow. There is a certain sequence of valid actions (like walking37 from one place to another or buying items) that will allow you to38 transform pokemon yellow into Pacman, or Tetris, or Pong, or a MIDI39 player, or anything else you can imagine.41 * Background43 [[http://tasvideos.org/2913S.html][This speedrun]] by Felipe Lopes de Freitas (p4wn3r), beats pokemon44 yellow in only 1 minute and 36 seconds. It does it by corrupting the45 in-game item list so that he can advance the list past its normal46 limit of 20 items. The memory immediately after the item list includes47 the warp points for the current map, and by treating that data as48 items and switching and dropping them, he can make the door from his49 house take him directly to the end of the game.51 When I first saw that speedrun, I was amazed at how fast pokemon52 yellow could be beaten, and that it was possible to manipulate the53 game from the inside, using only the item list. I wondered how far I54 could extend the techniques found in p4wn3r's run.56 The gameboy is an 8 bit computer. That means that ultimately, anything57 that happens in pokemon is a result of the gameboy's CPU reading a58 stream of 8 bit numbers and doing whatever those numbers mean. For59 example, in the gameboy, the numbers:61 [62 16 37 224 47 240 37 230 15 55]63 mean to check which buttons are currently pressed and copy that result64 into the "A" register. With enough numbers, you can spell out an65 interactive program that reads input from the buttons and allows you66 to write any program you want to the gameboy. Once you have assembled67 such a program and forced the game to run it, you have won, since you68 can use that program to write any other program (like Tetris or69 Pacman) over pokemon yellow's code. I call a program that allows you70 to write any other program a "bootstrapping program". So, the goal is71 to somehow get a bootstrapping program into pokemon yellow and then72 force yellow to run that program instead of its own.74 How can we spell out such a program? Everything in the game is75 ultimately numbers, including all items, pokemon, levels, etc. In76 particular, the item list looks like:78 #+begin_example79 item-one-id (0-255)80 item-one-quantity (0-255)81 item-two-id (0-255)82 item-two-quantity (0-255)83 .84 .85 .86 #+end_example88 Let's consider the button measuring program [37 62 16 37 224 37 24089 37 230 15 55] from before. Interpreted as items and item quantities, it is91 #+begin_example92 lemonade x1693 guard spec. x22494 leaf stone x24095 guard spec. x23096 parlyz heal x5597 #+end_example99 So, if we can get the right items in the right quantities, we can100 spell out a bootstrapping program. Likewise, when writing the101 bootstrapping program, we must be careful to only use numbers that are102 also valid items and quantities. This is hard because there aren't103 many different items to work with, and many machine instructions104 actually take 2 or even 3 numbers in a row, which severely restricts105 the types of items you can use. I ended up needing about 92 numbers to106 implement a bootstrap program. Half of those numbers were elaborate107 ways of doing nothing and were just there so that the entire program108 was also a valid item list.110 The final part of the hack is getting pokemon yellow to execute the111 new program after it has been assembled with items. Fortunately,112 pokemon keeps a number called a function pointer within easy reach of113 the corrupted item list. This function pointer is the starting point114 (address) of a program which the game runs every so often to check for115 poison and do general maintenance. By shifting an item over this116 function pointer, I can rewrite that address to point to the117 bootstrapping program, and make the game execute it. Without this118 function pointer, it would not be possible to take over the game.120 * The Run122 I start off and name my rival Lp/k. These characters will eventually be123 treated as items and shifted over the function pointer, causing it to124 execute the bootstrapping program that will soon be constructed. I125 start the run the same as p4wn3r's and restart the game while saving,126 so that the pokemon list is corrupted. By switching the 8th and 10th127 pokemon, I corrupt the item list and can now scroll down past the 20th128 item. I shift items around to increase the text speed to maximum and129 rewrite the warp point of my house to Celadon Dept. Store. (p4wn3r130 used this to go directly to the hall of fame and win the game in his131 run.) I deposit many 0x00 glitch items into the PC from my corrupted132 inventory for later use. Then, I withdraw the potion from the133 PC. This repairs my item list by overflowing the item counter from134 0xFF back to 0x00, though the potion is obliterated in the process. I135 then take 255 glitch items with ID 0x00 from the computer into my136 personal items.138 Leaving my house takes me directly to Celadon Dept. store, where I139 sell two 0x00 items for 414925 each, giving myself essentially max140 money. I hit every floor of the department store, gathering the141 following items:143 #+begin_example144 +-------------------+----------+145 |##| Item | Quantity |146 +--+----------------+----------+147 |1 | TM02 | 98 |148 |2 | TM37 | 71 |149 |3 | TM05 | 1 |150 |4 | TM09 | 1 |151 |5 | burn-heal | 12 |152 |6 | ice-heal | 55 |153 |7 | parlyz-heal | 99 |154 |8 | parlyz-heal | 55 |155 |9 | TM18 | 1 |156 |10| fire-stone | 23 |157 |11| water-stone | 29 |158 |12| x-accuracy | 58 |159 |13| guard-spec | 99 |160 |14| guard-spec | 24 |161 |15| lemonade | 16 |162 |16| TM13 | 1 |163 +--+----------------+----------+164 #+end_example166 After gathering these items, I deposit them in the appropriate order167 into the item PC to spell out my bootstrapping program. Writing a full168 bootstrap program in one go using only items turned out to be too169 hard, so I split the process up into three parts. The program that I170 actually construct using items is very limited. It reads only from the171 A, B, start, and select buttons, and writes 4 bits each frame starting172 at a fixed point in memory. After it writes 200 or so bytes, it jumps173 directly to what it just wrote. In my run, I use this program to write174 another bootstrapping program that can write any number of bytes to175 any location in memory, and then jump to any location in memory. This176 new program also can write 8 bits per frame by using all the177 buttons. Using this new bootstrap program, I write a final178 bootstrapping program that does everything the previous bootstrapping179 program does except it also displays the bytes it is writing to memory180 on the screen.182 After completing this bootstrapping program, I go to the Celadon183 mansion, because I find the metaness of that building to be184 sufficiently high to serve as an exit point for the pokemon185 universe. I corrupt my item list again by switching corrupted pokemon,186 scroll down to my rival's name and discard until it is equal to the187 address of my bootstrapping program, and then swap it with the188 function pointer. Once the menu is closed, the bootstrapping program189 takes over, and I write the payload....191 * Infrastructure193 The entire video was completely produced by bots --- I didn't manually194 play the game at all to produce this speedrun. Here is a brief account195 of the infrastructure I build to make the video. The entire source of196 the project is available at http://hg.bortreb.com/vba-clojure198 The first step was to build a programmatic interface to pokemon199 yellow. So, I downloaded vba-rerecording from200 http://code.google.com/p/vba-rerecording/. After repairing their201 broken auto-tools scripts so that it would compile on GNU/Linux, I202 added a low level C interface that I could call from Java via203 JNI. This C interface gives me basic control over the emulator: I can204 step the emulator either one clock cycle or one frame, and I can get205 the contents of any memory location or register. The interface also206 allows me to freeze the state of the emulator, save it to a Java207 object, and reload that state again at any time.209 I built a layer of [[http://clojure.org/][clojure]] code on top of the JNI bindings to get an210 entirely functional interface to vba-rerecording. This interface211 treats state of the emulator as an immutable object, and allows me to212 do everything I could do with the lower level C interface in a213 functional manner. Using this functional code, I wrote search programs214 that take a particular game-state and try out different combinations215 of button presses to get any desired effect. By combining different216 styles of search with different initial conditions, I created high217 level functions that could each accomplish a certain general task,218 like walking and buying items. For example, here is some actual code:220 #+begin_src clojure221 (defn-memo viridian-store->oaks-lab222 ([] (viridian-store->oaks-lab223 (get-oaks-parcel)))224 ([script]225 (->> script226 (walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓227 ← ← ← ← ← ← ← ← ←228 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓229 ← ←230 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓231 ↓ ↓ ↓ ↓ ↓ ↓ ↓232 → → → → → → → →233 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓234 ← ← ← ← ←235 ↓ ↓ ↓ ↓236 ])237 (walk-thru-grass238 [↓ ↓ ↓ ↓ ↓ ↓ ↓])239 (walk [↓ ↓ ← ↓ ↓ ↓ ←240 ↓ ↓ ↓ ↓ ↓ ↓241 → → → ↑])243 (do-nothing 1))))244 #+end_src246 This script walks from the Viridian City pokemon store to Oak's247 Lab in the most efficient way possible. The walk-thru-grass function248 guarantees that no wild battles will happen by manipulating the game's249 random number generator.251 #+begin_src clojure252 (defn-memo hacking-10253 ([] (hacking-10 (hacking-9)))254 ([script]255 (->> script256 begin-deposit257 (deposit-held-item 17 230)258 (deposit-held-item-named :parlyz-heal 55)259 (deposit-held-item 14 178)260 (deposit-held-item-named :water-stone 29)261 (deposit-held-item 14 32)262 (deposit-held-item-named :TM18 1)263 (deposit-held-item 13 1)264 (deposit-held-item 13 191)265 (deposit-held-item-named :TM02 98)266 (deposit-held-item-named :TM09 1)267 close-menu)))268 #+end_src270 This script calculates the fastest sequence of key presses to deposit271 the requested items into a PC, assuming that the character starts out272 in front of a computer.274 I also wrote functions that could grovel through the game's memory and275 present the internal data structures in usable ways. For example, the276 function =print-inventory= returns the current inventory in a human277 readable format.279 #+begin_src clojure :results output :exports both280 (com.aurellem.gb.items/print-inventory)281 #+end_src283 #+results:284 #+begin_example285 +-------------------+----------+286 |##| Item | Quantity |287 +--+----------------+----------+288 |0 | poke-ball | 14 |289 |1 | TM28 | 1 |290 |2 | TM11 | 1 |291 |3 | TM45 | 1 |292 |4 | nugget | 1 |293 |5 | s.s.ticket | 1 |294 |6 | helix-fossil | 1 |295 |7 | moon-stone | 1 |296 +--+----------------+----------+298 #+end_example301 Armed with these functions, I constructed a bootstrapping program that302 could be expressed as items. This is particularly hard, since many303 useful opcodes do not correspond any item, and the item quantities304 must all be less than 99.306 Here is the first bootstrapping program in all its glory.308 #+begin_src clojure309 (defn pc-item-writer-program310 []311 (let [;;limit 75312 limit 201 ;; (item-hack 201 is the smallest I could make this.)313 [target-high target-low] (disect-bytes-2 pokemon-list-start)]314 (flatten315 [[0x00 ;; (item-hack) no-op (can't buy repel (1E) at celadon)316 0x1E ;; load limit into E317 limit318 0x3F ;; (item-hack) set carry flag no-op320 ;; load 2 into C.321 0x0E ;; C == 1 means input-first nybble322 0x04 ;; C == 0 means input-second nybble324 0x21 ;; load target into HL325 target-low326 target-high327 0x37 ;; (item-hack) set carry flag no-op329 0x00 ;; (item-hack) no-op330 0x37 ;; (item-hack) set carry flag no-op332 0x00 ;; (item-hack) no-op333 0xF3 ;; disable interrupts334 ;; Input Section336 0x3E ;; load 0x20 into A, to measure buttons337 0x10339 0x00 ;; (item-hack) no-op340 0xE0 ;; load A into [FF00]341 0x00343 0xF0 ;; load 0xFF00 into A to get344 0x00 ;; button presses346 0xE6347 0x0F ;; select bottom four bits of A348 0x37 ;; (item-hack) set carry flag no-op350 0x00 ;; (item-hack) no-op351 0xB8 ;; see if input is different (CP A B)353 0x00 ;; (item-hack) (INC SP)354 0x28 ;; repeat above steps if input is not different355 ;; (jump relative backwards if B != A)356 0xED ;; (literal -19) (item-hack) -19 == egg bomb (TM37)358 0x47 ;; load A into B360 0x0D ;; dec C361 0x37 ;; (item-hack) set-carry flag362 ;; branch based on C:363 0x20 ;; JR NZ364 23 ;; skip "input second nybble" and "jump to target" below366 ;; input second nybble368 0x0C ;; inc C369 0x0C ;; inc C371 0x00 ;; (item-hack) no-op372 0xE6 ;; select bottom bits373 0x0F374 0x37 ;; (item-hack) set-carry flag no-op376 0x00 ;; (item-hack) no-op377 0xB2 ;; (OR A D) -> A379 0x22 ;; (do (A -> (HL)) (INC HL))381 0x1D ;; (DEC E)383 0x00 ;; (item-hack)384 0x20 ;; jump back to input section if not done385 0xDA ;; literal -36 == TM 18 (counter)386 0x01 ;; (item-hack) set BC to literal (no-op)388 ;; jump to target389 0x00 ;; (item-hack) these two bytes can be anything.390 0x01392 0x00 ;; (item-hack) no-op393 0xBF ;; (CP A A) ensures Z395 0xCA ;; (item-hack) jump if Z396 target-low397 target-high398 0x01 ;; (item-hack) will never be reached.400 ;; input first nybble401 0x00402 0xCB403 0x37 ;; swap nybbles on A405 0x57 ;; A -> D407 0x37 ;; (item-hack) set carry flag no-op408 0x18 ;; relative jump backwards409 0xCD ;; literal -51 == TM05; go back to input section410 0x01 ;; (item-hack) will never reach this instruction412 ]413 (repeat 8 [0x00 0x01]);; these can be anything415 [;; jump to actual program416 0x00417 0x37 ;; (item-hack) set carry flag no-op419 0x2E ;; 0x3A -> L420 0x3A423 0x00 ;; (item-hack) no-op424 0x26 ;; 0xD5 -> L425 0xD5426 0x01 ;; (item-hack) set-carry BC428 0x00 ;; (item-hack) these can be anything429 0x01431 0x00432 0xE9 ;; jump to (HL)433 ]])))435 #+end_src437 I use the glitch items 0x00 and 0xFF to great effect in my run. 0x00438 sells for almost half of maximum money --- I use just 3 of them to439 finance the purchase of all the other items I need. 0x00 is also a440 NO-OP in the gameboy's machine language, which means that I can stick441 them anywhere where I need to break up an other wise illegal pair of442 opcodes. 0xFF is also extremely useful because it is the end-of-list443 sentinel. Normally, the game will "compact" your items whenever you444 make a purchase or deposit. For example, if you deposit a pokeball,445 then deposit another pokeball, the item list looks like:447 #+begin_example448 pokeball x2449 #+end_example451 instead of:453 #+begin_example454 pokeball x1455 pokeball x1456 #+end_example458 However, the compaction stops after the first 0xFF item, so if there459 is an 0xFF item at the beginning of the list, it will "shield" all the460 items below it from compaction. It the beginning of the run, I stick461 an 0xFF item at the top of the PC item list, allowing me to put items462 in with impunity. At the end, I toss the 0xFF away to reveal the463 completed bootstrap program.465 The final payload program is multiple programs. I created a reduced466 form of MIDI and implemented it in gameboy machine language. Then I467 translated a midi file from http://www.everyponysings.com/ into this468 reduced MIDI language. The payload program contains both the music469 data and the MIDI interpreter to play that data. The picture works in470 a similar way. There is code to translate a png file into a form that471 can be displayed on a gameboy, and other code to actually display that472 image. Both the image and the display code are also written by the473 final bootstrapping program. Even though my final payload is rather474 simple, you can write any program at all as the payload. The source475 for the sound and image displaying code is at476 http://hg.bortreb.com/vba-clojure.