====== ポインタの自動計算機能 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 }}