ギドラの近代化。 ラムセガメガドライブ用ローダー


ご挨拶、同志。 私はまだオープンソースではないGHIDRAについて聞いたことがありません。おそらく、聴覚障害者/盲人/ GHIDRA /インターネットなしのリバースエンジニアだけでしょう。 すぐに使えるその機能は驚くべきものです:サポートされているすべてのプロセッサのデコンパイラ、新しいアーキテクチャの簡単な追加(IRへの適切な変換による即時のアクティブな逆コンパイル)、生活を簡素化する一連のスクリプト、 Undo / Redo可能性...そしてこれは提供されるすべての機能のごく一部です。 私が感銘を受けたと言うことは、ほとんど何も言わないことです。


したがって、この記事では、 Sega Mega Drive / Genesisゲーム用ラムローダーであるGHIDRA最初のモジュールをどのように作成したかを説明します。 それを書くために必要なのは...たった数時間です! 行こう


しかし、IDAはどうですか?

IDAダウンローダーを作成するプロセスを理解するのに数日費やしました。 それはバージョン6.5だったようで、当時はSDKドキュメントに多くの問題がありました。


開発環境を準備します


GHIDRA開発者はほとんどすべてを熟考しました( Ilfak 、以前はどこにいましたか?)。 そして、新しい機能の実装を単純化するために、彼らはEclipseプラグインGhidraDevを開発しました。これは実際にコードの作成を「 支援 」します。 プラグインは開発環境に統合されており、スクリプト、ローダー、プロセッサモジュール、およびそれらの拡張機能のプロジェクトテンプレートを作成したり、数クリックでモジュールをエクスポートしたりできます(理解しているように、これはプロジェクトからのデータのエクスポートの一種です)。


プラグインをインストールするには、 Eclipse for Javaダウンロードし、[ Help ]-> Install New Software... ]をクリックしてから、[ Add ]ボタンをクリックし、[ Archive... ]ボタンを使用してプラグインでアーカイブ選択ダイアログを開きます。 GhidraDevのアーカイブは、 $(GHIDRA)/Extensions/Eclipse/GhidraDevます。 それを選択して、[ Add ]ボタンをクリックします。



表示されるリストで、GhidraにGhidra 、[ Next > ]をクリックし、契約に同意し、[ Install Anyway ] Install Anywayクリックし(プラグインに署名がないため)、 Eclipseを再起動しEclipse



合計すると、プロジェクトの便利な作成と配布のために、新しいGhidraDevアイテムがIDE GhidraDevれます(もちろん、新しいEclipseプロジェクトの通常のウィザードを使用して作成することもできます)。 さらに、開発したプラグインまたはスクリプトをデバッグする機会があります。



また、アプリケーションデバッガはどこにありますか?

GHIDRAの状況を本当に苛立たせているのは、ほとんど同じ素材を含んでいるクソGHIDRA記事であり、それは事実ではありません。 例? はい、お願いします:


ツールの現在のバージョンは9.0です。 また、このツールには、暗号解析、Ghidra DebuggerであるOllyDbgとの対話などの追加機能を含めるオプションがあります。

そして、これはどこにありますか? いや!


2番目のポイント:開放性。 実際、ほとんどそこにありますが、実際には存在しません。 GHIDRAは、 Javaで記述されたコンポーネントのソースコードが含まれていますが、 Gradleスクリプトを見ると、秘密プロジェクトからの外部プロジェクトの束に依存関係があることがわかります。 研究所 NSAリポジトリ。
執筆時点では、デコンパイラおよびSLEIGHはありません(これは、プロセッサモジュールの説明とIRへの変換をコンパイルするためのユーティリティです)。


まあまあ、私は何かに気を取られています。


それでは、 Eclipse新しいプロジェクトを作成しましょう。


ローダープロジェクトを作成する


GhidraDev > GhidraDev > GhidraDev Ghidra Module Project... GhidraDev Ghidra Module Project...


プロジェクトの名前を示します( Loaderタイプの単語がファイル名に接着されることを考慮し、 sega_loaderLoader.javaようなものを取得しないように、それに応じて名前を付けます)。



Next >クリックします。 ここでは、必要なカテゴリの前にドーズを配置します。 私の場合、これはLoaderのみです。 Next >クリックします。



ここで、ディレクトリへのパスをます。 Next >クリックします。



GHIDRA使用GHIDRAと、Pythonでスクリプトを作成できGHIDRAJython )。 Javaで記述しJavaので、一言も書きません。 Finishを押します。



コードを書く


空のプロジェクトツリーは印象的です。



javaコードを持つすべてのファイルは、 /src/main/javaブランチにあります。



getName()


始めるために、ブートローダーの名前を選択しましょう。 getName()メソッドはそれを返します:


 @Override public String getName() { return "Sega Mega Drive / Genesis Loader"; } 

findSupportedLoadSpecs()


findSupportedLoadSpecs()メソッドは、(バイナリファイルに含まれるデータに基づいて)逆プロセッサ(およびIDA )に使用するプロセッサモジュールを決定します。 GHIDRA用語ではGHIDRAこれはCompiler Languageと呼ばれます。 プロセッサ、エンディアン、ビットネス、コンパイラ(既知の場合)が含まれます。


このメソッドは、サポートされているアーキテクチャと言語のリストを返します。 データの形式が間違っている場合、空のリストを返すだけです。


そのため、 Sega Mega Drive場合0x100ヘッダーのオフセット0x100SEGA 」という単語が最も頻繁に存在します(これは前提条件ではありませんが、99%のケースで満たされます)。 この行がインポートされたファイルにあるかどうかを確認する必要があります。 これを行うために、 ByteProvider providerfindSupportedLoadSpecs() ByteProvider providerByteProvider provider 、ファイルを操作します。


ファイルからデータを読み取るために、 BinaryReaderオブジェクトを作成します。


 BinaryReader reader = new BinaryReader(provider, false); 

この場合のfalse引数は、読み取り時のBig Endianの使用を示します。 それでは行を読みましょう。 これを行うには、 readerオブジェクトのreadAsciiString(offset, size)メソッドを使用します。


 reader.readAsciiString(0x100, 4).equals(new String("SEGA")) 

equals()true返すtrue 、Segovラムを処理しています。リストでは、リストList<LoadSpec> loadSpecs = new ArrayList<>(); Motorola m68kを追加できます。 これを行うには、タイプLoadSpec新しいオブジェクトを作成します。このコンストラクターは、R​​OMがロードされるローダーオブジェクト(この場合this )、 ImageBaseLanguageCompilerSpecPairタイプのオブジェクトとフラグ-このLoadSpecはリスト内の他の中でLoadSpecれます(はい、リスト内)複数のLoadSpecがある場合があります)。


LanguageCompilerSpecPairのコンストラクター形式LanguageCompilerSpecPair次のとおりです。


  1. 最初の引数は、「 ProcessorName:Endianness:Bits:ExactCpu 」という形式の文字列であるlanguageIDです。 私の場合、「 68000:BE:32:MC68020 」という行になります(残念ながら、 MC68000は配信に含まれていませんが、これは問題ではありません)。 ExactCpudefault場合がありdefault
  2. ここで指定する必要があるものを見つけるための2番目の引数compilerSpecIDは、ファイル68000.opinion内のプロセッサ記述( $(GHIDRA)/Ghidra/Processors/68000/data/languages )が含まれるディレクトリにあります。 ここではdefaultのみが示されていることがわかります。 実際に、私たちはそれを示します

結果として、次のコードがあります(ご覧のとおり、これまでのところ複雑なものはありません)。


 @Override public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException { List<LoadSpec> loadSpecs = new ArrayList<>(); BinaryReader reader = new BinaryReader(provider, false); if (reader.readAsciiString(0x100, 4).equals(new String("SEGA"))) { loadSpecs.add(new LoadSpec(this, 0, new LanguageCompilerSpecPair("68000:BE:32:MC68020", "default"), true)); } return loadSpecs; } 

モジュールに関するIDAとGHIDRAの違い

違いがあり、それはまだ非常に強いです。 GHIDRA 、さまざまなアーキテクチャ、さまざまなデータ形式を理解し、ローダー、プロセッサモジュール、逆コンパイラ機能の拡張、その他のGHIDRAを理解する1つのプロジェクトを作成できます。


IDAこれはアドオンの種類ごとに個別のプロジェクトです。


もっと便利ですか? 私の意見では、 GHIDRA -時々!


負荷()


この方法では、セグメントを作成し、入力データを処理し、コードと既知のラベルを作成します。 これを行うために、入力を支援するために次のオブジェクトが送信されます。


  1. ByteProvider provider :私たちはすでに彼を知っています。 バイナリファイルデータの操作
  2. LoadSpec loadSpecfindSupportedLoadSpecsメソッドを使用してファイルのインポートフェーズ中に選択されたアーキテクチャの仕様。 たとえば、1つのモジュールで複数のデータ形式を使用できる場合に必要です。 便利な
  3. List<Option> optionsList<Option> optionsリスト(カスタムを含む)。 私は彼らと働くことをまだ学んでいません
  4. Program program :リスト、アドレス空間、セグメント、ラベル、配列の作成など、必要なすべての機能へのアクセスを提供するメインオブジェクト。
  5. MemoryConflictHandler handlerTaskMonitor monitor :直接作業する必要はほとんどありません(通常、これらのオブジェクトを既製のメソッドに渡すだけです)
  6. MessageLog log :ロガー自体

それでは、 GHIDRAGHIDRAエンティティと既存のデータでの作業を簡素化するオブジェクトを作成しましょう。 もちろん、 BinaryReaderは間違いなく必要です。


 BinaryReader reader = new BinaryReader(provider, false); 

次。 これは私たちにとって非常に便利で、 FlatProgramAPIクラスのオブジェクト全体をほぼ単純化します(後でそれを使って何ができるかがわかります)。


 FlatProgramAPI fpa = new FlatProgramAPI(program, monitor); 

ラムの見出し


まず、通常のセガラムの見出しを確認します。 最初の0x100バイトには、ベクターへの64個のDWORDポインターのテーブルがあります。 DivideByZeroResetTrapDivideByZeroVBLANKなどです。


次に、Romaの名前、リージョン、 ROMブロックとRAMブロックの開始と終了のアドレス、チェックボックス(フィールドはプレフィックスではなく開発者のリクエストでチェックされます)およびその他の情報を含む構造が続きます。


これらの構造を操作し、構造のリストに追加されるデータ型を実装するjavaクラスを作成しましょう。


VectorsTable


新しいクラスVectorsTableを作成し、注意して、それがStructConverterインターフェイスを実装することを示します。 このクラスでは、ベクターのアドレス(将来の使用のため)とその名前を保管します。



ベクトル名とその番号のリストを宣言します:


 private static final int VECTORS_SIZE = 0x100; private static final int VECTORS_COUNT = VECTORS_SIZE / 4; private static final String[] VECTOR_NAMES = { "SSP", "Reset", "BusErr", "AdrErr", "InvOpCode", "DivBy0", "Check", "TrapV", "GPF", "Trace", "Reserv0", "Reserv1", "Reserv2", "Reserv3", "Reserv4", "BadInt", "Reserv10", "Reserv11", "Reserv12", "Reserv13", "Reserv14", "Reserv15", "Reserv16", "Reserv17", "BadIRQ", "IRQ1", "EXT", "IRQ3", "HBLANK", "IRQ5", "VBLANK", "IRQ7", "Trap0", "Trap1", "Trap2", "Trap3", "Trap4", "Trap5", "Trap6", "Trap7", "Trap8", "Trap9", "Trap10", "Trap11", "Trap12", "Trap13","Trap14", "Trap15", "Reserv30", "Reserv31", "Reserv32", "Reserv33", "Reserv34", "Reserv35", "Reserv36", "Reserv37", "Reserv38", "Reserv39", "Reserv3A", "Reserv3B", "Reserv3C", "Reserv3D", "Reserv3E", "Reserv3F" }; 

アドレスとベクター名を保存するための別のクラスを作成します。


 package sega; import ghidra.program.model.address.Address; public class VectorFunc { private Address address; private String name; public VectorFunc(Address address, String name) { this.address = address; this.name = name; } public Address getAddress() { return address; } public String getName() { return name; } } 

ベクトルのリストは、 vectors配列に保存されます。


 private VectorFunc[] vectors; 

VectorsTableのコンストラクターはVectorsTableを受け入れます。


  1. longアドレスをHydraのAddressデータ型に変換するためのFlatProgramAPI fpa (実際、このデータ型は、別のチップ(アドレス空間)にバインドすることにより、アドレスの単純な数値を補完します)
  2. BinaryReader reader -リーディングヤード

fpaオブジェクトにはtoAddr()メソッドがあり、 readersetPointerIndex()およびreadNextUnsignedInt()ます。 基本的に、他に何も必要ありません。 コードを取得します。


 public VectorsTable(FlatProgramAPI fpa, BinaryReader reader) throws IOException { if (reader.length() < VECTORS_COUNT) { return; } reader.setPointerIndex(0); vectors = new VectorFunc[VECTORS_COUNT]; for (int i = 0; i < VECTORS_COUNT; ++i) { vectors[i] = new VectorFunc(fpa.toAddr(reader.readNextUnsignedInt()), VECTOR_NAMES[i]); } } 

構造体を実装するためにオーバーライドする必要があるtoDataType()メソッドは、構造体フィールドの名前、サイズ、各フィールドのコメントを宣言する必要があるStructureオブジェクトを返す必要があります( nullを使用できnull )。


 @Override public DataType toDataType() { Structure s = new StructureDataType("VectorsTable", 0); for (int i = 0; i < VECTORS_COUNT; ++i) { s.add(POINTER, 4, VECTOR_NAMES[i], null); } return s; } 

さて、各ベクトルまたはリスト全体(定型コードの束)を取得するメソッドを実装しましょう。


その他の方法
  public VectorFunc[] getVectors() { return vectors; } public VectorFunc getSSP() { if (vectors.length < 1) { return null; } return vectors[0]; } public VectorFunc getReset() { if (vectors.length < 2) { return null; } return vectors[1]; } public VectorFunc getBusErr() { if (vectors.length < 3) { return null; } return vectors[2]; } public VectorFunc getAdrErr() { if (vectors.length < 4) { return null; } return vectors[3]; } public VectorFunc getInvOpCode() { if (vectors.length < 5) { return null; } return vectors[4]; } public VectorFunc getDivBy0() { if (vectors.length < 6) { return null; } return vectors[5]; } public VectorFunc getCheck() { if (vectors.length < 7) { return null; } return vectors[6]; } public VectorFunc getTrapV() { if (vectors.length < 8) { return null; } return vectors[7]; } public VectorFunc getGPF() { if (vectors.length < 9) { return null; } return vectors[8]; } public VectorFunc getTrace() { if (vectors.length < 10) { return null; } return vectors[9]; } public VectorFunc getReserv0() { if (vectors.length < 11) { return null; } return vectors[10]; } public VectorFunc getReserv1() { if (vectors.length < 12) { return null; } return vectors[11]; } public VectorFunc getReserv2() { if (vectors.length < 13) { return null; } return vectors[12]; } public VectorFunc getReserv3() { if (vectors.length < 14) { return null; } return vectors[13]; } public VectorFunc getReserv4() { if (vectors.length < 15) { return null; } return vectors[14]; } public VectorFunc getBadInt() { if (vectors.length < 16) { return null; } return vectors[15]; } public VectorFunc getReserv10() { if (vectors.length < 17) { return null; } return vectors[16]; } public VectorFunc getReserv11() { if (vectors.length < 18) { return null; } return vectors[17]; } public VectorFunc getReserv12() { if (vectors.length < 19) { return null; } return vectors[18]; } public VectorFunc getReserv13() { if (vectors.length < 20) { return null; } return vectors[19]; } public VectorFunc getReserv14() { if (vectors.length < 21) { return null; } return vectors[20]; } public VectorFunc getReserv15() { if (vectors.length < 22) { return null; } return vectors[21]; } public VectorFunc getReserv16() { if (vectors.length < 23) { return null; } return vectors[22]; } public VectorFunc getReserv17() { if (vectors.length < 24) { return null; } return vectors[23]; } public VectorFunc getBadIRQ() { if (vectors.length < 25) { return null; } return vectors[24]; } public VectorFunc getIRQ1() { if (vectors.length < 26) { return null; } return vectors[25]; } public VectorFunc getEXT() { if (vectors.length < 27) { return null; } return vectors[26]; } public VectorFunc getIRQ3() { if (vectors.length < 28) { return null; } return vectors[27]; } public VectorFunc getHBLANK() { if (vectors.length < 29) { return null; } return vectors[28]; } public VectorFunc getIRQ5() { if (vectors.length < 30) { return null; } return vectors[29]; } public VectorFunc getVBLANK() { if (vectors.length < 31) { return null; } return vectors[30]; } public VectorFunc getIRQ7() { if (vectors.length < 32) { return null; } return vectors[31]; } public VectorFunc getTrap0() { if (vectors.length < 33) { return null; } return vectors[32]; } public VectorFunc getTrap1() { if (vectors.length < 34) { return null; } return vectors[33]; } public VectorFunc getTrap2() { if (vectors.length < 35) { return null; } return vectors[34]; } public VectorFunc getTrap3() { if (vectors.length < 36) { return null; } return vectors[35]; } public VectorFunc getTrap4() { if (vectors.length < 37) { return null; } return vectors[36]; } public VectorFunc getTrap5() { if (vectors.length < 38) { return null; } return vectors[37]; } public VectorFunc getTrap6() { if (vectors.length < 39) { return null; } return vectors[38]; } public VectorFunc getTrap7() { if (vectors.length < 40) { return null; } return vectors[39]; } public VectorFunc getTrap8() { if (vectors.length < 41) { return null; } return vectors[40]; } public VectorFunc getTrap9() { if (vectors.length < 42) { return null; } return vectors[41]; } public VectorFunc getTrap10() { if (vectors.length < 43) { return null; } return vectors[42]; } public VectorFunc getTrap11() { if (vectors.length < 44) { return null; } return vectors[43]; } public VectorFunc getTrap12() { if (vectors.length < 45) { return null; } return vectors[44]; } public VectorFunc getTrap13() { if (vectors.length < 46) { return null; } return vectors[45]; } public VectorFunc getTrap14() { if (vectors.length < 47) { return null; } return vectors[46]; } public VectorFunc getTrap15() { if (vectors.length < 48) { return null; } return vectors[47]; } public VectorFunc getReserv30() { if (vectors.length < 49) { return null; } return vectors[48]; } public VectorFunc getReserv31() { if (vectors.length < 50) { return null; } return vectors[49]; } public VectorFunc getReserv32() { if (vectors.length < 51) { return null; } return vectors[50]; } public VectorFunc getReserv33() { if (vectors.length < 52) { return null; } return vectors[51]; } public VectorFunc getReserv34() { if (vectors.length < 53) { return null; } return vectors[52]; } public VectorFunc getReserv35() { if (vectors.length < 54) { return null; } return vectors[53]; } public VectorFunc getReserv36() { if (vectors.length < 55) { return null; } return vectors[54]; } public VectorFunc getReserv37() { if (vectors.length < 56) { return null; } return vectors[55]; } public VectorFunc getReserv38() { if (vectors.length < 57) { return null; } return vectors[56]; } public VectorFunc getReserv39() { if (vectors.length < 58) { return null; } return vectors[57]; } public VectorFunc getReserv3A() { if (vectors.length < 59) { return null; } return vectors[58]; } public VectorFunc getReserv3B() { if (vectors.length < 60) { return null; } return vectors[59]; } public VectorFunc getReserv3C() { if (vectors.length < 61) { return null; } return vectors[60]; } public VectorFunc getReserv3D() { if (vectors.length < 62) { return null; } return vectors[61]; } public VectorFunc getReserv3E() { if (vectors.length < 63) { return null; } return vectors[62]; } public VectorFunc getReserv3F() { if (vectors.length < 64) { return null; } return vectors[63]; } 

Gameheader


同じことを行い、 StructConverterインターフェイスを実装するGameHeaderクラスを作成します。


ゲームラムのヘッダー構造
開始オフセット終了オフセット説明
100ドル$ 10Fコンソール名(通常は「SEGA MEGA DRIVE」または「SEGA GENESIS」)
110ドル$ 11Fリリース日(通常、「©XXXX YYYY.MMM」。XXXXは会社コード、YYYYは年、MMM-月)
120ドル$ 14F国内名
150ドル$ 17F国際名
180ドル18Dバージョン(「XX YYYYYYYYYYYY」、XXはゲームタイプ、YYはゲームコード)
18Eドル18米ドルチェックサム
190ドル19米ドルI / Oサポート
1A0ドル1A3ドルROMスタート
1A4ドル1A7ドルROM終了
1A8ドル1ABドルRAM開始(通常$ 00FF0000)
1米ドル1AFRAM終了(通常$ 00FFFFFF)
10億ドル10億ドル「RA」と$ F8でSRAMを有効にします
$ 1B3----未使用($ 20)
$ 1B41B7ドルSRAMの開始(デフォルト$ 00200000)
1B8ドル1BBSRAM終了(デフォルト$ 0020FFFF)
紀元前1ドル1FFメモ(未使用)

フィールドを設定し、入力データの十分な長さを確認し、 readerオブジェクトのreadNextByteArray()readNextUnsignedShort()の2つの新しいメソッドを使用してデータを読み取り、構造を作成します。 結果のコードは次のとおりです。


Gameheader
 package sega; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; import ghidra.program.flatapi.FlatProgramAPI; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.data.Structure; import ghidra.program.model.data.StructureDataType; public class GameHeader implements StructConverter { private byte[] consoleName = null; private byte[] releaseDate = null; private byte[] domesticName = null; private byte[] internationalName = null; private byte[] version = null; private short checksum = 0; private byte[] ioSupport = null; private Address romStart = null, romEnd = null; private Address ramStart = null, ramEnd = null; private byte[] sramCode = null; private byte unused = 0; private Address sramStart = null, sramEnd = null; private byte[] notes = null; FlatProgramAPI fpa; public GameHeader(FlatProgramAPI fpa, BinaryReader reader) throws IOException { this.fpa = fpa; if (reader.length() < 0x200) { return; } reader.setPointerIndex(0x100); consoleName = reader.readNextByteArray(0x10); releaseDate = reader.readNextByteArray(0x10); domesticName = reader.readNextByteArray(0x30); internationalName = reader.readNextByteArray(0x30); version = reader.readNextByteArray(0x0E); checksum = (short) reader.readNextUnsignedShort(); ioSupport = reader.readNextByteArray(0x10); romStart = fpa.toAddr(reader.readNextUnsignedInt()); romEnd = fpa.toAddr(reader.readNextUnsignedInt()); ramStart = fpa.toAddr(reader.readNextUnsignedInt()); ramEnd = fpa.toAddr(reader.readNextUnsignedInt()); sramCode = reader.readNextByteArray(0x03); unused = reader.readNextByte(); sramStart = fpa.toAddr(reader.readNextUnsignedInt()); sramEnd = fpa.toAddr(reader.readNextUnsignedInt()); notes = reader.readNextByteArray(0x44); } @Override public DataType toDataType() { Structure s = new StructureDataType("GameHeader", 0); s.add(STRING, 0x10, "ConsoleName", null); s.add(STRING, 0x10, "ReleaseDate", null); s.add(STRING, 0x30, "DomesticName", null); s.add(STRING, 0x30, "InternationalName", null); s.add(STRING, 0x0E, "Version", null); s.add(WORD, 0x02, "Checksum", null); s.add(STRING, 0x10, "IoSupport", null); s.add(POINTER, 0x04, "RomStart", null); s.add(POINTER, 0x04, "RomEnd", null); s.add(POINTER, 0x04, "RamStart", null); s.add(POINTER, 0x04, "RamEnd", null); s.add(STRING, 0x03, "SramCode", null); s.add(BYTE, 0x01, "Unused", null); s.add(POINTER, 0x04, "SramStart", null); s.add(POINTER, 0x04, "SramEnd", null); s.add(STRING, 0x44, "Notes", null); return s; } public byte[] getConsoleName() { return consoleName; } public byte[] getReleaseDate() { return releaseDate; } public byte[] getDomesticName() { return domesticName; } public byte[] getInternationalName() { return internationalName; } public byte[] getVersion() { return version; } public short getChecksum() { return checksum; } public byte[] getIoSupport() { return ioSupport; } public Address getRomStart() { return romStart; } public Address getRomEnd() { return romEnd; } public Address getRamStart() { return ramStart; } public Address getRamEnd() { return ramEnd; } public byte[] getSramCode() { return sramCode; } public byte getUnused() { return unused; } public Address getSramStart() { return sramStart; } public Address getSramEnd() { return sramEnd; } public boolean hasSRAM() { if (sramCode == null) { return false; } return sramCode[0] == 'R' && sramCode[1] == 'A' && sramCode[2] == 0xF8; } public byte[] getNotes() { return notes; } } 

ヘッダーのオブジェクトを作成します。


 vectors = new VectorsTable(fpa, reader); header = new GameHeader(fpa, reader); 

セグメント


セガには、メモリ領域のよく知られたマップがあります。おそらく、ここではテーブルの形ではなく、セグメントの作成に使用されるコードのみを示します。


そのため、 FlatProgramAPIクラスのオブジェクトにはFlatProgramAPI createMemoryBlock()メソッドがあり、これを使用してメモリ領域を作成すると便利です。 入力では、次の引数を取ります。


  1. name :地域名
  2. address :領域の開始アドレス
  3. stream :メモリー領域内のデータの基礎となるInputStream型のオブジェクト。 nullを指定すると、初期化されていない領域が作成されます(たとえば、 68K RAMまたはZ80 RAM場合は、それだけが必要になります)
  4. size :作成された領域のサイズ
  5. isOverlaytrueまたはfalse受け入れ、メモリ領域がオーバーレイであることを示します。 わからない実行可能ファイル以外に必要な場所

出力で、 createMemoryBlock()は、タイプMemoryBlockオブジェクトを返します。このオブジェクトには、アクセス権フラグ( ReadWriteExecuteRead追加で設定できます。


その結果、次の形式の関数を取得します。


 private void createSegment(FlatProgramAPI fpa, InputStream stream, String name, Address address, long size, boolean read, boolean write, boolean execute) { MemoryBlock block = null; try { block = fpa.createMemoryBlock(name, address, stream, size, false); block.setRead(read); block.setWrite(read); block.setExecute(execute); } catch (Exception e) { Msg.error(this, String.format("Error creating %s segment", name)); } } 

ここでは、エラーメッセージを表示するために、 Msgクラスの静的errorメソッドを追加で呼び出しました。


ゲームラムを含むセグメントの最大サイズは0x3FFFFF (他のすべては既に他の地域に属します)。 作成する:


 InputStream romStream = provider.getInputStream(0); createSegment(fpa, romStream, "ROM", fpa.toAddr(0x000000), Math.min(romStream.available(), 0x3FFFFF), true, false, true); 

ここでは、オフセット0から始まる入力ファイルに基づいてInputStreamを作成しました。


ユーザーに確認せずにいくつかのセグメントを作成したくない(これらはSegaCDSega32Xセグメントです)。 これを行うには、 OptionDialogクラスの静的メソッドを使用できます。 たとえば、 showYesNoDialogWithNoAsDefaultButton()は、 YESボタンとNOボタンが付いたダイアログを表示し、デフォルトでNOボタンがアクティブになります。


上記のセグメントを作成します。


 if (OptionDialog.YES_OPTION == OptionDialog.showYesNoDialogWithNoAsDefaultButton(null, "Question", "Create Sega CD segment?")) { if (romStream.available() > 0x3FFFFF) { InputStream epaStream = provider.getInputStream(0x400000); createSegment(fpa, epaStream, "EPA", fpa.toAddr(0x400000), 0x400000, true, true, false); } else { createSegment(fpa, null, "EPA", fpa.toAddr(0x400000), 0x400000, true, true, false); } } if (OptionDialog.YES_OPTION == OptionDialog.showYesNoDialogWithNoAsDefaultButton(null, "Question", "Create Sega 32X segment?")) { createSegment(fpa, null, "32X", fpa.toAddr(0x800000), 0x200000, true, true, false); } 

:


 createSegment(fpa, null, "Z80", fpa.toAddr(0xA00000), 0x10000, true, true, false); createSegment(fpa, null, "SYS1", fpa.toAddr(0xA10000), 16 * 2, true, true, false); createSegment(fpa, null, "SYS2", fpa.toAddr(0xA11000), 2, true, true, false); createSegment(fpa, null, "Z802", fpa.toAddr(0xA11100), 2, true, true, false); createSegment(fpa, null, "Z803", fpa.toAddr(0xA11200), 2, true, true, false); createSegment(fpa, null, "FDC", fpa.toAddr(0xA12000), 0x100, true, true, false); createSegment(fpa, null, "TIME", fpa.toAddr(0xA13000), 0x100, true, true, false); createSegment(fpa, null, "TMSS", fpa.toAddr(0xA14000), 4, true, true, false); createSegment(fpa, null, "VDP", fpa.toAddr(0xC00000), 2 * 9, true, true, false); createSegment(fpa, null, "RAM", fpa.toAddr(0xFF0000), 0x10000, true, true, true); if (header.hasSRAM()) { Address sramStart = header.getSramStart(); Address sramEnd = header.getSramEnd(); if (sramStart.getOffset() >= 0x200000 && sramEnd.getOffset() <= 0x20FFFF && sramStart.getOffset() < sramEnd.getOffset()) { createSegment(fpa, null, "SRAM", sramStart, sramEnd.getOffset() - sramStart.getOffset() + 1, true, true, false); } } 

,


CreateArrayCmd . , :


  1. address : ,
  2. numElements :
  3. dataType :
  4. elementSize :

applyTo(program) , .


, , BYTE , WORD , DWORD . , FlatProgramAPI createByte() , createWord() , createDword() ..


, , (, VDP ). , :


  1. Program getSymbolTable() , , ..
  2. createLabel() , , . , , SourceType.IMPORTED

, :


 private void createNamedByteArray(FlatProgramAPI fpa, Program program, Address address, String name, int numElements) { if (numElements > 1) { CreateArrayCmd arrayCmd = new CreateArrayCmd(address, numElements, ByteDataType.dataType, ByteDataType.dataType.getLength()); arrayCmd.applyTo(program); } else { try { fpa.createByte(address); } catch (Exception e) { Msg.error(this, "Cannot create byte. " + e.getMessage()); } } try { program.getSymbolTable().createLabel(address, name, SourceType.IMPORTED); } catch (InvalidInputException e) { Msg.error(this, String.format("%s : Error creating array %s", getName(), name)); } } private void createNamedWordArray(FlatProgramAPI fpa, Program program, Address address, String name, int numElements) { if (numElements > 1) { CreateArrayCmd arrayCmd = new CreateArrayCmd(address, numElements, WordDataType.dataType, WordDataType.dataType.getLength()); arrayCmd.applyTo(program); } else { try { fpa.createWord(address); } catch (Exception e) { Msg.error(this, "Cannot create word. " + e.getMessage()); } } try { program.getSymbolTable().createLabel(address, name, SourceType.IMPORTED); } catch (InvalidInputException e) { Msg.error(this, String.format("%s : Error creating array %s", getName(), name)); } } private void createNamedDwordArray(FlatProgramAPI fpa, Program program, Address address, String name, int numElements) { if (numElements > 1) { CreateArrayCmd arrayCmd = new CreateArrayCmd(address, numElements, DWordDataType.dataType, DWordDataType.dataType.getLength()); arrayCmd.applyTo(program); } else { try { fpa.createDWord(address); } catch (Exception e) { Msg.error(this, "Cannot create dword. " + e.getMessage()); } } try { program.getSymbolTable().createLabel(address, name, SourceType.IMPORTED); } catch (InvalidInputException e) { Msg.error(this, String.format("%s : Error creating array %s", getName(), name)); } } 

 createNamedDwordArray(fpa, program, fpa.toAddr(0xA04000), "Z80_YM2612", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10000), "IO_PCBVER", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10002), "IO_CT1_DATA", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10004), "IO_CT2_DATA", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10006), "IO_EXT_DATA", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10008), "IO_CT1_CTRL", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA1000A), "IO_CT2_CTRL", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA1000C), "IO_EXT_CTRL", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA1000E), "IO_CT1_RX", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10010), "IO_CT1_TX", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10012), "IO_CT1_SMODE", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10014), "IO_CT2_RX", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10016), "IO_CT2_TX", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA10018), "IO_CT2_SMODE", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA1001A), "IO_EXT_RX", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA1001C), "IO_EXT_TX", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA1001E), "IO_EXT_SMODE", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA11000), "IO_RAMMODE", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA11100), "IO_Z80BUS", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xA11200), "IO_Z80RES", 1); createNamedByteArray(fpa, program, fpa.toAddr(0xA12000), "IO_FDC", 0x100); createNamedByteArray(fpa, program, fpa.toAddr(0xA13000), "IO_TIME", 0x100); createNamedDwordArray(fpa, program, fpa.toAddr(0xA14000), "IO_TMSS", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC00000), "VDP_DATA", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC00002), "VDP__DATA", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC00004), "VDP_CTRL", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC00006), "VDP__CTRL", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC00008), "VDP_CNTR", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC0000A), "VDP__CNTR", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC0000C), "VDP___CNTR", 1); createNamedWordArray(fpa, program, fpa.toAddr(0xC0000E), "VDP____CNTR", 1); createNamedByteArray(fpa, program, fpa.toAddr(0xC00011), "VDP_PSG", 1); 


createData() DataUtilities . :


  1. program : Program
  2. address : ,
  3. dataType :
  4. dataLength : . -1
  5. stackPointers : true , - . false
  6. clearDataMode : , (, )

: .. (, ), . FlatProgramAPI createFunction() , .


:


 private void markVectorsTable(Program program, FlatProgramAPI fpa) { try { DataUtilities.createData(program, fpa.toAddr(0), vectors.toDataType(), -1, false, ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA); for (VectorFunc func : vectors.getVectors()) { fpa.createFunction(func.getAddress(), func.getName()); } } catch (CodeUnitInsertionException e) { Msg.error(this, "Vectors mark conflict at 0x000000"); } } private void markHeader(Program program, FlatProgramAPI fpa) { try { DataUtilities.createData(program, fpa.toAddr(0x100), header.toDataType(), -1, false, ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA); } catch (CodeUnitInsertionException e) { Msg.error(this, "Vectors mark conflict at 0x000100"); } } 

load()


load() setMessage() TaskMonitor , .


 monitor.setMessage(String.format("%s : Start loading", getName())); 

, :


 @Override protected void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program, MemoryConflictHandler handler, TaskMonitor monitor, MessageLog log) throws CancelledException, IOException { monitor.setMessage(String.format("%s : Start loading", getName())); BinaryReader reader = new BinaryReader(provider, false); FlatProgramAPI fpa = new FlatProgramAPI(program, monitor); vectors = new VectorsTable(fpa, reader); header = new GameHeader(fpa, reader); createSegments(fpa, provider, program, monitor); markVectorsTable(program, fpa); markHeader(program, fpa); monitor.setMessage(String.format("%s : Loading done", getName())); } 

getDefaultOptions validateOptions


,



Run -> Debug As -> 1 Ghidra . ここではすべてが簡単です。



GHIDRA


, - . Eclipse extension.properties , :


 description=Loader for Sega Mega Drive / Genesis ROMs author=Dr. MefistO createdOn=20.03.2019 

GhidraDev -> Export -> Ghidra Module Extension... :





dist zip- (- ghidra_9.0_PUBLIC_20190320_Sega.zip ) GHIDRA .


. , File -> Install Extensions... , , . ...





github- , .


レースの間:結論は次のように描くことができるIDAGHIDRA、ゆっくりと当事者の一方によって失われ始めます。私にはそう思われます。



Source: https://habr.com/ru/post/J444562/


All Articles