GHIDRA的现代化。 朗姆Sega Mega Drive装载机


亲爱的问候。 我还没有听说过尚未开放源代码的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的加载器对象(在本例中为thisImageBase ,一个LanguageCompilerSpecPair类型的对象和一个标志-此LoadSpec在列表中是其他(在列表中是) LoadSpec可能有多个LoadSpec )。


LanguageCompilerSpecPair的构造函数格式如下:


  1. 第一个参数是languageID ,形式为“ ProcessorName:Endianness:Bits:ExactCpu ”的字符串。 在我的情况下,应该是“ 68000:BE:32:MC68020 ”行(不幸的是,交货时未包含MC68000 ,但这不是这样的问题)。 ExactCpu可能是default
  2. 可以在文件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


加载(个)


在这种方法中,我们将创建段,处理输入数据,创建代码和先前已知的标签。 为此,在入口处提交以下对象以帮助我们:


  1. ByteProvider provider :我们已经认识他。 处理二进制文件数据
  2. LoadSpec loadSpec :在文件的导入阶段使用findSupportedLoadSpecs方法选择的体系结构的findSupportedLoadSpecs 。 例如,如果我们能够在一个模块中使用多种数据格式,则很有必要。 方便的
  3. List<Option> optionsList<Option> options列表(包括自定义)。 我还没学会和他们一起工作
  4. Program program :提供对所有必要功能的访问的主要对象:清单,地址空间,段,标签,创建数组等。
  5. MemoryConflictHandler handlerMemoryConflictHandler handler TaskMonitor monitor :我们很少需要直接使用它们(通常,只需将这些对象传递给现成的方法)
  6. MessageLog log :记录器本身

因此,对于初学者来说,让我们创建一些对象来简化GHIDRA实体和现有数据的工作。 当然,我们肯定需要BinaryReader


 BinaryReader reader = new BinaryReader(provider, false); 

下一个 这对我们非常有用,它将简化FlatProgramAPI类的几乎整个对象(稍后您将看到可以使用它做什么):


 FlatProgramAPI fpa = new FlatProgramAPI(program, monitor); 

朗姆酒标题


首先,我们将确定普通的世嘉朗姆酒的标题。 在前0x100字节中,有一个表,该表包含指向矢量的64个DWORD指针,例如: ResetTrapDivideByZeroVBLANK等。


接下来是带有Roma名称,区域, ROMRAM块的开始和结束地址,复选框(此字段是应开发人员的要求而不是前缀进行检查)和其他信息的结构。


让我们创建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将接受:


  1. FlatProgramAPI fpa用于将long地址转换为Hydra的Address数据类型(实际上,此数据类型通过将地址绑定到另一个芯片-地址空间来补充地址的简单数值)
  2. 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$ 19FI / O支持
$ 1A0$ 1A3ROM启动
$ 1A4$ 1A7ROM结束
$ 1A8$ 1ABRAM启动(通常$ 00FF0000)
$ 1AC$ 1AFRAM结束(通常是$ 00FFFFFF)
$ 1B0$ 1B2'RA'和$ F8使能SRAM
$ 1B3----未使用($ 20)
$ 1B4$ 1B7SRAM启动(默认$ 00200000)
$ 1B8$ 1BBSRAM结束(默认$ 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()方法,使用该方法可以方便地创建内存区域。 在输入中,它采用以下参数:


  1. name :地区名称
  2. address :区域的起始地址
  3. streamInputStream类型的对象,它将作为内存区域中数据的基础。 如果指定null ,则会创建一个未初始化的区域(例如,对于68K RAMZ80 RAM我们只需要
  4. size :创建区域的大小
  5. isOverlay :接受truefalse ,并指示内存区域已覆盖。 除了可执行文件外,我不知道需要什么地方

在输出中, createMemoryBlock()返回类型为MemoryBlock的对象,您可以选择MemoryBlock设置访问权限标志( ReadWriteExecute )。


结果,我们得到以下形式的函数:


 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 . , :


  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 . .

Source: https://habr.com/ru/post/zh-CN444562/


All Articles