PSPでexploitを探してHello World、そしてHalf-Byte Loader移植までのチュートリアル

公式ファームウェアのPSPでHomebrewを実行するために必要なことは、
1. PSPでユーザーモードexploitを探す
2. ユーザーモードexploitでバイナリーローダーを作成する
3. exploitとバイナリーローダーを利用してHBL(Half Byte Loader)へ移植する

の3つです。

この分野ではHBLプロジェクトメンバーであるwololo氏が古くからexploitに関する情報をブログで紹介してくれており、私も何度か翻訳して日本語で紹介してきました。

その結果日本版ゲームで複数のexploitが見つかっていますが、今回はそれらのチュートリアルを再編集しまとめてみました。
もしexploitを発見したら、是非GameGazフォーラムに投稿して、日本でのPSPシーン盛り上げに一役買ってくださいね。

exploit

目次
第1章: PSPのセーブデータexploitを探す
第2章: バイナリーローダーの作成
第3章: 新たなexploitにHBLを移植する

第1章は、実は過去に翻訳していません。wololo氏が記事を書いたのは1年半以上前でしたのでかなり内容をアレンジしています。翻訳とは言い難いです。
第2章は大人のためのゲーム講座の記事を再編集したものです。
第3章はwololo氏が最近投稿した記事ですので初翻訳となります。

    第1章: PSPのセーブデータexploitを探す


【参考:wololo.net: Finding gamesaves exploits on the PSP

この記事は、exploitに成り得る脆弱性なのか単なるクラッシュなのかを切り分けるためのチュートリアル記事です。PSPでのセーブデータexploitの見つけ方のチュートリアルでもあります。

セーブデータを読み込んだPSPをクラッシュさせるのはPSPのexploitへの第一歩であることは間違いありませんが、クラッシュ=exploitではありません。クラッシュだけですべてを公開することはソニーに対策をお願いし、より強固なファームウェアへの成長を促していることになります。また、最近はexploitとして使っているゲーム名を公開しないことが一般的になっています。何らかの結果(HENやHBLなど)へ繋がる前に公開することも強固なファームウェアへの成長を促しているに過ぎないため現在ではしてはいけないこととして扱われています。かつてはHello World表示に成功した時点で公開していた時期もありますが、Hello Worldはバイナリーローダーで”Hello World”という文字を表示する自作コード起動に成功しただけですので実用性には欠けると言わざるを得ません。例えばh.binの形でコンパイルしたHomebrewであれば起動しますが、EBOOT.PBPとして利用されているPSPのHomebrewの起動はできません。

最近公開されているPSPのセーブデータexploitはほとんどがMaTiAz氏がGripshiftで実践した、”This is spartaaaaaa方式”を利用しています。これはセーブデータの名前の部分に続けて”a”のような文字を続けて入れたセーブデータを作成しクラッシュを引き起こすことで脆弱性を探す方式です。プレーヤーが最初のゲーム起動時に入力する名前の部分を脆弱性を探す際に利用するのはPSPが確実に読み込むデータでありセーブデータ内のどこに格納されているのかが分かりやすいという理由からです。理論的には名前の部分でなくとも確実に読み込むと分かっているデータエリアであれば構わないのですが、チュートリアルとしてはやはり簡単な名前の部分を使う前提でお話しします。

私自身『みんなのゴルフ PORTABLE2』でHello Worldを公開しましたが、私はハッカーと呼ばれる人種ではありませんし、ましてやプログラマーでもありません。そのため表現として不適切な部分があるとは思いますが御了承下さい。

この記事を読むにあたり、基本的なプログラミングについての概念、特に変数やアドレス、配列といった知識は知っておいた方が良いと思います。コマンドラインについても扱い方を知っておいた方が良いでしょう。アセンブラの知識はあるに越したことがありませんが絶対に必要なものでもありませんが、アセンブラの知識はあった方がより理解が深まるでしょう。

セーブデータexploitの基礎
セーブデータexploitのほとんどは”バッファーオーバーフロー”という脆弱性を利用しています。バッファーオーバーフローとは、プログラムが確保したメモリサイズを越えて文字列が入力されると領域があふれて(オーバーフロー)しまい、予期しない動作が起きることを言います。バッファオーバーフローを利用した(ソニーの見地からは不正な)アクセスに成功すると、バッファーオーバーフローを引き起こしたプログラムが持っているアクセス権の範囲で任意の動作を行なうことが可能となります。例えば10個分の配列を用意してあるところに11個のデータを格納しようとすると1個分あふれてしまいます。その結果どうなるかと言うと、メモリー内の予期せぬところのどこかにそのあふれた1個分のデータが格納されることになり、詳しくは後で述べますが面白い結果を生むことがあるのです。バッファーオーバーフローについてはこちらの記事が分かりやすいでしょう。

さて、バッファーオーバーフローを引き起こすための最も一般的な方法は(セーブデータ内の話として進めますが、必ずしもセーブデータでないとできないということはありません)非常に長い文字列をセーブデータ内のどこかに仕込む方法です。文字列は文字の集まりなので仕込むのは簡単です。更にプレーヤー名は一般的には復号化されたセーブデータファイルであれば、そのファイル内のどこにあるのかを探すのは非常に簡単です。ゲーム中に入手したゴールドメダルの数が格納されている場所を探すのは簡単なことではありませんが、名前であればHEXエディタにある検索機能を使えば非常に簡単です。ただし、名前の場合文字コードを考えなければならない日本語(2バイト文字)にするよりはアルファベットを用いた1バイト文字のほうが検索が簡単になります(「まもすけ」よりも「mamosuke」の方が簡単です)。バッファーオーバーフローを引き起こす目的でプレイヤー名をゲームで入力しセーブデータを作成する場合は、ひらがなしか受け付けないなどの特殊な場合を除いてプレーヤー名をアルファベットで入力しておくことをお勧めします。また、PSPでプレイヤー名を入力する際何文字目かでそれ以上入力できないようになっている場合がほとんどですが、できる限り限界まで文字を入れておいた方が検索の精度が上がります。

名前の文字列がセーブデータのどこにあるのかを見つけたら、次のステップはそこに非常に長い文字列を入れてみることです。『Gripshift』の場合は復号化されたセーブデータ内で見つけた名前の文字列の最初の文字から「This is spartaaaaaaa・・・」と上書きして入力しています。『みんなのGOLF PORTABLE2』の場合は「mamosuke」という文字列を見つけたら、それに続けて「aaaaa・・・」と「a」を連続して入力しました。上書き、追記どちらでも構いませんが、文字列はHEXエディタで単純に挿入するとその分セーブデータ全体のファイルサイズが大きくなってしまいますので(破損データになる恐れがあります)1バイト分「a」を入れる場合は元のセーブデータに必ず上書きするようにしてください。

sparta

ここでは『Gripshift』に倣って「aaa・・・」と入力すると説明しましたが、必ずしも「a」である必要はありません。「A」でも「b」でも「c」でも「Z」でも構いません。HEXエディタには16進数のコードのみで表記される部分と、それをアルファベット文字で表示する部分があるのが一般的です。文字表示部分に「a」と入れれば16進数で表示される部分に「61」と表示されます。同様に「A」は「61」、「b」は「42」、「c」は「63」、「Z」は「5A」です。これはコンピュータ上で文字を利用するために各文字に割り当てられるバイト表現(文字コード)により決まっているためで、文字コードは複数種ありますが簡単なアルファベットであればほとんどが上記のように表示されます。自分が使いやすいと思う文字を1つ決めて、それを名前の部分に連続文字列として入力します。この際、連続して同じ文字にするのを忘れないでください。後に説明しますが、PSPLinkでレジスタにその文字のコードが入っていることの確認がより簡単になります。

セーブデータのプレイヤー名に文字を入れると言っても、実際ゲーム中の入力画面で行えるわけではありません。PSPでは暗号化されたセーブデータがメモリースティックのms0:¥PSP¥SAVEDATA¥UCJSXXXX(『みんなのGOLF PORTABLE2』の場合はUCJS10075)にありますが、暗号化されているため直接バイナリエディタで開いてもプレイヤー名は見つかりません。セーブデータを編集する時には一旦復号化をして、編集した後にもう一度暗号化をしてやる必要があります。exploitを探す用途の場合にはsavegame-deemerという復号化/暗号化を自動でしてくれるプラグインが非常に便利です。このプラグインを有効にしておけばあとは何も意識することなくセーブデータの暗号化/復合化が可能になります。暗号化したセーブデータはSAVEDATAフォルダに、復号化したセーブデータはSAVEPLAINに自動保存してくれます。

復号化したセーブデータに、先ほど述べた「aaa・・・」などを入力してゲームをプレイしてセーブデータを読み込んだ後クラッシュするかどうかをみてください。savegame-deemerを有効にしておけば復号化したSAVEPLAINフォルダ内の該当ゲームフォルダにあるセーブデータ(ファイル名はSDDATA.BIN)を読み込んでくれますので途中で暗号化する必要はありません。一度やってみると分かりますが、殆どの場合ゲームはクラッシュしないか、セーブデータが壊れていると表示されてしまうかのどちらかです。ただし、「aaa・・・」と「a」を何文字(何バイト)入れるかにより挙動は異なります。『みんなのGOLF PORTABLE2』の場合はなんと、プレーヤー名の頭のアドレスから数えて1200バイトくらい入れてはじめてクラッシュしました(名前が入っているアドレスから1108バイト目が$raに格納されるというとんでもないものです。名前格納アドレスとは全く関係ないのかもしれません。)。Patapon2のように数十バイトでクラッシュするものもあれば『みんなのGOLF PORTABLE2』のようにPatapon2とは2桁異なるものまで様々です。

ここで大切なのは”どこで諦めるのか”の判断です。しつこく1バイトづつ「a」を入れていくといつかクラッシュするかもしれませんし、その前にデータ破損してしまうかもしれません。あるのかないのか分からない脆弱性に対してしつこく調査を続けて行くのは経験上かなり無駄な労力を消費します。『みんなのGOLF PORTABLE2』の場合、数百バイト程度までデータを入れた段階で諦めてしまう可能性が非常に高いです。J416氏が『みんなのGOLF PORTABLE』で既にexploitが存在することを証明してくれていたことと、シリーズ物はファームウェアアップデートでまとめて対策されることから「2にもあるに違いない」と考えたからこそ『みんなのGOLF PORTABLE2』でexploitを発見できたのであり、普通なら誰もが諦めてしまうことでしょう。データを改造してゲームを起動して動作確認、といったサイクルをひたすら繰り返しますので簡単なことではありません。ですから”これ以上はめんどくさい”と思ったところで一旦線を引き、そのゲームにはexploitが存在しなかったことにして他のゲームで別のクラッシュを探した方が懸命です。

もし、改造したセーブデータでPSPがクラッシュするのを発見したら、そのゲームではプログラマーがきちんとクラッシュしないようプログラムを組まなかったということになります。そうです。この時点では単なるバグの可能性があるためクラッシュ=バッファーオーバーフローを利用したexploitではないのです。これがexploitなのかどうかは解析してやる必要があり、その解析に使うのがPSPLinkというアプリケーションです。

PSPLinkでゲームを起動する
ここからは少し難しい操作が必要になります。まずはPSPLinkをインストールしてあるCFWのPSPが必要です。PSPLinkのインストール方法などはネットで検索すれば出てきますので割愛します。PSPLinkは数種類のprxファイルで構成されています。PSPではPSPLinkをプラグインの形で利用します。sepluginsフォルダにusbhostfs.prxとpsplink.prxを入れて、そのプラグインをゲーム起動中有効にするためにgame.txtでpsplink.prxを記述しておいてリカバリーメニューで有効にしておいてください。psplink.prxを有効にしておけばusbhostfs.prxはPSPLinkが自動で有効状態にしてくれますのでusbhostfs.prxをわざわざ有効にする必要はありません。基本的にはPSPの画面をPCの画面上に表示するRemoteJoyからリモートジョイスティック機能を無しにしたものがPSPLinkだと思ってください。チュートリアルはどこにでもあるであろうRemoteJoyを使えるようセットアップできていればPSPLinkは使えるはずです。
すべてインストールが完了し準備ができたら、PCでusbhostfs_pc.exeを起動してゲームを起動したPSPとPCをUSBケーブルで繋ぎpspsh.exeを起動してください。

using_psplink

うまく動作すれば、usbhostfs_pcの画面には”connected to device”と表示され、pspshには”all modules loaded”と表示された後”/host0:>”と出てきます。

psplink

セーブデータを読み込んだ後、クラッシュするまでの動作を行ってください。クラッシュはセーブデータ読み込み直後に起こる場合もあれば、何らかの操作を行った後クラッシュする場合まで様々です。クラッシュするとpspshの画面に以下のような画面が表示されます。

crash

何を探せば良いのか?
ここからはアセンブラの話になりますので少し難しいかもしれません。PSPLinkはクラッシュ直前の各種レジスタの状態を表示してくれます。レジスタはプログラムで使われている32ビットの整数型で、文字やメモリアドレスなどプログラムによっていろんな用途で使われています。
exploitを探したい場合には、メモリー内の任意の場所にジャンプすることができるコードが書けるかどうかを調べます。ただし、それができる方法はそんなに多くありません。今知る限りでその唯一の方法は”$ra”というレジスタの中身を任意の値に書き換える方法です。”$ra”というレジスタは”return address”、つまり”戻りアドレス”の意味ですので、これが何故重要なのかは想像がつくでしょう。あるサブルーチンから抜け出して元に戻る時に”j $ra”($raにジャンプする)というコマンドとして使われます。$ra自体は通常$spというレジスタからデータを受け取ります。そのため$spに関係するデータが扱えるならば流れとしては良い方向と言えます。
他のレジスタではダメなのかと言うと、そうでもありません。場合によっては$spや$raに値を受け渡しすることもあります。その方法の場合は非常に複雑であるためここでは説明しません。

exploitとなったクラッシュと、単なるクラッシュを比較する

2つのクラッシュの結果を比較してみます。

exploitだった『Gripshift』のクラッシュの場合

gripshift_crash

プレイヤー名に「a」を大量に入れました。PSPLinkのクラッシュレポートでは$raが 0x61616161 となっています。61は「a」という文字を16進数で表した数字です。これがビンゴ!0x61616161はセーブデータ内に入れた、通常連続した「aaaa」の部分のどこかなので、それさえ突き止めればメモリー内の任意のアドレスへジャンプさせる方法が見つかったことになります。

単なるクラッシュだった『ファンタシースター ポータブル』のクラッシュの場合

phantasy_crash

“61”と言う数字があちらこちらにありますが、肝心な$spや$raにはありません。$a1、$a2、$a3に”61″が出てくる時点でexploitに成り得る可能性はゼロとまで言い切れませんが、スタートとしてはその後の手間を考えると悪い部類だと言えます。$a1、$a2、$a3が何らかの良い影響を与えているかもしれませんのでここで一度PSPLinkで見てみましょう。PSPLinkのEPCという部分に表示されているあたりで何が起こったのかをディスアセンブルしてみます。下の図の場合にはEPCのアドレスより少し前の0x089520B0から40行分表示するためpspshで”disasm 0x089520B0 40″と入力しています。

phantasy_disasm

$spにはなんの影響もなさそうなのが分かりますし、非常に複雑な動きをしているのも分かります。これでは駄目そうです。一般的にはこのような状態であったらさっさと諦めて他のゲームで別のexploitを探すのが賢明です。

$raにストアされるアドレスを探す
PSPLinkで$raに表示された「aaaa」の4バイトがどこにあるのかを探し、”61″の代わりに任意の値を入れてやることでPSPのメモリ内の任意のアドレスへジャンプさせることができるようになります。この方法には王道がなく、地道に探して行くしかありません。

方法は非常に単純です。
セーブデータに「aaaa・・・」と入力した部分は16進数で
「61 61 61 61」
と成っているはずですので、そこにユニークな数字となる値を順に入れて行き、PSPLinkの結果を見ればそのアドレスが特定できます。
「01 02 03 04・・・」と入れて言った場合にPSPLinkで$raが 0x04030201のように表示されたらそれがビンゴです。
1000バイト以上「aaaa・・・」を入れた場合には、例えば1000バイト以下の場合にはクラッシュすらしないことが多いようです。ということは、クラッシュするようになった1000バイト目以降の部分で同じことを行うだけでアドレスの特定ができます。それでもかなりの量がある場合には、行(16バイトあります)ごとに
「A0 A0 A0 A0・・・」
「B0 B0 B0 B0・・・」
のように16バイトづつ同じ値を入れておき、まずどの行が$raに該当するのかを調べれば後は簡単です。

[ad#between-the-post]


hello_world_630_by_mamosuke

    第2章: バイナリーローダーの作成


【情報源:wololo.net: Writing a binary Loader

この記事について
PSPでHomebrewを実行するための最初のステップはユーザーモードexploitを発見することです。これはゲームでのバッファーオーバーフロー画像を操作するといったテクニックを利用してPSPのRAMをコントロールすることになります。
こうした脆弱性を見つけて、値が可変するレジスタ$raをフルコントロールします。次のステップはその実証となります。バイナリーローダーを作るにあたり基本的な考え方は、記述したコードからメモリースティックに置いたファイルにある別のコードを読み込み実行させるということになります。
バイナリーローダーの作成は方法さえわかってしまえばそんなに複雑ではありませんが、どこかに情報が集まっている訳ではないためできるかどうかは知識の有無に依存してしまいます。
ということでこちらです。

警告: これから書く方法は最良の方法ではありませんが、誰でも簡単にPSPで”Hello World”を表示(自作コードの実行が可能なことを証明)することが可能になります。今回サンプルに使ったsparta_sdkはもちろんこのチュートリアルに沿って作ったわけではありませんし、本来であればアセンブラなどの基礎も覚えて上で取り組むべきことであるのは事実です。しかし原則を主張していても実利は生まれません。試してみたらできた、という楽しみ方の提案だと思って読んでください。

このガイドでは皆さんが既に$raの値を自由に変えることならばできるという前提で書きます。ゲームのセーブデータを例にあげてみましょう。XMBでの脆弱性を見つけた場合だと少しやり方が変わりますが、主要な所は同じです。PSPLinkの使い方も知っているものとします。HEXエディタの使い方やCやMIPSを扱うに当たっての言語の記述の仕方、基本的なMakefileの大まかな知識、そしてHomebrewを作りたいという気持ちも既にお持ちだとの前提です

目次
セーブデータexploitを使ってバイナリーローダーを作成するためのステップは以下です:

1.ジャンプする場所を探す
2.インポートする関数を探す
3.バイナリーローダーをコンパイルする
4.バイナリーローダーをセーブデータに書き込む
5.ゲームに適用するSDK(開発キット)を作成する
6.動作を実証するためのバイナリーファイルを作成する(Hello Worldなど)
7.セーブデータを暗号化し直す

ジャンプする場所を探す
PSPLinkを起動させ、いつものやっているようにゲームをクラッシュさせます。そのまま次のステップを実行していきます。

wololo1

$raのコントロールができたら、バイナリーローダーのコードを入れたいアドレスにジャンプさせるようにします。原則としてコードはセーブデータの中に書きますので、セーブデータがRAMのどこからどこまでに入っているかは知っておく必要があります。当然セーブデータとRAMを見て比較しなければなりません。クラッシュした後にRAMをダンプするにはPSPLinkでsavemem 0x08800000 20000000 memdump.binと入力してください。これによりPCにmemdump.binという約20MBのファイルが保存されます。

セーブデータの中の分かりやすい部分をまず探してください。同じ文字や数字が続いている部分が分かりやすいでしょう。その部分と同じ部分をダンプしたデータの中から探します(もちろんHEXエディタを使います)。下の画像ではダンプデータが左でセーブデータが右になります。

DUMP

ダンプデータでセーブデータと同じ部分を見つけることができればジャンプしたいアドレスを知ることができ、セーブデータのどこにコードを記述すれば良いのかが分かります。この例の場合見つけたパターンはセーブデータの場合0x43F0から始まっていて、それがダンプデータでは0x32C3E0になっています。実際にダンプを開始したアドレスは0x08800000からなのでRAM上での実アドレスは0x8800000 + 0x32C3E0 (=0x08B2C3E0)ということになります。PSPLinkがまだ開いていたらmemdump 0x08B2C3E0 20と入力してメモリダンプしてみれば先ほどのパターンが出てくるかを確認できます(アドレスの部分は自分で決めたアドレスにしてくださいね。もちろん。)
実際にはセーブデータ内に先ほど選んだコード記述エリアへジャンプ命令によりジャンプすることになりますので、その場所は正確に把握しておく必要があります。そのエリアでセーブデータとダンプデータに違いがあった場合、それはセーブデータブロックがそのエリアで完全に読み込まれなかったということになりコードを書くことがより困難になります(コードがRAM内に散乱するよりも1ヶ所にまとめた方が良いのです)。
ということで第一ステップとしては: ジャンプするに相応しい場所(0x08B2C3E0)を探してSDDATA.BIN(0x43F0から)にバイナリーローダーを入れ込むことになります。

インポートする関数を探す
ここからはかなり難しそうなところへ踏み込みます。かなり本格的な知識が必要なように感じますが、実際にはそれほどでもありません。その理由はつまり、良いツールがすでにあるからです。
Homebrewのコードを書くときには、PSPSDKの関数を呼び出します。ただしゲームを利用するexploitの場合にはRAM内のどこで関数を呼び出すのかはあらかじめ分かりません。そのためきちんと探し出してリダイレクトしてやる必要があります。
ではゲームの場合で考えてみましょう。ゲームからEBOOT.BINを抜き出して復号化するとします。EBOOT.BINはゲームのISOファイル内にあるのでまずそのISOファイルを用意しなければなりません。ゲームがUMDだった場合、カスタムファームウェアであればISOファイルに変換することは簡単です。どのカスタムファームウェアでもUSB Driveオプションがついていますので問題ありません。(ゲームがPSNで購入したものだった場合にはNPDecryptorでISOファイルに変換できます)。
まもすけ注:NPDecryptorは著作権を侵害しているという理由で現在ネット上からほとんど削除されてしまっています。

iso

ほとんどの場合、EBOOT.BINは暗号化されているのでprxdecrypterを使ってさらに復号してやる必要があります。

PrxDecrypter23a

一度EBOOT.BINを復号化してしまえば、prxtoolを使ってそこから情報を抜き出せます。コマンドは以下です:

prxtool -f EBOOT.BIN

これによってゲームに使われている個々の関数の実アドレスが分かります。Homebrewやバイナリーローダーで使える関数はこのようになっています。

prxtool

関数名に対応して16進数の値の後にライブラリの名称があることが分かるでしょう。ここから、その関連性が分かるだけでなくNIDSライブラリ関数名の関連付けが分かるxmlファイルも入手できます。こういったxmlファイルはsilversprings氏(Lan.st管理人)のサイト(http://silverspring.lan.st/)でも入手可能です。
xml nidsファイルが手に入ったら、次のようにコマンドを入力してください。

prxtool -f -n yourfile.xml EBOOT.BIN
※yourfile.xmlは任意の.xmlファイル名です。

更に分かりやすいものが出てきます。基本的にはこちらを使います。

prxtool2

この結果は必ず保存しておいてください!
もしprxtoolにファイルはprxではないと言われたら、どこかの復号化で失敗しているか、違うEBOOT.BINを使ったか(ISOファイル内にはダミーのEBOOT.BINが複数あり)の可能性が大です。

バイナリーローダーをコンパイルする
このステップを実行する前にMatiaz氏とFreeplay氏のsparta_sdkをダウンロードすることをお勧めします。Gripshiftのexploitを利用したsparta_sdkはバイナリーローダーの参考例として非常に有用です。
バイナリーローダーはアセンブラで書いてしまうとそれ自体は非常にシンプルなものになります。SDKを利用してCをベースに書くこともできますが、sparta_sdkのバイナリーローダーはMIPSアセンブラで書かれているようです。アセンブラはC/C++よりもハードルが高いのですが、今回はMatiazs氏の作ったものを今回発見したexploitのケースに当てはめてみたいと思います。ですから何ヶ所かを置き換えるだけ、ということになります。
sparta_sdkのloader.Sのファイルを開いてみてください。

loader

基本的な作業内容はsparta binLoaderで使われている関数アドレスを置き換えることとなります。Matiaz氏はGripshiftで使えるようなアドレスを入れていますので自分が見つけたゲームexploitとはそこが異なります。記述してある4つの関数(sceIoOpen、sceIORead、sceIOClose、sceKernelDcacheInvalidateRange)を、先ほど作成したリストに沿ってGripshiftでの値を自分のゲームでの値に置き換えてください。先ほど私が作ったリストで言うと、sceIoCloseとなっている0x08A698540x08C88590に置き換える、などという感じです。
よく起こりうるのが、
sceKernelDcacheInvalidateRange がリストにない場合です。その場合には
sceKernelDcacheWritebackInvalidateAll
が代わりに使えますので探してみてください。
更に、ファイルネームがストアされているアドレスも変えてやる必要があります。ファイルネームとは自分で作ろうとしているバイナリのファイル名のことで、よく使われるのがms0:/h.binです。これをセーブデータのジャンプ箇所のあたりに文字列として記述しておかなければなりません。もちろんそのアドレスをasmコードとして出しておいてください。下記の例で言うと、ms0:/h.binというファイルネームをジャンプポイントより0xF0バイト後ろに置いたことになるので、spartaの0xC0をF0に書き換えます。注意しておきたいのはコンパイルしたバイナリーローダーの長さはF0より小さくしなければならないことです。そうしないとファイルネームのところがバイナリーローダーのコードで上書きされてしまいますよ。

saveplain_inject

変えなければならないのはそのくらいです。ではこのファイルをコンパイルしてSDDATA.BIN(暗号化されていないセーブデータ)の中に書き込んであげましょう。
アセンブラのコンパイルはPSPSDKで提供されている各種ツールのなかでも特に難しいということはありません。ではsparta_sdkをヒントに、ここでコンパイルのためのコマンドを紹介します。

psp-as loader.s
psp-objcopy -O binary a.out a.bin

最初がコードをコンパイルするコマンドで、次がそれをバイナリー版にするコマンドです。

バイナリーローダーをセーブデータに書き込む
バイナリーローダーのコンパイルができればあとは簡単で、セーブデータに書き込むだけです。手でコピペしてもいいですし、お好きなスクリプト言語を使っても良いでしょう。注意してほしいのは先ほどこの記事でも書きましたが、$raにいれたアドレスから始まるよう正確に書き込むということです。

書き込みをするにあたり、SDDATA.BINファイルにexploitの読み込み+バイナリーローダーをコンパイルして書き込みを直接やってしまえるように私の場合はRubyでスクリプトを組みました。コードは自分で見つけたexploit用に大幅に書き換えなければならないので画像としてお見せしておきます。コードを書き込むための方法は自分がやり易いと思うやり方で実行していただいて構いません。

ruby

この時点でバイナリーローダーの代わりにhello worldをアセンブラで作って入れておけば良いことにお気づきでしょう。ただ、バイナリーローダーなどではなくもっと簡単なコード(例えばsceKernelExitGameを呼び出すなど)を試してみるのも良いですが、まずは自分の書いたコードを確実に動作させることに専念してください。

SDKの作成
一見難しそうに思えますが、実はそうでもありません。関数をセーブデータにインポートできていればお茶の子さいさいです。必要なのはprxtoolを使って入手したインポートする関数リストだけで、sparta_sdkのサンプルフォルダにあるsdk.hをアップデートするだけです。迷うようなことは全くありません。今回の例でいうと、0x08C885900x08A69854に置き換える、などです。先ほどの”.S”ファイルと同じです。リストにない場合には、なかったことが後で分かるように該当部分をコメントアウト( /**/で囲むことでコメント文扱いになりますのでプログラムとしては無視されます )してください。

sdk

実は、sparta_sdkのsdk.hファイルはベストな例とは言えません。それよりもMOHH exploitのsdk.Sを一度見てみた方が良いでしょう。これもアセンブラですが超シンプルで、関数がどうとかを探す必要すらありません。

sdk_asm

ここで、sdk.Sの関数の中での”functions imports”ファイルを分析するための簡単なRubyのスクリプトを作りました。ただ、prxtoolにもこの操作をする上で役立つオプションはあるようです。
MOHH exploitにあるsdk.hを探してみてください。”.S”と”.h”というファイルがそうです。

Hello worldの作成
SDKの作成ができれば、Hello Worldは本当に簡単です。実際sparta SDKにあるサンプルを書き換えや流用できたわけですからね。最初はできるだけ小さなファイルを作るところから始めて下さい。 簡単に実証するためのCのコードは“sceKernelExitGame()”を呼び出すことです。作成したSDKとバイナリーローダー(先ほど作成したもの)がきちんと動作することを確認する分にはそれで十分です。

hello

sparta_sdkのイベントをすべて自作sdk用に置き換えるだけだと考えてください。代わりにMOHH sdk.Sを使った場合はmakefileに若干変更を加えなければなりません(その場合はspartaのMakefileではなくMOHHのMakefileを見て考えてみてください)。
コンパイル後に出力するファイル名はh.binにします。バイナリーローダーがms0:/h.binを読み込むようにしてあるので、そのh.binをメモリースティックのルートに配置します。バイナリーローダーが入っているSDDATA.binはSAVEPLAINというフォルダ名のサブフォルダ内に配置します。以上の準備が完了したらゲームを起動してください(もちろんSaveGameDeemerとPSPLinkのプラグインは有効にしておきましょう)。exploitが発動して自作Hello Worldが表示されるはずです。

トラブルシューティング
exploitの動作がうまく行かない場合、いくつかの原因が考えられます。コンピューターのプログラムは書かれた流れ通りに実行されるだけなので、動作環境が不十分であってもバカ正直に間違った動作をしてしまいます。そんな時はPSPLinkの利用が成功の鍵となります。クラッシュしてしまったらRAMの状態を調べてみてください。正しいアドレスにきちんとジャンプしていますか?バイナリーローダーはそこにちゃんとありますか? すべて正しい場所にあって、おかしな理由で途切れたりしていませんか?実際に動作しているかを確認するためにバイナリーローダーにブレイクポイントを追加してみてください。バイナリーローダーの動作が確認できたら次はRAMに読み込まれるはずのHello Worldが設定したアドレスに読み込まれているかを確認してください。それができていれば今度はまた同様にHello Worldでブレイクポイントを設けてみてください。あとはいかに簡素化するかです。
操作をある程度自動化しないで一つ一つのステップを手動で行っているとかなり苦痛に感じるかもしれません。私は作業を自動化するようなスクリプトを作ることにまず時間を割いた方が良いと思います(バイナリーローダーのコンパイルやそれをセーブデータに書き込む作業など)。これなら自信があると言う言語でスクリプトを作りましょう

セーブデータを暗号化し直す
最後のステップは公式ファームウェアでexploitを動作させるようにすることです。SEDというツールでセーブデータexploitを暗号化できます。SEDでの暗号化にはgamekeyが必要です。gamekeyはSaveGameDeemerで作られるSAVEPLAINデータの中にあります。

ms0¥PSP¥SAVEPLAIN¥XXXX.bin(XXXXはゲーム自体のコード:パタポン2日本版ならUCJS10089.bin)
というデータの一番最後の部分がgamekeyです。

XXXX.binをバイナリエディタで開いてファイルの一番最後の部分を見ると、次のようになっています。
・ファイルの一番最後の20バイトは必ずゼロ
・その直前の16バイトがそのゲームのGamekey

final_block_of_bin

薄く色がついているところがGamekeyになります。

その16バイトだけを抜き出してgamekeyが作れます。

gamekey

SEDの場合は16バイトのXXXX.bin(:パタポン2日本版ならUCJS10089.bin)というファイル名にして
ms0¥PSP¥GAME¥SED¥gamekey
フォルダにコピーしておきます。

あとはSEDを起動すればセーブデータの暗号化/復号化は自由自在です。
SEDの使い方についてはググってください。ちょっといい加減な説明ですね。使い方さえ分かればSEDについては問題ないでしょう

[ad#footer-ad]


HBL_LOGO_631

    第3章: 新たなexploitにHBLを移植する


【情報源:wololo.net: Port HBL to your game exploit

セーブデータを利用したユーザーモードexploitを見つけて、次にバイナリーローダーを作成。で、次はどうしたらよいのでしょうか。ご存じだとは思いますが、PSPでHello Worldだけというのはもう誰も喜んでくれません。もちろんHello Worldが実現できることはは素晴らしいことですが、それだけでは何の成果も得られません。単にソニーに対してexploitの存在をアピールするだけで、せっかくの脆弱性が何らかの結果に結び付く前に早々とパッチされてしまうことは自明の理です。

さて、exploitを発見後の次のステップといえば、理想的にはHENやカスタムファームウェアとなります。もちろんそれを実現するには新たにカーネルexploitが必要ですが、それを発見することは相当困難です。ですのでユーザーの利益に成りうるより現実的な選択はHBLの移植となります。HBLはノーマルPSPで合法的なコンテンツを楽しむための扉を開くアプリケーションです。我々はそのためにHBLを開発したのですからセーブデータを使ったゲームexploitをHBLに移植することは簡単にできるようになっています。

このチュートリアルはこの記事を書いている現時点ではファームウェア6.31以下であれば対応しています。理論上HBLはこの先出るであろうファームウェアでも動作するはずですが、もちろん今後新たなセキュリティシステムが採用されてしまう可能性もありますのでその場合にはチュートリアルに沿って移植しても動作しないこともあり得ます。更にはゲームにもよりますが、syscall estimation(システム機能を呼び出すためのシステムコール命令の判定: Homebrewの互換性や実行速度に影響を与える)の内容が変わってしまう可能性もあります。

0. 実はカンタン
HBLはPatapon2以外のセーブデータexploitにも簡単に移植できるようになっています。特定の一ファイルを除き、ゲームごとに必要なファイルは後程詳しく書きますがほとんどがサブフォルダごとに分けて入れておくことになります。チュートリアルを実行するに当たっては、シェルを扱う基礎知識とPSPSDKでの作業に必要なスキル、セーブデータexploitを探す知識、バイナリーローダーまたはHello Worldのコーディングに必要な知識、Ruby言語の知識が必要です(一般的にはRuby以外のスクリプト言語を知っていれば簡単に把握できます。違いはそんなに多くありません)。

2. HBLのソースを入手しコンパイルする
移植への最初のステップはBLのソースを入手しコンパイルすることです。可能であればコンパイル後のHBLを既にリリース済みのexploitで正しく作業が出来ているかを確認するための動作確認を行ってください。

HBLのソースはこちらからダウンロードしてください(SVNクライアントが必要です)。

コンパイルするためにはPSPSDKが必要です(バイナリーローダーを作成したのであれば既にお持ちだと思います)。コンパイル自体は非常に簡単ですが、ある特定のexploitで起動するようHBLをコンパイルするのであればそのexploitごとにフォルダを作成しておく必要があります。例えば、make FOLDER=hotshotsとした場合には『Hot Shot Golf(US)』用のexploit用にコンパイルしているという形になります。この記事を書いている現時点では『みんなのゴルフ Portable』のJP/US/EUとPatapon2 USの合わせて4種類のフォルダが以下のフォルダ名で存在しています:minna、hotshots、everybody、patapon2

2. 新たに発見したexploit用のフォルダを作成する
もうお分かりだと思いますが、HBLを移植したいexploitのためのフォルダをまず作成することになります。wololoというゲームでexploitを発見したと想定してみましょう。その場合はeLoaderフォルダの中にwololoという名称のサブフォルダを作成することになります。我々の基本的なスタンスとしては別のexoploitが見つかればそのフォルダに入れるためのファイルは別途提供する方針ですし、我々がすでに提供しているexploitにも反映させたいと考えています。実際patapon2フォルダの構成を見てみましょう。

patapon2

このフォルダ内には6つのファイルと2つのフォルダがあります。これらに新たに発見したexploitで動作するよう修正を加えていくことになります。個々のファイルごとに説明したいと思います。

3. 新たに発見したexploit用のファイルを作成する
ツール類

フォルダは実際には絶対必要というものではないのですが、これから移植していくにあたりメモリーダンパーなどのツールは必要ですのでそういったツール類をフォルダに入れておきます。

linker_loader.x

これはh.binファイルとリンクさせるためのファイルになります。もしバイナリーローダーやHello Worldを作成済であればこのファイルは既に持っていることになります。それはlinker.xというファイル名になっていると思いますのでそのlinker.xをHello World
からコピーし
linker_loader.xへとファイル名を変更して終わりです。

sdk_loader.S

これはh.binのためのSDKです。もしバイナリーローダーやHello Worldを作成済であればこのファイルは既に持っているかもしれません。それはsdk.Sというファイル名になっていると思いますのでそのsdk.Sをコピーしsdk_loader.Sへとファイル名を変更してください。ない場合にはゲームから抜き出したEBOOT.BINを使ってprxtoolを実行して作成するか、moskitoolを使ってください。大抵の場合Hello Worldを作成済であればこのファイルは既にあるはずですのでこれ以上詳細は書きません。以上。

configフォルダとsdk_hbl.S

まず空のconfigフォルダを作成してください。後で必要になります。このフォルダの中身はeLoader¥tools¥imports.config generator¥eLoaderconf.rbのRubyスクリプトで自動生成されます。sdk_hbl.Sも同様に生成されます。スクリプトを適当なテキストエディタで以下のように変更してください。

eloaderconf

基本的には各々のexploitのハッシュは把握しているので、HBLで使えるstub(スタブ:他のプログラムを呼び出す際に仲介となるプログラム)がどこにあるのかは分かります。理論的にはstubの見つけ方はm0skit0s氏のフォーラムに書いてありましたが、残念ながら今はフォーラム自体が無くなってしまいました。今回は皆さんのためにRubyスクリプトをeLoader\tools\stubs.rbに用意してあります。使用するにはユーザーメモリーのダンプが必要です。CFWのPSPでPSPLinkを使って用意するか、今回移植しようとしている新exploitを利用してusermem dumperを使って用意するかしてください。用意できたらファイル名をmemdump.binにしてstubs.rbを実行します。メモリダンプはファームウェアごとに必要になりますので他のハッカーやベータテスターに協力を仰ぎ用意してください。(ちなみにファームウェア検知にはかなり多数のメモリダンプが必要になるので出来るならば可能な限り数を集め、ファイルが埋もれて管理できなくならないよう正しく分けて管理しておくことが大切です。)

どのライブラリからstubを得られるのかを知るためにPSPLinkでmodlistを実行します。最も重要なstubはゲームから得られるものになります。通常はmainと呼ばれますが名称が変わることはよくあり、例えばPatapon2の場合にはLaboと呼ばれていました。

そのため、基本的にはメモリーダンプごとにstubs.rbを実行するとファームウェアごとのアドレスが分かるようになっています。必要に応じてファームウェアごとにこの作業を繰り返します。結果に応じてそれをeLoaderconf.rbにコピぺしてください。

exploit_config.h

このファイルが最も変更する部分が多いです。この書き換えがほとんどすべてと言ってよいでしょう。exploitごとに変化するファイルの定義が含まれており、コンパイル時にHBLへ統合されます。

HBL_LOAD_ADDRESS これはRAMにHBLを読み込むためのアドレスのことです。ゲームで使用するRAM領域ではないのはどこなのかの値は把握しておきたいところですが、基本的にPSPに確保できる領域はおおよそ200kB程度です。特定のアドレス指定なしで200kBの領域を確保するためのh.binファイルを作成することによりこの値が分かります。そして以下のようにしてそのアドレスを入手するようにします。

u32 uid = sceKernelAllocPartitionMemory(2, “test”, PSP_SMEM_Low, size, NULL);
u32 addr = sceKernelGetBlockHeadAddr(uid);
//then log addr in a debug file or something, this is what you’ll use for HBL_LOAD_ADDRESS

TH_ADDR_LIST は動作させたくないスレッド(小さな単位で実行するプログラム処理)のリストです。スレッドはSceUIDによって定義されますが、値は常に変化してしまうため実際に知りたいのはどこで定義されているのかというアドレスになります。PSPLinkではゲーム(またはHello World)起動中にthlistと入力することによりこのスレッドリストが入手できます。次にRAM内の各々のスレッドのUIDを探します。そのアドレスはthidが定義されておりすべてユニークなはずですが、それこそがリストにいれておきたいものです。PSPLinkでuidlistと入力してもアドレスが分かると思いますが私自身試したことがありません。そこまでマメじゃなくて申し訳ない。

EV_ADDR_LIST は動作させたくないイベント(実行中のプログラムからの処理要求)のリストです。PSPLinkでevlistと入力すれば出てきます。TH_ADDR_LISTと構造が似ています。

SEMA_ADDR_LIST は動作させたくないセマフォ(並行して動作しているプロセス間で同期を取ったり割り込み処理の制御を行なう機構)のリストです。PSPLinkでsmlistと入力すれば出てきます。TH_ADDR_LISTと構造が似ています。

GAME_FREEMEM_ADDR これはゲームのメモリが割り当てられているRAM内のアドレスです。ほとんどのゲームにはあるはずですが、Patapon2のように無いゲームもあるのでその場合には値をコメントアウト(削除せずコメント文として残す)しておきます。この値を見つけるにはPSPLinkでuidlistと入力しSceSysMemMemoryBlockセクションあたりで探してください。0xFFがあるユーザーエリアに属するブロック(00のところではありません!)でスタック(最後に入力したデータが先に出力されるという特徴をもつデータ構造)ではないところを探します。みんゴルexploitの場合はこのブロックはそのまんまのblockと呼ばれているので見つけるのは簡単です。エントリーアドレスが大切なのであり、UIDではありませんよ。

UNLOAD_ADDITIONAL_MODULES これは可能ならばいろいろ変えて定義してみてください。HBLのメモリで問題が出た時にはコメントアウトしてください。

Firmware Detection: ファームウェア検出のための変数は現時点ではトライアンドエラーしかありません。いろんなユーザーからファームウェアごと、モデルごとにメモリダンプをお願いして集めて比較してみるしかありません。目標にするのはファームウェアごとにメモリダンプで同じ部分を見つけ出すことです。ファームウェアが異なればその部分が今度は異なるはずです。

linker_hbl.x

linker_loader.xをコピーしてlinker_hbl.xとしてください。アドレス値を先ほどexploit_config.hを作成した際に見つけたHBL_LOAD_ADDRESSの値と置き換えて終わりです。

4. コンパイル

  • eLoaderconf.rbを実行しSDKとconfig(設定)ファイルを生成してください。この操作により今移植しようとしているexploitを含め、eLoaderconf.rbで定義されている全exploitのconfigファイルが生成されます。(移植するexploit専用のサブフォルダの中にconfigフォルダを作っておくことを忘れないようにしてください)
  • make FOLDER=yourfolder を実行してください。
  • ※PSPというフォルダを作りたい場合には make PSP となります。

  • すべて完了したらh.binとhbl.binをメモリースティック(goの場合は内蔵メモリでも可)のルートに、configフォルダをexploitごとのサブフォルダに、libs_フォルダをルートにそれぞれ入れます。これであなたが発見したexploitで起動するHBLの中身は完成です。
  • 5. 最後に、大切なこと
    HBLはGPL(General Public License)のライセンスに基づき頒布されています。二次的著作物をコンパイルして頒布する場合ソースコードも頒布しなければなりません。我々にソースコードを要求されるようなことが無いよう注意して下さい。
    このチュートリアルは皆さんに是非、という想いで作りましたが、完璧でしっかりしたものではありません。HBLへの移植は簡単ですが、うまくいったあかつきには自分で調査できる程度のスキルなら身についていることと思います。とはいえ分からないことがあった時には遠慮なく聞いて下さい。

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

    このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください