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解凍関数を求めてみましょう。
まずは、FE8JのROMを開きます。
いったいどうやって実現しているのでしょうか?
これはバイナリ比較を行うことで実現しています。
その関数と似たような処理は似たようなアセンブリコードを持っているはずだという仮説から作られています。
もちろん、たまに間違うこともあるのですが、かなりの精度で正解を言い当ててくれます。
(当然ですが、バイナリ比較には、MAPファイルを利用していません。)
再度検索したい場合は、アドレスの項目でエンターキーを押してください。
それでは、今度は、FE6の同様の関数を見つけてみましょう。
おや、今度はうまくいきませんでした。
0xFFFFFFFF が表示されています。見つからなかったようです。
これがこのツールの限界なのでしょうか?
そんなことはありません。
自動追跡システムのレベルを上げればよいのです。
ディフォルトでは、正確さを重視しています。
FE8J→FE8Uのような同世代のROMならば、これでもいいのですが、FE8J→FE6のような世代を超える場合は、バイナリが多少違います。
そのため、多少の正確性を犠牲にして、自動追尾レベルを上げる必要があります。
自動追尾レベルを上げると、曖昧でもマッチするようになります。
今回は、08013CA4という数字が表示されました。
この数字は、FE6のLZ77解凍関数の数字と一致しています。
やりました。自動的に、FE8Jの関数から、FE6の関数のアドレスを見つけることができました。
ROMのデータを自動的に追跡できることがわかりました。
それでは、RAMのデータはどうでしょうか?
FE8 0202BCEC ステージの領域 {J} 0202BCF0 ステージの領域 {U}
今までは追跡でませんでしたが、新しく参照値から検索機能が実装されました。
早速やってみましょう。
FE8Jのステージの領域 0202BCEC から、FE8Uのステージの領域を検索してみましょう。
これはLDR参照を利用した技です。
アルゴリズムは以下の通りです。
1. 元ROMで指定された数字をLDR参照している部分を見つける ↓ 2. そこから関数のプロローグまでさかのぼる。(検索する余地を増やすためです) ↓ 3. さかのぼったバイト数を記録する. ↓ 4. その関数が、相手ROMにあるかどうかを調べる ↓ 5. 相手ROMに関数があった場合、そのアドレスに、先ほどさかのぼったバイト数をアドレス加算する。 ↓ 6. 加算した場所がLDR参照かどうかを見る ↓ 7. LDR参照だった場合、その値を採用する.
これもまた、処理が似ているならソースコードも似ているだろうという仮説から由来するアルゴリズムです。
LDR値はRAMポインタですから、検索することができませんが、
それを呼び出しているコードはROMデータですから、検索することができます。
そして、コードは処理が同じならたいてい似ているものです。
検索オプションを変更すると、手動でもっと細かい比較をすることができます。
自動追跡では誤判定をさけるために、ある程度のところまでしか追跡しません。
もっと深いレベルで追跡したい場合は、手動で設定を変えることができます。
比較するバイナリサイズを指定します。
当然ですが、小さい方がマッチしやすいです。ただ、小さすぎると誤判定の可能性がより高まります。
なるべく長い比較サイズを利用することが大切です。
探したいものが、ASM関数なのか、データなのかを設定します。
これは次のパターンマッチに利用されます。
完全一致の場合、 memcmp のように完全にマッチしたデータのみが検出されます。
パターンマッチの場合、 一部に?ワイルドカードを利用したマッチを行います。
パターンマッチデータは、“内容”で設定したもので変わります。
データの場合、0x08000000 - 0x0A000000 までのポインタと思われる部分がワイルドカードに置き換われます。
ポインタはROMが違えば違うので、ここを無視して比較することで、マッチする可能性を高めます。
ASMの場合は、LDR参照やBL呼び出しがワイルドカードに置き換わります。
ROMによって、LDR直値のデータや、BL呼び出しは違いますし、中には、構造体のオフセット値が違う場合があります。
そのため、これらをワイルドカードに変換してマッチさせます。
指定したバイトをジャンプし無視して検索します。
コンパイラはアライメント調整のため、NOPを埋め込んだり、バージョンによってプロローグで生成される push時にpushされるレジスタの数が違ったりします。
これらを無視して検索します。
これらの検索オプションを手でいちいち試すのは面倒なので、自動で勝手にオプションを調整して検索してくれるモードです。
本来は手動で検索オプションを調整して、厳格な検査からスタートして、それでも見つからなければ曖昧な比較としていっていたのですが、面倒になったため開発されました。
これを利用することで、エンターキー押すだけで自動的に比較してくれます。
新しく追加した機能です。
ディフォルトは、真ん中のレベルの“参照あれば警告を無視する”です。
間違ったマッチを避けるために、あまりに元ROMと異なるアドレスとマッチしたり、ゼロがたくさんある領域とマッチしたときは警告を表示します。
自動追跡システムで検索したときに、この警告が出たマッチを、成功とみなすかどうかの設定です。
マッチ成功とみなせば、それ以降の検索をしません。
警告をエラーにする | 警告があれば、マッチしなかったものとします。 |
参照あれば警告を無視する | 警告があったとしても、そのアドレスへの参照が取れれば、マッチしたとみなします。 |
警告を無視する | 警告が出たとしても、マッチしたとみなします。 |
まとめてアドレスの変換をやりたい場合、手でいちいちコピペするのは面倒です。
そこで、tsv形式とかで張り付けると、関数アドレスぽいところを自動で検出して、アドレスを求めてくれる機能を作りました。
これが、一括処理バッチ です。
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が知っている領域であれば、その領域の名前が表示されます。
この名前は、repoint用機能のデバッグ用に持っているデータなので、簡素なものですが、ヒントにはなります。
ちなみに、0x085C5528は、何かと問い合わると、MenuDef4だという答えを得られます。
0x085C5528は、デバッグメニューの一部の領域のアドレスでした。
上から4番目のメニューなので MenuDef4と表示されました。