====== ポインタの自動計算機能 FEBuilderGBAを利用したROM解析(hacker用) ====== \\ FEBuilderGBAはGBAFEを簡単に改造するためのツールですが、\\ 本格的にコードを解析したい方を手助けすることもできます。\\ \\ 今回は、FE8の関数アドレスをFE6の関数アドレスに自動的に変換したりすることができる、ポインタの自動計算機能を紹介します。\\ ====== ポインタの自動計算機能 ====== FEBuilderGBAには、関数やデータアドレスの移植機能が搭載されています。\\ この機能を使うと、FE8Jの関数アドレスから、FE7Uの関数アドレスを求めることができます。\\ \\ 例えば、LZ77圧縮されたデータを解凍する関数を探してみましょう。\\ 先人たちの解析により、私たちは、この関数の場所が以下であることを知っています。\\ FEBuiderGBAに自動的にやらせてみましょう。\\ \\ FE8 08013008 LZ77圧縮されているデータの解凍AutoCopyOrDecompressImageToVRAM void r0:圧縮データ r1:解凍場所 {J} 08012f50 LZ77圧縮されているデータの解凍AutoCopyOrDecompressImageToVRAM void r0:圧縮データ r1:解凍場所 {U} FE7 08013688 LZ77圧縮されているデータの解凍AutoCopyOrDecompressImageToVRAM void r0:圧縮データ r1:解凍場所 {J} 08013168 LZ77圧縮されているデータの解凍AutoCopyOrDecompressImageToVRAM void r0:圧縮データ r1:解凍場所 {U} FE6 08013ca4 LZ77圧縮されているデータの解凍AutoCopyOrDecompressImageToVRAM void r0:圧縮データ r1:解凍場所 {J} \\ まずは、小手調べということで、FE8JのLZ77解凍関数 08013008 から、 FE8UのLZ77解凍関数を求めてみましょう。\\ ===== StepA01 ===== まずは、FE8JのROMを開きます。\\ ===== StepA02 ===== メニューから ツール->ポインタ計算ツール を選択してください。\\ {{ https://i.imgur.com/BGvcg4I.jpg }} ===== StepA03 ===== ポインタ計算ツールが起動します。\\ {{ https://i.imgur.com/F42b4fi.jpg }} ===== StepA04 ===== アドレスの部分に、 FE8JのLZ77解凍関数 08013008 を入れます。\\ {{ https://i.imgur.com/Tkx9MZe.jpg }} ===== StepA05 ===== 次に、"別ROM読込"ボタンをクリックして、FE8UのROMを開きます。\\ {{ https://i.imgur.com/cK1ryMk.jpg }} ===== StepA06 ===== すると、自動的に比較が行われ・・・・\\ {{ https://i.imgur.com/Z4LbBb8.jpg }} ===== StepA07 ===== 08012F50 という数字が表示されました。\\ この数字は、FE8UのLZ77解凍関数 08012F50 の値と同一です。\\ {{ https://i.imgur.com/3JkYdFf.jpg }} ====== 魔法? ====== いったいどうやって実現しているのでしょうか?\\ これはバイナリ比較を行うことで実現しています。\\ その関数と似たような処理は似たようなアセンブリコードを持っているはずだという仮説から作られています。\\ \\ もちろん、たまに間違うこともあるのですが、かなりの精度で正解を言い当ててくれます。\\ (当然ですが、バイナリ比較には、MAPファイルを利用していません。)\\ \\ 再度検索したい場合は、アドレスの項目でエンターキーを押してください。\\ ====== FE8JからFE6の関数を見つける ====== それでは、今度は、FE6の同様の関数を見つけてみましょう。\\ \\ ===== StepB01 ===== "別ROM読込"ボタンを押して、FE6のROMを読みこみます。\\ すると、自動的に比較が行われ・・・・\\ {{ https://i.imgur.com/55CluzB.jpg }} ===== StepB02 ===== おや、今度はうまくいきませんでした。\\ 0xFFFFFFFF が表示されています。見つからなかったようです。\\ {{ https://i.imgur.com/voJj0jV.jpg }} \\ これがこのツールの限界なのでしょうか?\\ そんなことはありません。\\ \\ 自動追跡システムのレベルを上げればよいのです。\\ ディフォルトでは、正確さを重視しています。\\ FE8J->FE8Uのような同世代のROMならば、これでもいいのですが、FE8J->FE6のような世代を超える場合は、バイナリが多少違います。\\ そのため、多少の正確性を犠牲にして、自動追尾レベルを上げる必要があります。\\ 自動追尾レベルを上げると、曖昧でもマッチするようになります。\\ ===== StepB03 ===== とりあえず、早速やってみましょう。\\ 今回は、最大の追跡レベル7にしてしみましょう。\\ {{ https://i.imgur.com/P9h21Rm.jpg }} \\ 追跡レベルを変更したあとは、再検索をするために、アドレスのテキストボックスでエンターを押してください。\\ {{ https://i.imgur.com/RGffwTx.jpg }} \\ ===== StepB04 ===== アドレスのテキストボックスでエンターキーを押すと、解析が開始されます。\\ 今回は、追跡レベルを上げたので、比較に少し時間がかかります。\\ {{ https://i.imgur.com/wMgVEYh.jpg }} ===== StepB05 ===== 今回は、08013CA4という数字が表示されました。\\ この数字は、FE6のLZ77解凍関数の数字と一致しています。\\ \\ やりました。自動的に、FE8Jの関数から、FE6の関数のアドレスを見つけることができました。\\ {{ https://i.imgur.com/3kbgn7b.jpg }} ====== データ追尾機能 ====== ROMのデータを自動的に追跡できることがわかりました。\\ それでは、RAMのデータはどうでしょうか?\\ \\ FE8 0202BCEC ステージの領域 {J} 0202BCF0 ステージの領域 {U} \\ 今までは追跡でませんでしたが、新しく参照値から検索機能が実装されました。\\ 早速やってみましょう。\\ \\ FE8Jのステージの領域 0202BCEC から、FE8Uのステージの領域を検索してみましょう。\\ ===== StepC01 ===== アドレスのテキストボックスに、0202BCECと入力します。\\ そして、"別ROM読込"でFE8UのROMを読みこみます。\\ {{ https://i.imgur.com/Spv9eYV.jpg }} ===== StepC02 ===== すると、自動で検索が行われ・・・・\\ {{ https://i.imgur.com/7MYsqSr.jpg }} ===== StepC03 ===== 0202BCF0という数字が表示されました。\\ これは、FE8Uのステージの領域の数字と一致しています。\\ RAM領域も検索することできてしまいました。\\ {{ https://i.imgur.com/1qa3zjh.jpg }} ====== 魔法? ====== これはLDR参照を利用した技です。\\ アルゴリズムは以下の通りです。\\ \\ 1. 元ROMで指定された数字をLDR参照している部分を見つける ↓ 2. そこから関数のプロローグまでさかのぼる。(検索する余地を増やすためです) ↓ 3. さかのぼったバイト数を記録する. ↓ 4. その関数が、相手ROMにあるかどうかを調べる ↓ 5. 相手ROMに関数があった場合、そのアドレスに、先ほどさかのぼったバイト数をアドレス加算する。 ↓ 6. 加算した場所がLDR参照かどうかを見る ↓ 7. LDR参照だった場合、その値を採用する. \\ これもまた、処理が似ているならソースコードも似ているだろうという仮説から由来するアルゴリズムです。\\ LDR値はRAMポインタですから、検索することができませんが、\\ それを呼び出しているコードはROMデータですから、検索することができます。\\ そして、コードは処理が同じならたいてい似ているものです。\\ \\ ====== 検索オプション ====== {{ https://i.imgur.com/XrwRvpr.jpg }} \\ 検索オプションを変更すると、手動でもっと細かい比較をすることができます。\\ 自動追跡では誤判定をさけるために、ある程度のところまでしか追跡しません。\\ もっと深いレベルで追跡したい場合は、手動で設定を変えることができます。\\ ===== 比較サイズ ===== 比較するバイナリサイズを指定します。\\ 当然ですが、小さい方がマッチしやすいです。ただ、小さすぎると誤判定の可能性がより高まります。\\ なるべく長い比較サイズを利用することが大切です。\\ ===== 内容 ===== 探したいものが、ASM関数なのか、データなのかを設定します。\\ これは次のパターンマッチに利用されます。\\ ===== 比較方法 ===== 完全一致の場合、 memcmp のように完全にマッチしたデータのみが検出されます。\\ パターンマッチの場合、 一部に?ワイルドカードを利用したマッチを行います。\\ \\ パターンマッチデータは、"内容"で設定したもので変わります。\\ データの場合、0x08000000 - 0x0A000000 までのポインタと思われる部分がワイルドカードに置き換われます。\\ ポインタはROMが違えば違うので、ここを無視して比較することで、マッチする可能性を高めます。\\ \\ ASMの場合は、LDR参照やBL呼び出しがワイルドカードに置き換わります。\\ ROMによって、LDR直値のデータや、BL呼び出しは違いますし、中には、構造体のオフセット値が違う場合があります。\\ そのため、これらをワイルドカードに変換してマッチさせます。\\ \\ ===== スライドして追加検索 ===== 指定したバイトをジャンプし無視して検索します。\\ コンパイラはアライメント調整のため、NOPを埋め込んだり、バージョンによってプロローグで生成される push時にpushされるレジスタの数が違ったりします。\\ これらを無視して検索します。\\ \\ ===== 自動追跡システム ===== これらの検索オプションを手でいちいち試すのは面倒なので、自動で勝手にオプションを調整して検索してくれるモードです。\\ 本来は手動で検索オプションを調整して、厳格な検査からスタートして、それでも見つからなければ曖昧な比較としていっていたのですが、面倒になったため開発されました。\\ これを利用することで、エンターキー押すだけで自動的に比較してくれます。\\ \\ \\ ===== 警告システム ===== 新しく追加した機能です。\\ ディフォルトは、真ん中のレベルの"参照あれば警告を無視する"です。\\ 間違ったマッチを避けるために、あまりに元ROMと異なるアドレスとマッチしたり、ゼロがたくさんある領域とマッチしたときは警告を表示します。\\ 自動追跡システムで検索したときに、この警告が出たマッチを、成功とみなすかどうかの設定です。\\ マッチ成功とみなせば、それ以降の検索をしません。\\ |警告をエラーにする|警告があれば、マッチしなかったものとします。| |参照あれば警告を無視する|警告があったとしても、そのアドレスへの参照が取れれば、マッチしたとみなします。| |警告を無視する|警告が出たとしても、マッチしたとみなします。| ====== 一括処理バッチ ====== まとめてアドレスの変換をやりたい場合、手でいちいちコピペするのは面倒です。\\ そこで、tsv形式とかで張り付けると、関数アドレスぽいところを自動で検出して、アドレスを求めてくれる機能を作りました。\\ これが、一括処理バッチ です。\\ ===== StepD01 ===== tsv形式とかでデータを作り、コピペしてボタンを押すだけです。\\ {{ https://i.imgur.com/QnLn125.jpg }} ===== StepD02 ===== {{ https://i.imgur.com/R8n5rmd.jpg }} ===== StepD03 ===== {{ https://i.imgur.com/PHnNVS3.jpg }} 080D4E34 MPlayContinue {J} 080D4E50 MPlayFadeOut {J} 080D4E70 m4aSoundInit {J} 080D4EE8 m4aSoundMain {J} 080D4EF4 m4aSongNumStart {J} ↓↓↓↓変換結果↓↓↓↓ 080D4E34(FFFFFFFF->FFFFFFFF,FFFFFFFF->080D013C) MPlayContinue {J} 080D4E50(FFFFFFFF->FFFFFFFF,FFFFFFFF->080D0158) MPlayFadeOut {J} 080D4E70(FFFFFFFF->FFFFFFFF,FFFFFFFF->080D0178) m4aSoundInit {J} 080D4EE8(FFFFFFFF->FFFFFFFF,0029B4C8->080D01F0) m4aSoundMain {J} 080D4EF4(FFFFFFFF->FFFFFFFF,FFFFFFFF->080D01FC) m4aSongNumStart {J} ===== アドレスの種類判定 ===== アドレスマッチとは関係ない機能です。\\ あなたがdebuggerでROM追いかけていると、よくわからない 0x085C5528 等のアドレスにであったとします。\\ いったいこれは何のデータなのか?\\ 疑問に思うときがあります。\\ \\ そんなとき、いちいち資料を探すのは面倒です。\\ \\ アドレスの欄に、あなたが調べたいアドレスを入れて、"アドレスの種類判別"ボタンを押してください。\\ FEBuilderGBAが知っている領域であれば、その領域の名前が表示されます。\\ \\ {{ https://i.imgur.com/cLePP7x.jpg }} \\ この名前は、repoint用機能のデバッグ用に持っているデータなので、簡素なものですが、ヒントにはなります。\\ ちなみに、0x085C5528は、何かと問い合わると、MenuDef4だという答えを得られます。\\ \\ 0x085C5528は、デバッグメニューの一部の領域のアドレスでした。\\ 上から4番目のメニューなので MenuDef4と表示されました。\\ {{ https://i.imgur.com/SdX2ywz.jpg }}