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







ご挨拶、同志。 私はまだオープンソースではない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でスクリプトを作成できGHIDRA



Jython



)。 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



ヘッダーのオフセット0x100



SEGA



」という単語が最も頻繁に存在します(これは前提条件ではありませんが、99%のケースで満たされます)。 この行がインポートされたファイルにあるかどうかを確認する必要があります。 これを行うために、 ByteProvider provider



findSupportedLoadSpecs()



ByteProvider provider



ByteProvider 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



)、 ImageBase



LanguageCompilerSpecPair



タイプのオブジェクトとフラグ-このLoadSpec



はリスト内の他の中でLoadSpec



れます(はい、リスト内)複数のLoadSpec



がある場合があります)。







LanguageCompilerSpecPair



のコンストラクター形式LanguageCompilerSpecPair



次のとおりです。







  1. 最初の引数は、「 ProcessorName:Endianness:Bits:ExactCpu 」という形式の文字列であるlanguageID



    です。 私の場合、「 68000:BE:32:MC68020 」という行になります(残念ながら、 MC68000



    は配信に含まれていませんが、これは問題ではありません)。 ExactCpu



    default



    場合があり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 loadSpec



    findSupportedLoadSpecs



    メソッドを使用してファイルのインポートフェーズ中に選択されたアーキテクチャの仕様。 たとえば、1つのモジュールで複数のデータ形式を使用できる場合に必要です。 便利な
  3. List<Option> options



    List<Option> options



    リスト(カスタムを含む)。 私は彼らと働くことをまだ学んでいません
  4. Program program



    :リスト、アドレス空間、セグメント、ラベル、配列の作成など、必要なすべての機能へのアクセスを提供するメインオブジェクト。
  5. MemoryConflictHandler handler



    TaskMonitor monitor



    :直接作業する必要はほとんどありません(通常、これらのオブジェクトを既製のメソッドに渡すだけです)
  6. MessageLog log



    :ロガー自体


それでは、 GHIDRA



GHIDRA



エンティティと既存のデータでの作業を簡素化するオブジェクトを作成しましょう。 もちろん、 BinaryReader



は間違いなく必要です。







 BinaryReader reader = new BinaryReader(provider, false);
      
      





次。 これは私たちにとって非常に便利で、 FlatProgramAPI



クラスのオブジェクト全体をほぼ単純化します(後でそれを使って何ができるかがわかります)。







 FlatProgramAPI fpa = new FlatProgramAPI(program, monitor);
      
      





ラムの見出し



まず、通常のセガラムの見出しを確認します。 最初の0x100



バイトには、ベクターへの64個のDWORDポインターのテーブルがあります。 DivideByZero



Reset



Trap



DivideByZero



VBLANK



などです。







次に、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()



メソッドがあり、 reader



setPointerIndex()



および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米ドル 1AF RAM終了(通常$ 00FFFFFF)
10億ドル 10億ドル 「RA」と$ F8でSRAMを有効にします
$ 1B3 ---- 未使用($ 20)
$ 1B4 1B7ドル SRAMの開始(デフォルト$ 00200000)
1B8ドル 1BB SRAM終了(デフォルト$ 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. isOverlay



    true



    またはfalse



    受け入れ、メモリ領域がオーバーレイであることを示します。 わからない実行可能ファイル以外に必要な場所


出力で、 createMemoryBlock()



は、タイプMemoryBlock



オブジェクトを返します。このオブジェクトには、アクセス権フラグ( Read



Write



Execute



Read



追加で設定できます。







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







 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



を作成しました。







ユーザーに確認せずにいくつかのセグメントを作成したくない(これらはSegaCD



Sega32X



セグメントです)。 これを行うには、 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- , .







レースの間:結論は次のように描くことができるIDA



GHIDRA



、ゆっくりと当事者の一方によって失われ始めます。私にはそう思われます。








All Articles