
亲爱的问候。 我还没有听说过尚未开放源代码的GHIDRA
,可能只是聋哑/盲目/愚蠢/没有互联网的逆向工程师。 其开箱即用的功能令人惊叹:所有支持的处理器的反编译器,新架构的简单添加(由于将强大的功能转换为IR可以立即进行主动反编译),一堆简化生活的脚本, Undo
/ Redo
的可能性...而这只是所提供所有功能的很小一部分。 要说给我留下深刻的印象是什么也没说。
因此,在本文中,我想告诉您我如何为GHIDRA
编写我的第一个模块-用于Sega Mega Drive / Genesis
游戏的朗姆酒装载Sega Mega Drive / Genesis
。 要写它,我只需要几个小时! 走吧
但是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
上放一个daw,单击Next >
,同意协议,单击Install Anyway
(因为该插件没有签名),然后重新启动Eclipse
。

总的来说,新的GhidraDev
项将出现在IDE GhidraDev
以方便创建和分发项目(当然,您也可以通过常规向导为新的Eclipse
项目创建)。 此外,我们还有机会调试开发的插件或脚本。

应用程序调试器在哪里?GHIDRA
真正惹恼了这种情况, GHIDRA
就是他妈的沙哑的炒作包含几乎相同的材料,而且事实并非如此。 一个例子? 是的,请:
该工具的当前版本是9.0。 该工具具有一些选项,可以包括其他功能,例如密码分析,与Ghidra调试器OllyDbg的交互。
这是哪里? 不行
第二点:开放性。 实际上,它几乎存在,但是实际上不存在。 GHIDRA
包含用Java
编写的组件的源代码,但是如果您查看Gradle
脚本,您会发现,秘密项目对一堆外部项目有依赖关系 实验室 NSA
存储库。
在撰写本文时,尚无反编译器和SLEIGH
(这是一个用于编译处理器模块说明和转换为IR的实用程序)。
好吧,哦,我对某些事情分心了。
因此,让我们在Eclipse
创建一个新项目。
创建一个加载器项目
GhidraDev
> New
-> Ghidra Module Project...
我们指定项目的名称(考虑到Loader
类型的单词将被粘贴到文件名上,并且为了不得到sega_loaderLoader.java
类的sega_loaderLoader.java
,我们相应地命名它们)。

单击Next >
。 在这里,我们将daws放在所需类别的前面。 就我而言,这只是一个Loader
。 单击Next >
。

在这里,我们使用
指示目录的路径。 单击Next >
。

GHIDRA
允许GHIDRA
使用python(通过Jython
)编写脚本。 我将用Java
编写,所以我不敢说。 我按Finish
。

编写代码
空的项目树看起来令人印象深刻:

所有带有java
代码的文件都在/src/main/java
分支中:

getName()
首先,我们为引导加载程序选择一个名称。 getName()
方法返回它:
@Override public String getName() { return "Sega Mega Drive / Genesis Loader"; }
findSupportedLoadSpecs()
findSupportedLoadSpecs()
方法(基于二进制文件中包含的数据findSupportedLoadSpecs()
决定应使用哪个处理器模块进行反汇编(以及IDA
中的反汇编)。 在GHIDRA
术语中GHIDRA
这称为Compiler Language
。 它包括:处理器,字节序,位和编译器(如果已知)。
此方法返回受支持的体系结构和语言的列表。 如果数据格式错误,我们只返回一个空列表。
因此,在Sega Mega Drive
的情况下,单词“ SEGA
”最经常出现在标头的偏移量0x100
(这不是先决条件,但在99%的情况下可以实现)。 您需要检查此行是否在导入的文件中。 为此,将ByteProvider provider
findSupportedLoadSpecs()
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
,那么我们正在处理Segov朗姆酒,并在列表中List<LoadSpec> loadSpecs = new ArrayList<>();
可以添加Motorola m68k
。 为此,请创建一个类型为LoadSpec
的新对象,该对象的构造函数接受将要加载ROM的加载器对象(在本例中为this
) ImageBase
,一个LanguageCompilerSpecPair
类型的对象和一个标志-此LoadSpec
在列表中是其他(在列表中是) LoadSpec
可能有多个LoadSpec
)。
LanguageCompilerSpecPair
的构造函数格式如下:
- 第一个参数是
languageID
,形式为“ ProcessorName:Endianness:Bits:ExactCpu ”的字符串。 在我的情况下,应该是“ 68000:BE:32:MC68020 ”行(不幸的是,交货时未包含MC68000
,但这不是这样的问题)。 ExactCpu
可能是default
- 可以在文件
68000.opinion
中的带有
处理器描述( $(GHIDRA)/Ghidra/Processors/68000/data/languages
)的目录中找到第二个参数$(GHIDRA)/Ghidra/Processors/68000/data/languages
,以在此处查找需要指定的68000.opinion
。 我们看到这里仅显示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
您可以编写一个项目,该项目将了解不同的体系结构,不同的数据格式,成为加载程序,处理器模块,反编译器功能的扩展以及其他优点。
在IDA
,这是每种加载项的单独项目。
有多少方便? 我认为,有时在GHIDRA
!
加载(个)
在这种方法中,我们将创建段,处理输入数据,创建代码和先前已知的标签。 为此,在入口处提交以下对象以帮助我们:
ByteProvider provider
:我们已经认识他。 处理二进制文件数据LoadSpec loadSpec
:在文件的导入阶段使用findSupportedLoadSpecs
方法选择的体系结构的findSupportedLoadSpecs
。 例如,如果我们能够在一个模块中使用多种数据格式,则很有必要。 方便的List<Option> options
: List<Option> options
列表(包括自定义)。 我还没学会和他们一起工作Program program
:提供对所有必要功能的访问的主要对象:清单,地址空间,段,标签,创建数组等。MemoryConflictHandler handler
和MemoryConflictHandler handler
TaskMonitor monitor
:我们很少需要直接使用它们(通常,只需将这些对象传递给现成的方法)MessageLog log
:记录器本身
因此,对于初学者来说,让我们创建一些对象来简化GHIDRA
实体和现有数据的工作。 当然,我们肯定需要BinaryReader
:
BinaryReader reader = new BinaryReader(provider, false);
下一个 这对我们非常有用,它将简化FlatProgramAPI
类的几乎整个对象(稍后您将看到可以使用它做什么):
FlatProgramAPI fpa = new FlatProgramAPI(program, monitor);
朗姆酒标题
首先,我们将确定普通的世嘉朗姆酒的标题。 在前0x100
字节中,有一个表,该表包含指向矢量的64个DWORD指针,例如: Reset
, Trap
, DivideByZero
, VBLANK
等。
接下来是带有Roma名称,区域, ROM
和RAM
块的开始和结束地址,复选框(此字段是应开发人员的要求而不是前缀进行检查)和其他信息的结构。
让我们创建java
类来使用这些结构,以及实现将添加到结构列表中的数据类型。
向量表
我们创建一个新的类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
将接受:
FlatProgramAPI fpa
用于将long
地址转换为Hydra的Address
数据类型(实际上,此数据类型通过将地址绑定到另一个芯片-地址空间来补充地址的简单数值)BinaryReader reader
-阅读码
toAddr()
对象具有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
):
@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]; }
我们将执行相同的操作,并创建一个实现StructConverter
接口的GameHeader
类。
游戏朗姆酒头结构开始偏移 | 结束偏移 | 内容描述 |
---|
100美元 | $ 10F | 控制台名称(通常是“ SEGA MEGA DRIVE”或“ SEGA GENESIS”) |
$ 110 | $ 11楼 | 发布日期(通常为“©XXXX YYYY.MMM”,其中XXXX是公司代码,YYYY是年份,MMM-月) |
$ 120 | $ 14F | 国内名 |
$ 150 | $ 17F | 国际名 |
$ 180 | $ 18D | 版本(“ XX YYYYYYYYYYYYYY”,其中XX是游戏类型,而YY是游戏代码) |
$ 18E | $ 18F | 校验和 |
$ 190 | $ 19F | I / O支持 |
$ 1A0 | $ 1A3 | ROM启动 |
$ 1A4 | $ 1A7 | ROM结束 |
$ 1A8 | $ 1AB | RAM启动(通常$ 00FF0000) |
$ 1AC | $ 1AF | RAM结束(通常是$ 00FFFFFF) |
$ 1B0 | $ 1B2 | 'RA'和$ F8使能SRAM |
$ 1B3 | ---- | 未使用($ 20) |
$ 1B4 | $ 1B7 | SRAM启动(默认$ 00200000) |
$ 1B8 | $ 1BB | SRAM结束(默认$ 0020FFFF) |
$ 1BC | $ 1FF | 注释(未使用) |
我们设置字段,检查输入数据的长度是否足够,使用reader
对象的readNextByteArray()
, readNextUnsignedShort()
两个新方法来读取数据,并创建一个结构。 结果代码如下:
游戏头 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);
区隔
Sega有一个著名的内存区域映射,在这里我可能不会以表的形式给出,但我只会给出用于创建段的代码。
因此, FlatProgramAPI
类的对象具有createMemoryBlock()
方法,使用该方法可以方便地创建内存区域。 在输入中,它采用以下参数:
name
:地区名称address
:区域的起始地址stream
: InputStream
类型的对象,它将作为内存区域中数据的基础。 如果指定null
,则会创建一个未初始化的区域(例如,对于68K RAM
或Z80 RAM
我们只需要size
:创建区域的大小isOverlay
:接受true
或false
,并指示内存区域已覆盖。 除了可执行文件外,我不知道需要什么地方
在输出中, createMemoryBlock()
返回类型为MemoryBlock
的对象,您可以选择MemoryBlock
设置访问权限标志( Read
, Write
, Execute
)。
结果,我们得到以下形式的函数:
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
(其他所有0x3FFFFF
都已经属于其他区域)。 创建它:
InputStream romStream = provider.getInputStream(0); createSegment(fpa, romStream, "ROM", fpa.toAddr(0x000000), Math.min(romStream.available(), 0x3FFFFF), true, false, true);
InputStream
, 0.
, ( 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
. .