ポケモン ダイヤモンド/パール 任意コード実行やってみた

BDSP発売直前ぐらいに発表され世間を賑わせたダイパの任意コード実行。
手順や埋め込みプログラムの公開などをされているデデロニー氏のブログにほぼ必要なことは書かれているが、手法の改良が続けられており、現時点(2023年1月)で1から追うのは中々難しかったので、備忘録を兼ねてやったことをまとめておきます。


概要

まず、DPでの任意コード実行のために特定の性格値を持つポケモンが必要になるため、エメラルドでの任意コード実行環境を整える。
その後、必要なポケモンを第4世代に送り、任意コード実行の環境を構築する。
DPでの任意コード実行環境は、最新版だとNPC ASEを利用したものが便利で、これには以下の2つを使用する。

  • ①簡易メモリエディタプログラム
  • ②簡易メモリエディタプログラム起動用にイベントIDを書き換えたNPC

任意コードや任意スクリプト(ボックスを開く等ゲーム内で使われている処理を直接叩けるもの)を実行するためにメモリを書き換えることができるのが①のプログラムで、このプログラムを起動状態にすることが任意コード実行の第一歩(実際には二歩目ぐらいだが)である。
しかしこのプログラムが起動した状態というのはレポートに保存されず、ゲームを起動する度にこのプログラムを起動状態にする手順を踏まなければならない。
これを簡略化したのが②で、特定のNPCのイベントIDを書き換えた状態をレポートに保存しておくことで、ゲーム起動直後の状態からごくわずかな手順で①のプログラムを起動状態にすることができる。

エメラルド任意コード実行

0x085Fの入手

ぼんじり氏が発見した、バグポケモン0x085Fを使った手法の記事が公開されており、詳細な手順もデデロニー氏の記事で解説されている。
注意点としては、ぼんじり氏の記事では0x085Fを孵化する際はポケモンセンター内で行うことと書かれているのでそうした方がよさそうというぐらいで、あとは手順通りにやるだけ。

この時点でボックス1~のボックス名に記述した任意コードが実行できるので、例えばたんじょうのしまに行ってみたりすることも可能。(日本語版エメラルドにはオーロラチケットが配布されていないため本来は不可能)

DP任意コード実行に必要な個体の生成

0x085Fを用意しただけではボックス名にプログラムコードを直接記述する必要があるが、これを任意スクリプト実行環境にすることで更に使いやすい状態になる。
これがデデロニー氏の汎用コード環境で、以下のページに導入手順がまとめられている。
Em任意コード実行によるイベントスクリプトの実行【汎用コード】 - 哀しき獣の村
汎用コード環境を導入することで、ボックス1~のボックス名に記述したゲーム内スクリプトを実行できるようになる。この上でDP任意コード実行の解説ページで紹介されているスクリプトを使い、4匹のポケモンを入手する。

NNは自由なので、とりあえず上から「ASE1」~「ASE4」とつけておいたが、性格値の上4桁とかの方が普通にわかりやすかった気がする。

ダイヤモンド/パール任意スクリプト実行

準備

エメラルドから上記の4匹を連れて来て、この記事の「セットアップ」の項目通りに殿堂入りを記録、ボックス配置・アイテム欄などを整える。
ちなみに28回目までの殿堂入りは、初期版ROMならリョウの部屋で下の扉になみのりを使い、

下1(なみのり解除)
右9
上14
左9(上側の扉の真上)
上63
(部屋切り替えSE)
上1
セーブリセット
上1

がコトブキバグをやらなくていいため楽。
29回目~30回目は手持ちポケモンに制限があるためなみのりを使えず、真面目にコトブキバグを実行してポケッチカンパニーから殿堂入り部屋に侵入する必要がある。
chara.ge

任意スクリプト実行環境に入る

個人的に自転車の操作精度が要求されるコトブキバグはあまりやりたくなかったので、以下のNewASEのチャートを使用した。
www.craft.do
(もう少し調べたところ、RETIRE氏のpastebinには四天王部屋から入る歩数も書いてあったので、そちらを使えば一切コトブキバグをやらなくて済みそうだった。)
いずれにせよ、

  • ①殿堂入りデータをメモリに展開する(セーブでは記録されない。つうしんサーチャーのエラーでは維持される)
  • ②パルパークモードに入る(リタイアコマンド使用可能)
  • ③マップID:333に行く

が満たせればよい。

成功すると、リタイアコマンドにより電卓の計算結果をスクリプトデータとして実行できる状態になる。
スクリプトデータの詳細仕様は以下のページにまとめられている。
DPP Script Commands - Generation 4 - Project Pokemon Forums
試しに指定したポケモンのタマゴを入手するスクリプトを実行してみると、以下のようにマナフィのタマゴなどを入手できる。

簡易メモリエディタの導入

任意スクリプト実行可能状態になったということで、概要①の簡易メモリエディタプログラムを導入する。以下の記事の【簡易メモリエディタの導入】の通りにやればOK。
DP任意コード実行のメモリ書き換え高速化【簡易メモリエディタ】(改) - 哀しき獣の村
(マーキングACEの記事を飛ばしているが、この導入記事で出てくる「マーキングACEにより性格値0xFFFD20F4のポケモンにプログラムを保存する。」とは、0xFFFD20F4のポケモンをボックス整理画面で選択してマーキングを開き、何も変更せずに即決定を押すこと。)

この手順により、簡易メモリエディタのプログラムは性格値0xFFFD20F4のフシギダネの内部データ(Cブロック)に書き込まれ、セーブにより保存される。
そして【簡易メモリエディタの使い方】にあるように、Cブロック呼び出しスクリプト(正確に言うと「マーキングで決定を押した時に、そのポケモンのCブロックの内容をプログラムとして実行する状態」にゲームプログラムを書き換えるスクリプト)を実行した後、フシギダネに対しマーキングを実行すれば起動できる。

パルパークモードの終了とエスカレーターの修復

NewASEのページの「・「リタイア」コマンドの消し方」の手順を行い(便利ボタン有効化は無視して歩きでOK)、パルパークモードを終了する。
これによりレポートが書けるようになるが、簡易メモリエディタプログラムの機能でリタイアコマンドは残ったままになっている。
次に壊れたエスカレーターを直すために、ヨスガシティのコンテスト会場左の受付に話しかけてキャンセル。(公式のお知らせに掲載されていた方法)
これでポケモンセンターエスカレーターも普通に使えるようになるので、マサゴタウンポケモンセンター2階に移動する。

NPC ASEの導入

前述の手順を行い、デデロニー氏の簡易メモリエディタ起動済みかつパルパークモード終了済みマサゴタウンポケモンセンター2階のPCの前にいることを前提とする。

必要ポケモンの生成とプログラム書き込み

追加でNPC ASE用補助ポケモンエディタ専用セットアップポケモンを用意する必要がある。

※補足
イベントIDを0x973Bに書き換えたNPCに話しかけると、直前にボックス整理画面で掴んだポケモンの先頭ブロック末尾の値をオフセットとするアドレスにジャンプしその先のスクリプトを実行するため、補助ポケモンの先頭ブロック末尾をジャンプ用オフセット→ジャンプ先をエディタ専用セットアップポケモンのCブロックとすることで、NPCに話しかけることをトリガーとして簡易メモリエディタの起動に必要なセットアップを行うことができる。
(エディタ専用セットアップポケモンのCブロックには、前述のCブロック呼び出しスクリプトとボックス整理画面を開くスクリプトを書き込む。)

情報が分散しているがどちらのポケモンの準備も流れは一緒で、

  • ボックスで簡易メモリエディタを書き込んだポケモン(手順通りなら性格値0xFFFD20F4のフシギダネ)の表IDを確認
  • 今いるマップIDを333にする(アドレス36115532+表IDに値333をセット)
  • スクリプトコードをセット
    • アドレスを36301552+表IDにセット
    • 値を0x00010097、0x00020000と連続セット(前者を0x01EA0097にすればマナフィのタマゴになる)

の書き換えを行った後に、

  • 乱数値を0にする(アドレス0x021C65A8に値0をセット)
  • リタイアで性格値0xE97E0000のタマゴを入手
  • タマゴをボックスに預ける
  • 暗号化解除ACEをセット(アドレス0x0206DAB0に値0x0000E7C7をセット)
  • マーキングで●だけ選択して決定
  • CブロックアドレスセットACEをセット(アドレス0x0206DAB0に値0x60066F20、アドレス0x027E3B80に値0x02293020をセット)
  • マーキングで何もせず決定(ここでメモリエディタのアドレスに目標のCブロックのアドレスがセットされるため、以後値の書き込みのみ)
  • 値の連続セットでプログラムを書き込み

を2匹分行う。
NPC ASE用補助ポケモンのプログラムはこのページに、エディタ専用セットアップポケモンのプログラムはこのページに記載されている。

エディタ専用セットアップポケモンはボックス5の21番目に、NPC ASE用補助ポケモンはボックスの触りやすい位置に置いておく。
また、マップIDを333に書き換えているので、1歩動いて本来のマップIDに戻しておく。

NPCイベントIDの書き換え

  • Cブロック呼び出しACEをセット(アドレス0x0206DAB0に値0x000046B7をセット)
  • NPC ASE用補助ポケモンに対しマーキングで何もせず決定

これでNPCの見た目が変わっていれば書き換え成功。この状態のNPCに話しかけるとイベントID=0x973Bが実行される。
この状態はレポートで保存されるので、レポートを書いてリセットしても問題ないが、エリアを切り替えると元のイベントIDに戻ってしまうので注意。

NPCイベントからの簡易メモリエディタの起動

NPCのイベントIDを書き換えた状態のセーブであれば、ゲーム起動直後であっても

  • ボックス整理画面でNPC ASE用補助ポケモンを掴んで閉じる(他のポケモンには触らない)
  • 書き換えたNPCに話しかける

とするとボックス整理画面が開くので、簡易メモリエディタプログラムを書き込んだポケモンのマーキングを開いて決定する。これで簡易メモリエディタ起動中状態にすぐに復帰できる。

簡易メモリエディタ起動状態にさえしてしまえば、上の手順で再びNPCのイベントIDを書き換えられるため、エリアを切り替えてどこに行っても問題ない。
大事なのは「簡易メモリエディタ起動状態」か「NPCのイベントID書き換え状態」のどちらかを常に維持しておくこと。つまりゲームを起動して簡易メモリエディタを起動したら、終了する時は必ずマサゴタウンポケモンセンター2階に戻ってきてNPCのイベントIDを書き換えてからレポートして終了する。

おまけ資料

なぞのばしょ移動中に何が起きているのか

なぞのばしょの仕組みについては2017年にアルセウスを捕まえる方法を発表したCryo氏の解説(Glitch City Laboratories Archives - Obtaining Arceus via the Void Glitch - Page 1)および翻訳(ポケモンDPの技術的解説(翻訳) - reiju's diary)に詳しく書かれている。

主人公がいま居るエリアを32x32の領域に区切った時、どの領域がどこマップIDかを定義したテーブルがMap Matrix Layoutである。
Map Matrix Layoutは現在主人公のいるマップIDを含む最大30x30のテーブルとなっていて、ポケッチカンパニーのような1画面に収まる室内などでは1x1しかなく、街や道路などの屋外では30x30をフルに使っている。
ここで1x1しかMap Matrix Layoutのデータが無いポケッチカンパニーなどの室内で壁抜けを行い、本来移動できないマップの境界を越えると、Map Matrix Layoutの実際のデータではないメモリ上のデータにアクセスすることができる。
そしてそのようなデータは多くの場合0などの無効なマップIDを示しており、これが「なぞのばしょ」の正体である。

また、Map Matrix Layoutはセーブした時に正しいデータとなるように再展開される。例えばマップID 0x008D を含むMap Matrix Layoutにいる時にテーブル外の領域にアクセスして 0x00B0 の位置に行き、そこでセーブ&リセットするとマップID 0x00B0 を含むMap Matrix Layoutにワープすることができる。
これらの移動とワープを適切に繰り返していくことで、目的のマップIDに到達することができる。
アルセウスなら510、任意コード実行なら332/333など。)

マーキングプログラム

マーキングプログラムはアドレス0x0206DAB0に格納されていて、ここにDSのCPUアーキテクチャであるARMv5TEのThumb命令の機械語を書き込めば、マーキング決定時に実行される。
例えばデフォルトのプログラムは

0x7820 LDRB R0, [R4, #0]
0x73B8 STRB R0, [R7, #E]

となっていて、これはR4レジスタのアドレスに格納されているデータ(=マーキングUI上のボタンの状態)をR0レジスタ経由でR7レジスタのアドレス+0xEの位置に書き込むということを意味している。
これを例えば以下のように変更すると、R4レジスタのアドレス+0x70の位置(0x027E3B80)にある数値をR7レジスタのアドレス+0x1Cの位置に書き込むプログラムとなり、これは予め0x027E3B80にセットした値をシンオウリボンのデータ領域に書き込むプログラムとなる。

0x7738 LDR R0, [R4, #70]
0x6F20 STRB R0, [R7, #1C]


NPCイベントID

【懐かしの】任意コード実行総合スレ3【バグ】:ポケモンBBS(掲示板)
によると、イベントID 0x283D 以上のイベントは、動的メモリの始点 + 0x29698 + (イベントID - 0x283C) * 4 のアドレスに対し、アドレス自体 + アドレスに格納されている値 + 4 の位置からスクリプトを実行する。
今回の例だと、イベントID 0x973B に対し、注目するアドレスは 始点 + 0x29698 + (0x973B - 0x283C) * 4 = 始点 + 0x45294
これが「最後に掴んだポケモンの先頭ブロック末尾」のアドレスとなっていて、ここに 0xFFFCBAE8 = -0x34518 を書き込んでおくと、 始点 + 0x45294 - 0x34518 + 4 = 始点 + 0x10D80 となり、これはまさにボックス5、21番目のポケモンの先頭ブロックのアドレスである。