ご挨拶、同志。 私はまだオープンソースではないGHIDRA
について聞いたことがありません。おそらく、聴覚障害者/盲人/ GHIDRA
/インターネットなしのリバースエンジニアだけでしょう。 すぐに使えるその機能は驚くべきものです:サポートされているすべてのプロセッサのデコンパイラ、新しいアーキテクチャの簡単な追加(IRへの適切な変換による即時のアクティブな逆コンパイル)、生活を簡素化する一連のスクリプト、 Undo
/ Redo
可能性...そしてこれは提供されるすべての機能のごく一部です。 私が感銘を受けたと言うことは、ほとんど何も言わないことです。
したがって、この記事では、 Sega Mega Drive / Genesis
ゲーム用ラムローダーであるGHIDRA
最初のモジュールをどのように作成したかを説明します。 それを書くために必要なのは...たった数時間です! 行こう
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
新しいオブジェクトを作成します。このコンストラクターは、ROMがロードされるローダーオブジェクト(この場合this
)、 ImageBase
、 LanguageCompilerSpecPair
タイプのオブジェクトとフラグ-このLoadSpec
はリスト内の他の中でLoadSpec
れます(はい、リスト内)複数のLoadSpec
がある場合があります)。
LanguageCompilerSpecPair
のコンストラクター形式LanguageCompilerSpecPair
次のとおりです。
- 最初の引数は、「 ProcessorName:Endianness:Bits:ExactCpu 」という形式の文字列である
languageID
です。 私の場合、「 68000:BE:32:MC68020 」という行になります(残念ながら、MC68000
は配信に含まれていませんが、これは問題ではありません)。ExactCpu
がdefault
場合がありdefault
- ここで指定する必要があるものを見つけるための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; }
違いがあり、それはまだ非常に強いです。 GHIDRA
、さまざまなアーキテクチャ、さまざまなデータ形式を理解し、ローダー、プロセッサモジュール、逆コンパイラ機能の拡張、その他のGHIDRA
を理解する1つのプロジェクトを作成できます。
IDA
これはアドオンの種類ごとに個別のプロジェクトです。
もっと便利ですか? 私の意見では、 GHIDRA
-時々!
負荷()
この方法では、セグメントを作成し、入力データを処理し、コードと既知のラベルを作成します。 これを行うために、入力を支援するために次のオブジェクトが送信されます。
-
ByteProvider provider
:私たちはすでに彼を知っています。 バイナリファイルデータの操作 -
LoadSpec loadSpec
:findSupportedLoadSpecs
メソッドを使用してファイルのインポートフェーズ中に選択されたアーキテクチャの仕様。 たとえば、1つのモジュールで複数のデータ形式を使用できる場合に必要です。 便利な -
List<Option> options
:List<Option> options
リスト(カスタムを含む)。 私は彼らと働くことをまだ学んでいません -
Program program
:リスト、アドレス空間、セグメント、ラベル、配列の作成など、必要なすべての機能へのアクセスを提供するメインオブジェクト。 -
MemoryConflictHandler handler
とTaskMonitor monitor
:直接作業する必要はほとんどありません(通常、これらのオブジェクトを既製のメソッドに渡すだけです) -
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
を受け入れます。
-
long
アドレスをHydraのAddress
データ型に変換するためのFlatProgramAPI fpa
(実際、このデータ型は、別のチップ(アドレス空間)にバインドすることにより、アドレスの単純な数値を補完します) -
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つの新しいメソッドを使用してデータを読み取り、構造を作成します。 結果のコードは次のとおりです。
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()
メソッドがあり、これを使用してメモリ領域を作成すると便利です。 入力では、次の引数を取ります。
-
name
:地域名 -
address
:領域の開始アドレス -
stream
:メモリー領域内のデータの基礎となるInputStream
型のオブジェクト。null
を指定すると、初期化されていない領域が作成されます(たとえば、68K RAM
またはZ80 RAM
場合は、それだけが必要になります) -
size
:作成された領域のサイズ -
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
. , :
-
address
: , -
numElements
: -
dataType
: -
elementSize
:
applyTo(program)
, .
, , BYTE
, WORD
, DWORD
. , FlatProgramAPI
createByte()
, createWord()
, createDword()
..
, , (, VDP
). , :
-
Program
getSymbolTable()
, , .. -
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
. :
-
program
:Program
-
address
: , -
dataType
: -
dataLength
: .-1
-
stackPointers
:true
, - .false
-
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
、ゆっくりと当事者の一方によって失われ始めます。私にはそう思われます。