「なかよしバッヂ」で任意コード実行ができるわけ

初代ポケモン赤緑 (後期版)のバグ解説記事です。 表題の通り「なかよしバッヂ」で任意コード実行ができる理由を解説します。

メモリバンク3のアドレス0x563d、つまり03:563dというアドレスを今後UseItem_と呼びます。

UseItem_にはアイテムを使うときの処理のルーチンが記述されています。

その中身は次の図の通りです。

つまり、UseItem_の先頭には「ジャンプベクトル」を使ってアイテムIDに応じたアドレスにジャンプする処理が格納されています。 その処理に続けてジャンプベクトルが格納されています。 その0バイト〜1バイト目はアイテム1を使ったときのジャンプ先、2バイト〜3バイト目はアイテム2を使ったときのジャンプ先、…ということですね。 さらにそこに続けて、ジャンプベクトルでジャンプ先として指定されている、個々のアイテムの処理が書かれています。

ところが、ジャンプベクトルは83個のジャンプ先 (166バイト)しかありません。 ポケモン赤緑はここで、ベクトルの境界チェックをしません。 つまり内部IDが84 (アイテムの内部IDは1から始まることに注意)以上なアイテムを使用すると変なところからアドレスを拾ってきて、そこにジャンプするわけですね。 なお、ひでんマシンとわざマシンは内部ID196 (=0xc4)以上ですが、これらについては最初の処理に分岐があり、ここで紹介したジャンプベクトルは使わないようになっています。

変なアドレスと言いましたが、具体的には84番目のアイテムでは「ボールを使うときの処理」 の先頭(UseItem_+0xc0)に書かれたバイナリを読み取り、それをアドレスとして読み取ってそこへジャンプするわけです。85番目のアイテムなら「ボールを使うときの処理」の2バイト目、86番目なら「ボールを使うときの処理」の4バイト目、…というわけです。

さて、なかよしバッヂは内部ID 103 (=0x67)ですから、「ボールを使うときの処理」 の38 (= (103 - 84) * 2)バイト目を見るわけですね。 そこには

83 d9

が書かれています。 したがって、なかよしバッヂを使うと0xd983へジャンプするわけです。 ここはサファリボールの残り個数が格納されているアドレスであり、その続きには育て屋にあずけているポケモンにニックネームが格納されているので、これを適当にいじることで任意コード実行ができます。

なお、サファリボールの残り個数のアドレスがボールの処理の中に書かれているのは何も不思議ではありません。 ボールの処理で「サファリボールの残り個数を1減らす」という処理をしないといけないからですね。

この記事を見て興味を持った方はぜひ、bgbというエミュレータでアドレス「03:563d」にブレークポイントを設定して遊んでみてください。