この章は、Exploitに成り得る脆弱性なのか単なるクラッシュなのかを切り分けるためのチュートリアルです。PSPでのゲームの脆弱性探しのチュートリアルでもあります。
セーブデータを読み込んだPSPをクラッシュさせるのはPSPのExploitへの第一歩であることは間違いありませんが、クラッシュ=Exploitではありません。クラッシュだけですべてを公開することはソニーに対策をお願いし、より強固なファームウェアへの成長を促していることになります。また、最近はExploitとして使っているゲーム名を公開しないことが一般的になっています。何らかの結果(HENやHBLなど)へ繋がる前に公開することも強固なファームウェアへの成長を促しているに過ぎないため現在ではしてはいけないこととして扱われています。かつてはHello World表示に成功した時点で公開していた時期もありますが、Hello Worldはバイナリーローダーで”Hello World”という文字を表示する自作コード起動に成功しただけですので実用性には欠けると言わざるを得ません。例えばH.BINの形でコンパイルした自作ソフトであれば起動しますが、EBOOT.PBPとして利用されているPSPの自作ソフトの起動はできません。
最近公開されているPSPのセーブデータExploitはほとんどがMaTiAz氏がGripshiftで実践した、「This is spartaaaaaa方式」を利用しています。これはセーブデータの名前の部分に続けて'a'のような文字を続けて入れたセーブデータを作成しクラッシュを引き起こすことで脆弱性を探す方式です。プレーヤーが最初のゲーム起動時に入力する名前の部分を脆弱性を探す際に利用するのはPSPが確実に読み込むデータでありセーブデータ内のどこに格納されているのかが分かりやすいという理由からです。理論的には名前の部分でなくとも確実に読み込むと分かっているデータエリアであれば構わないのですが、チュートリアルとしてはやはり簡単な名前の部分を使う前提でお話しします。
私自身様々なゲームでExploitを公開しましたが、私はハッカーと呼ばれる人種ではありませんし、ましてやプログラマーでもありません。そのため表現として不適切な部分があるとは思いますが御了承下さい。
この記事を読むにあたり、基本的なプログラミングについての概念、特に変数やアドレス、配列といった知識は知っておいた方が良いと思います。コマンドラインについても扱い方を知っておいた方が良いでしょう。アセンブラの知識はあるに越したことがありませんが絶対に必要なものでもありませんが、アセンブラの知識はあった方がより理解が深まるでしょう。
PSPLinkでゲームを起動するPSPLinkはデバッグに必要不可欠なツールです。まずはPSPLinkをインストールしてあるCFWのPSPが必要です。
私が改変したPSPLinkの使用を強く勧めます。場合によってはvalentine-hblの移植で必要になります。
Releases · 173210/psplinkusb · GitHubPSPLinkのインストール方法などはネットで検索すれば出てきますので割愛します。PSPLinkは数種類のprxファイルで構成されています。PSPではPSPLinkをプラグインの形で利用します。sepluginsフォルダにusbhostfs.prxとpsplink.prxを入れて、そのプラグインをゲーム起動中有効にするためにgame.txtでpsplink.prxを記述しておいてリカバリーメニューで有効にしておいてください。psplink.prxを有効にしておけばusbhostfs.prxはPSPLinkが自動で有効状態にしてくれますのでusbhostfs.prxをわざわざ有効にする必要はありません。基本的にはPSPを遠隔操作するRemoteJoyがキー入力を送信するところでコマンドを送信するのがPSPLinkだと思ってください。チュートリアルはどこにでもあるであろうRemoteJoyを使えるようセットアップできていればPSPLinkは使えるはずです。
すべてインストールが完了し準備ができたら、PCでusbhostfs_pc.exeを起動してゲームを起動したPSPとPCをUSBケーブルで繋ぎpspsh.exeを起動してください。うまく動作すれば、usbhostfs_pcの画面には”Connected to device”と表示され、pspshには"host0:/>”と出てきます (画像はまもすけ氏による)。
セーブデータExploitの基礎セーブデータExploitのほとんどは「スタックバッファオーバーフロー」という脆弱性を利用しています。詳しい仕組みを知りたい方はググってください。
さて、セーブデータでスタックバッファオーバーフローを引き起こすための最も一般的な方法は非常に長い文字列をセーブデータ内のどこかに仕込む方法です。文字列は文字の集まりなので仕込むのは簡単です。更に文字列は一般的には復号化されたセーブデータファイルであれば、そのファイル内のどこにあるのかを探すのは非常に簡単です。ゲーム中に入手したゴールドメダルの数が格納されている場所を探すのは簡単なことではありませんが、文字列であればPSPLinkにある検索機能を使えば非常に簡単です。ただし、名前の場合文字コードを考えなければならない日本語(2バイト文字)にするよりはアルファベットを用いた1バイト文字のほうが検索が簡単になります(「文字列」よりも"STRING"の方が簡単です)。スタックバッファオーバーフローを引き起こす目的でプレイヤー名をゲームで入力しセーブデータを作成する場合は、ひらがなしか受け付けないなどの特殊な場合を除いてプレーヤー名をアルファベットで入力しておくことをお勧めします。また、PSPでプレイヤー名を入力する際何文字目かでそれ以上入力できないようになっている場合がほとんどですが、できる限り限界まで文字を入れておいた方が検索の精度が上がります。
名前を入力したら、一旦セーブしてゲームを終了し、もう一度起動させてセーブデータを再び読み込ませてください。では、ここでスレッドがスタックに文字列を読み込むか試してみましょう。
次のコマンドを入力してユーザーモードのスレッドを見つけてください。
- コード: 全て選択
uidlist Thread
スレッドの大きなリストが出力されます。attrが0xFFのスレッドを探してください。それがユーザーモードのスレッドです。
- コード: 全て選択
[Thread] UID 0xCCCCCCCC (attr 0x0 entry 0xEEEEEEEE)
(Name): user_main, (UID): 0xDDDDDDDD, (entry): 0x0FFFFFFF (attr): 0xFF # <- This thread is usermode!
(Name): NAME, (UID): 0xBBBBBBBB, (entry): 0x88000000 (attr): 0x0 # <- This Thread is not usermode. :/
# 以下略
次に、このコマンドを入力してスタックのアドレスとサイズを調べます。(0xDDDDDDDDはスレッドのUIDで置き換えてください。)
- コード: 全て選択
thinfo 0xDDDDDDDD
こんなふうにスレッドの情報が表示されます。
- コード: 全て選択
UID: 0xDDDDDDDD - Name: user_main
Attr: 0xBBBBBBBB - Status: 0/STATUS- Entry: 0xEEEEEEEE
Stack: 0x0AAAAAAA - StackSize 0x00CCCCCC - GP: 0x0FFFFFFF
InitPri: 32 - CurrPri: 32 - WaitType 0
WaitId: 0x00000000 - WakeupCount: 0 - ExitStatus: 0x00000000
RunClocks: 0 - IntrPrempt: 0 - ThreadPrempt: 0
ReleaseCount: 0, StackFree: 0
この例ではアドレスは0x0AAAAAAAでサイズは0x00CCCCCCと表示されています。
準備はこれで終わりです。脆弱性探しを始めましょう!
まず、どうにかして文字列をスタックに読み込ませなければいけません。必要となった時にコピーするはずなので、その文字列を表示する画面を開いたら読み込まれるかもしれません。
文字列がコピーされたと思ったら、pspshにコマンドを入力してスタックの中の文字列を探しましょう。(0x0AAAAAAAをアドレス、0x00CCCCCCをサイズ、STRINGを入力した文字列にそれぞれ置き換えてください。
- コード: 全て選択
findstr 0x0AAAAAAA 0x00CCCCCC STRING
すると、一致した文字列のアドレスが次のように出力されます。(0x0EEEEEEEがアドレス)
- コード: 全て選択
Found match at address 0x0EEEEEEE
やりました!99%の確率でそのゲームはExploitできます。
TIP: 文字列を見つけられない時は…スタックに文字列を見つけられないかもしれません。文字列が他の関数で上書きされているかもしれません。でも心配御無用、解決策があります。
文字列をコピーする際にスレッドをクラッシュさせて、上書きされる前に止めればいいのです。
次の指示に従ってください。
まず、セーブデータを復号化しなければなりません。
MagicSaveはセーブデータを復号化する非常に優れたツールです。ググって使い方を調べましょう。すぐに復号化できます。
次に、バイナリエディタでセーブデータを開いてください。バイナリエディタの使い方もググればいっぱい見つかります。
セーブデータを開いたら、バイナリエディタの検索機能でセーブデータ内に保存されている入力した文字列を探し、こんな感じに長い文字列で上書きしてください (画像はwololo氏による)。
編集がおわったら、
MagicSaveで暗号化するか、復号化したデータを読ませてください。
何かしらの動作を行うとクラッシュするかもしれません。全くクラッシュしなければ、文字列をより長くしてください。もしセーブデータが破損していると言われたら諦めてください。
クラッシュしたら、
findstrコマンドをもう一度試してみてください。