diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/AppleSingleDoubleBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/AppleSingleDoubleBinaryAnalysisCommand.java index 6e77bfcbe2..d0c06486d8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/AppleSingleDoubleBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/AppleSingleDoubleBinaryAnalysisCommand.java @@ -15,9 +15,10 @@ */ package ghidra.app.cmd.formats; -import java.io.IOException; import java.util.List; +import java.io.IOException; + import ghidra.app.plugin.core.analysis.AnalysisWorker; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.util.bin.ByteProvider; @@ -49,8 +50,8 @@ public class AppleSingleDoubleBinaryAnalysisCommand extends FlatProgramAPI public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor) throws CancelledException, Exception { try { - ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), - currentProgram.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); AppleSingleDouble header = new AppleSingleDouble(provider); Address address = toAddr(0); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffArchiveBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffArchiveBinaryAnalysisCommand.java index c05e082d6f..93073cc677 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffArchiveBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffArchiveBinaryAnalysisCommand.java @@ -44,8 +44,8 @@ public class CoffArchiveBinaryAnalysisCommand extends FlatProgramAPI public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor) throws Exception, CancelledException { - ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), - currentProgram.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); if (!CoffArchiveHeader.isMatch(provider)) { return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffBinaryAnalysisCommand.java index 61824951da..4e2f8e1932 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/CoffBinaryAnalysisCommand.java @@ -66,9 +66,8 @@ public class CoffBinaryAnalysisCommand extends FlatProgramAPI public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor) throws Exception, CancelledException { - ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), - currentProgram.getAddressFactory().getDefaultAddressSpace()); - + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); CoffFileHeader header = new CoffFileHeader(provider); if (!CoffMachineType.isMachineTypeDefined(header.getMagic())) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java index 2bb23a25c3..7bb0256610 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java @@ -77,8 +77,8 @@ public class ElfBinaryAnalysisCommand extends FlatProgramAPI Listing listing = currentProgram.getListing(); SymbolTable symbolTable = currentProgram.getSymbolTable(); - ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), - currentProgram.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); try { ElfHeader elf = new ElfHeader(provider, msg -> messages.appendMsg(msg)); elf.parse(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/MachoBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/MachoBinaryAnalysisCommand.java index 4bd91357b4..e325c3d947 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/MachoBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/MachoBinaryAnalysisCommand.java @@ -89,8 +89,8 @@ public class MachoBinaryAnalysisCommand extends FlatProgramAPI BookmarkManager bookmarkManager = program.getBookmarkManager(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); try { MachHeader header = diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PefBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PefBinaryAnalysisCommand.java index c434147d76..5c7f93fee2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PefBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PefBinaryAnalysisCommand.java @@ -15,6 +15,10 @@ */ package ghidra.app.cmd.formats; +import java.util.List; + +import java.io.IOException; + import ghidra.app.cmd.data.CreateStringCmd; import ghidra.app.plugin.core.analysis.AnalysisWorker; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; @@ -32,9 +36,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.List; - public class PefBinaryAnalysisCommand extends FlatProgramAPI implements BinaryAnalysisCommand, AnalysisWorker { private MessageLog messages = new MessageLog(); @@ -47,8 +48,7 @@ public class PefBinaryAnalysisCommand extends FlatProgramAPI implements BinaryAn public boolean canApply(Program program) { try { ByteProvider provider = - new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); new ContainerHeader(provider); return true; } @@ -61,8 +61,7 @@ public class PefBinaryAnalysisCommand extends FlatProgramAPI implements BinaryAn public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor) throws Exception, CancelledException { ByteProvider provider = - new MemoryByteProvider(currentProgram.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); try { ContainerHeader header = new ContainerHeader(provider); header.parse(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PortableExecutableBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PortableExecutableBinaryAnalysisCommand.java index 230910c58f..072bf8cf3d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PortableExecutableBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/PortableExecutableBinaryAnalysisCommand.java @@ -15,9 +15,10 @@ */ package ghidra.app.cmd.formats; -import java.io.IOException; import java.util.List; +import java.io.IOException; + import ghidra.app.plugin.core.analysis.AnalysisWorker; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.util.bin.*; @@ -32,7 +33,6 @@ import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; import ghidra.program.model.symbol.SourceType; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; @@ -48,11 +48,8 @@ public class PortableExecutableBinaryAnalysisCommand extends FlatProgramAPI @Override public boolean canApply(Program program) { try { - Memory memory = program.getMemory(); - - ByteProvider provider = new MemoryByteProvider(memory, - program.getAddressFactory().getDefaultAddressSpace()); - + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); DOSHeader dosHeader = new DOSHeader(reader); @@ -75,8 +72,8 @@ public class PortableExecutableBinaryAnalysisCommand extends FlatProgramAPI public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor) throws Exception, CancelledException { - ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); PortableExecutable pe = new PortableExecutable(provider, SectionLayout.FILE); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DwarfLineNumberAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DwarfLineNumberAnalyzer.java index 66beaa74b5..9a124586f9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DwarfLineNumberAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DwarfLineNumberAnalyzer.java @@ -15,9 +15,10 @@ */ package ghidra.app.plugin.core.analysis; +import java.util.List; + import java.io.File; import java.io.IOException; -import java.util.List; import ghidra.app.services.*; import ghidra.app.util.bin.*; @@ -46,6 +47,7 @@ public class DwarfLineNumberAnalyzer extends AbstractAnalyzer { setSupportsOneTimeAnalysis(); } + @Override public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); @@ -59,7 +61,7 @@ public class DwarfLineNumberAnalyzer extends AbstractAnalyzer { BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); - while (!monitor.isCancelled() && reader.getPointerIndex() < provider.length()) { + while (!monitor.isCancelled() && reader.hasNext()) { long startIndex = reader.getPointerIndex(); StatementProgramPrologue prologue = new StatementProgramPrologue(reader); @@ -134,10 +136,9 @@ public class DwarfLineNumberAnalyzer extends AbstractAnalyzer { else if (ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) { // We now load the .debug section as an overlay block, no need for the // original file - MemoryBlock block = null; - block = program.getMemory().getBlock(sectionNames.SECTION_NAME_LINE()); + MemoryBlock block = program.getMemory().getBlock(sectionNames.SECTION_NAME_LINE()); if (block != null) { - return new MemoryByteProvider(program.getMemory(), block.getStart()); + return MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), block); } // TODO: this will not handle the case where the .debug section is // in a separate file. Can the file in a separate location? @@ -147,6 +148,7 @@ public class DwarfLineNumberAnalyzer extends AbstractAnalyzer { program.getExecutableFormat()); } + @Override public boolean canAnalyze(Program program) { return isElfOrMacho(program); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC1_ClassAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC1_ClassAnalyzer.java index 17126e8f34..3cbb09be1a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC1_ClassAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC1_ClassAnalyzer.java @@ -15,6 +15,11 @@ */ package ghidra.app.plugin.core.analysis; +import java.util.ArrayList; +import java.util.List; + +import java.io.IOException; + import ghidra.app.services.*; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.MemoryByteProvider; @@ -28,10 +33,6 @@ import ghidra.program.model.mem.MemoryBlock; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - public class ObjectiveC1_ClassAnalyzer extends AbstractAnalyzer { private static final String DESCRIPTION = "An analyzer for extracting Objective-C class structure information."; @@ -46,13 +47,12 @@ public class ObjectiveC1_ClassAnalyzer extends AbstractAnalyzer { setDefaultEnablement(true); } + @Override public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { MemoryByteProvider provider = - new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); - + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); ObjectiveC1_State state = @@ -94,6 +94,7 @@ public class ObjectiveC1_ClassAnalyzer extends AbstractAnalyzer { } } + @Override public boolean canAnalyze(Program program) { return ObjectiveC1_Constants.isObjectiveC(program); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java index b5058dbf59..c9f146ffdb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_ClassAnalyzer.java @@ -15,9 +15,10 @@ */ package ghidra.app.plugin.core.analysis; -import java.io.IOException; import java.util.*; +import java.io.IOException; + import ghidra.app.services.*; import ghidra.app.util.bin.*; import ghidra.app.util.bin.format.macho.dyld.LibObjcOptimization; @@ -67,8 +68,8 @@ public class ObjectiveC2_ClassAnalyzer extends AbstractAnalyzer { ObjectiveC2_State state = new ObjectiveC2_State(program, monitor, ObjectiveC2_Constants.CATEGORY_PATH); - try (ByteProvider provider = new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace())) { + try (ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false)) { BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); // Create a map of Objective-C specific memory blocks. If this is a dyld_shared_cache diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java index 6fbb274823..5867d96645 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java @@ -28,6 +28,12 @@ import ghidra.util.*; * */ public class BinaryReader { + + // jvm's will typically refuse to allocate arrays that are exactly Integer.MAX_VALUE. + // This is a conservative stab at a max array element count since we don't have a requirement + // to reach exactly 2g elements + private static final int MAX_SANE_BUFFER = Integer.MAX_VALUE - 1024; + /** * The size of a BYTE in Java. */ @@ -66,7 +72,7 @@ public class BinaryReader { public BinaryReader(ByteProvider provider, boolean isLittleEndian) { this(provider, DataConverter.getInstance(!isLittleEndian), 0); } - + /** * Creates a BinaryReader instance. * @@ -147,6 +153,7 @@ public class BinaryReader { /** * Returns the length of the underlying file. + * * @return returns the length of the underlying file * @exception IOException if an I/O error occurs */ @@ -155,18 +162,19 @@ public class BinaryReader { } /** - * Returns true if the specified index into - * the underlying byte provider is valid. - * @param index the index in the byte provider + * Returns true if the specified unsigned int32 index into the underlying byte provider is + * valid. + * + * @param index an integer that is treated as an unsigned int32 index into the byte provider * @return returns true if the specified index is valid */ public boolean isValidIndex(int index) { - return provider.isValidIndex(index & Conv.INT_MASK); + return provider.isValidIndex(Integer.toUnsignedLong(index)); } /** - * Returns true if the specified index into - * the underlying byte provider is valid. + * Returns true if the specified index into the underlying byte provider is valid. + * * @param index the index in the byte provider * @return returns true if the specified index is valid */ @@ -175,19 +183,75 @@ public class BinaryReader { } /** - * Aligns the current index on the specified alignment value. - * For example, if current index was 123 and align value was - * 16, then current index would become 128. + * Returns true if the specified range is valid and does not wrap around the end of the + * index space. + * + * @param startIndex the starting index to check, treated as an unsigned int64 + * @param count the number of bytes to check + * @return boolean true if all bytes between startIndex to startIndex+count (exclusive) are + * valid (according to the underlying byte provider) + */ + public boolean isValidRange(long startIndex, int count) { + if (count < 0) { + return false; + } + if (count > 1) { + // check the end of the range first to fail fast + + long endIndex = startIndex + (count - 1); + if (Long.compareUnsigned(endIndex, startIndex) < 0) { + // the requested range [startIndex..startIndex+count] wraps around the int64 to 0, so fail + return false; + } + + if (!provider.isValidIndex(endIndex)) { + return false; + } + count--; // don't check the last element twice + } + for (int i = 0; i < count; i++) { + if (!provider.isValidIndex(startIndex + i)) { + return false; + } + } + return true; + } + + /** + * Returns true if this stream has data that could be read at the current position. + * + * @return true if there are more bytes that could be read at the + * {@link #getPointerIndex() current index}. + */ + public boolean hasNext() { + return provider.isValidIndex(currentIndex); + } + + /** + * Returns true if this stream has data that could be read at the current position. + * + * @param count number of bytes to verify + * @return true if there are at least count more bytes that could be read at the + * {@link #getPointerIndex() current index}. + */ + public boolean hasNext(int count) { + return isValidRange(currentIndex, count); + } + + /** + * Advances the current index so that it aligns to the specified value (if not already + * aligned). + *

+ * For example, if current index was 123 and align value was 16, then current index would + * be advanced to 128. + * * @param alignValue - * @return the number of bytes required to align + * @return the number of bytes required to align (0..alignValue-1) */ public int align(int alignValue) { - long align = currentIndex % alignValue; - if (align == 0) { - return 0; - } - currentIndex = currentIndex + (alignValue - align); - return (int) (alignValue - align); + long prevIndex = currentIndex; + currentIndex = NumericUtilities.getUnsignedAlignedValue(currentIndex, alignValue); + return (int) (currentIndex - prevIndex); } //////////////////////////////////////////////////////////////////// @@ -279,7 +343,7 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readNextUnsignedByte() throws IOException { - return readNextByte() & NumberUtil.UNSIGNED_BYTE_MASK; + return Byte.toUnsignedInt(readNextByte()); } /** @@ -301,7 +365,7 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readNextUnsignedShort() throws IOException { - return readNextShort() & NumberUtil.UNSIGNED_SHORT_MASK; + return Short.toUnsignedInt(readNextShort()); } /** @@ -323,7 +387,7 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public long readNextUnsignedInt() throws IOException { - return readNextInt() & NumberUtil.UNSIGNED_INT_MASK; + return Integer.toUnsignedLong(readNextInt()); } /** @@ -463,7 +527,6 @@ public class BinaryReader { return result; } - /** * Reads a byte array of nElements * starting at the current index and then increments the current @@ -520,20 +583,33 @@ public class BinaryReader { // String stuff //-------------------------------------------------------------------------------------------- private byte[] readUntilNullTerm(long index, int charLen) throws IOException { - long maxPos = provider.length() - charLen; - if (index > maxPos) { - throw new EOFException(String.format("Attempted to read string at 0x%x", index)); - } - long curPos = index; ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (; curPos <= maxPos; curPos += charLen) { - byte[] bytes = readByteArray(curPos, charLen); - if (isNullTerm(bytes, 0, charLen)) { - return baos.toByteArray(); + long curPos = index; + for (; Long.compareUnsigned(curPos, index) >= 0; curPos += charLen) { + // loop while we haven't wrapped the index value around to 0 + if ((long) baos.size() + charLen >= MAX_SANE_BUFFER) { + // gracefully handle hitting the limit of the ByteArrayOutputStream before it fails + throw new EOFException("Run-on unterminated string at 0x%s..0x%s".formatted( + Long.toUnsignedString(index, 16), Long.toUnsignedString(curPos, 16))); + } + try { + byte[] bytes = readByteArray(curPos, charLen); + if (isNullTerm(bytes, 0, charLen)) { + return baos.toByteArray(); + } + baos.write(bytes); + } + catch (IOException e) { + if (baos.size() == 0) { + // failed trying to read the first byte + throw new EOFException("Attempted to read string at 0x%s" + .formatted(Long.toUnsignedString(index, 16))); + } + break; // fall thru to throw new EOF(unterminate string) } - baos.write(bytes); } - throw new EOFException(String.format("Unterminated string at 0x%x..0x%x", index, curPos)); + throw new EOFException("Unterminated string at 0x%s..0x%s" + .formatted(Long.toUnsignedString(index, 16), Long.toUnsignedString(curPos, 16))); } private boolean isNullTerm(byte[] bytes, int offset, int charLen) { @@ -711,7 +787,7 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readUnsignedByte(long index) throws IOException { - return readByte(index) & NumberUtil.UNSIGNED_BYTE_MASK; + return Byte.toUnsignedInt(readByte(index)); } /** @@ -732,7 +808,7 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public int readUnsignedShort(long index) throws IOException { - return readShort(index) & NumberUtil.UNSIGNED_SHORT_MASK; + return Short.toUnsignedInt(readShort(index)); } /** @@ -753,7 +829,7 @@ public class BinaryReader { * @exception IOException if an I/O error occurs */ public long readUnsignedInt(long index) throws IOException { - return readInt(index) & NumberUtil.UNSIGNED_INT_MASK; + return Integer.toUnsignedLong(readInt(index)); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/ByteProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/ByteProvider.java index b871e35dd9..0e41572d2c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/ByteProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/ByteProvider.java @@ -74,6 +74,20 @@ public interface ByteProvider extends Closeable { */ public long length() throws IOException; + /** + * Returns true if this ByteProvider does not contain any bytes. + * + * @return boolean true if this provider is empty, false if contains bytes + */ + default public boolean isEmpty() { + try { + return length() == 0; + } + catch (IOException e) { + return true; + } + } + /** * Returns true if the specified index is valid. * @@ -127,4 +141,5 @@ public interface ByteProvider extends Closeable { } return new ByteProviderInputStream(this, index); } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/EmptyByteProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/EmptyByteProvider.java index a45fbd58a5..266c837a72 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/EmptyByteProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/EmptyByteProvider.java @@ -81,6 +81,11 @@ public class EmptyByteProvider implements ByteProvider { return 0; } + @Override + public boolean isEmpty() { + return true; + } + @Override public boolean isValidIndex(long index) { return false; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java index 10023800e2..a4794fe773 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java @@ -18,11 +18,37 @@ package ghidra.app.util.bin; import java.io.*; import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; /** * A {@link ByteProvider} implementation based on {@link Memory}. + *

+ * The bytes returned by this provider are indexed relative to the {@code baseAddress} + * supplied to the constructor, and are limited to {@link MemoryBlock memory blocks} of the + * same address space. + *

+ * Warnings: + *

+ * Using this ByteProvider with memory block/address spaces that are not simple "ram" initialized + * memory blocks is fraught with peril. + *

+ * Addresses and address spaces can use all 64 bits of a {@code long} as an offset, which + * causes a problem when trying to express the correct {@link #length()} of this ByteProvider as + * a long. (this is why address ranges deal with inclusive end values instead of exclusive). + *

+ *

+ * Not all byte provider index locations between 0 and {@link #length()} will be valid + * (because gaps between memory blocks), and may generate exceptions when those locations are read. + *

*/ public class MemoryByteProvider implements ByteProvider { @@ -33,54 +59,149 @@ public class MemoryByteProvider implements ByteProvider { * @param block {@link MemoryBlock} to read from * @return new {@link ByteProvider} that contains the bytes of the specified MemoryBlock */ - public static ByteProvider createMemoryBlockByteProvider(Memory memory, MemoryBlock block) { - long blockLen = block.getEnd().subtract(block.getStart()) + 1; - ByteProvider bp = new MemoryByteProvider(memory, block.getStart()); - return new ByteProviderWrapper(bp, 0, blockLen); + public static MemoryByteProvider createMemoryBlockByteProvider(Memory memory, + MemoryBlock block) { + return new MemoryByteProvider(memory, block.getStart(), block.getEnd()); + } + + /** + * Create a {@link ByteProvider} that starts at the beginning of the specified + * {@link Program program's} memory, containing either just the first + * memory block, or all memory blocks (of the same address space). + * + * @param program {@link Program} to read + * @param firstBlockOnly boolean flag, if true, only the first memory block will be accessible + * via the returned provider, if false, all memory blocks of the address space will be accessible + * @return new {@link MemoryByteProvider}, starting at program's minAddress + */ + public static MemoryByteProvider createProgramHeaderByteProvider(Program program, + boolean firstBlockOnly) { + return new MemoryByteProvider(program.getMemory(), program.getMinAddress(), firstBlockOnly); + } + + /** + * Create a {@link ByteProvider} that starts at the beginning (e.g. 0) of the specified + * {@link Program program's} default address space memory, containing either the first memory + * block, or all memory blocks (of the same address space). + * + * @param program {@link Program} to read + * @param firstBlockOnly boolean flag, if true, only the first memory block will be accessible + * via the returned provider, if false, all memory blocks of the address space will be accessible + * @return new {@link MemoryByteProvider}, starting at program's minAddress + */ + public static MemoryByteProvider createDefaultAddressSpaceByteProvider(Program program, + boolean firstBlockOnly) { + return new MemoryByteProvider(program.getMemory(), + program.getAddressFactory().getDefaultAddressSpace().getMinAddress(), firstBlockOnly); } protected Memory memory; protected Address baseAddress; + protected long maxOffset; // max valid offset, inclusive + protected boolean isEmtpy; // empty is tracked separately because maxOffset == 0 does not mean empty /** - * Constructs a new {@link MemoryByteProvider} for a specific {@link AddressSpace}. Bytes will be - * provided starting at address 0 in the space. + * Constructs a new {@link MemoryByteProvider} for a specific {@link AddressSpace}. Bytes + * will be provided relative to the minimum address (typically 0) in the space, and ranges + * to the highest address in the same address space currently found in the memory map. + *

+ * * * @param memory the {@link Memory} * @param space the {@link AddressSpace} */ public MemoryByteProvider(Memory memory, AddressSpace space) { - this(memory, space.getAddress(0)); + this(memory, space.getMinAddress()); } /** - * Constructs a new {@link MemoryByteProvider} relative to the specified base address. + * Constructs a new {@link MemoryByteProvider} relative to the specified base address, + * containing the address range to the highest address in the same address space currently + * found in the memory map. * * @param memory the {@link Memory} * @param baseAddress the base address */ public MemoryByteProvider(Memory memory, Address baseAddress) { - this.memory = memory; - this.baseAddress = baseAddress; + this(memory, baseAddress, false); } /** - * Converts an index into this ByteProvider into an {@link Address}. - *

+ * Constructs a new {@link MemoryByteProvider} relative to the specified base address, + * containing the address range to the end of the first memory block, or the highest address + * in the same address space, currently found in the memory map. * - * @param index absolute index in this ByteProvider to convert into an Address - * @return {@link Address} - * @throws AddressOutOfBoundsException if wrapping is not supported by the - * corresponding address space and the addition causes an out-of-bounds - * error + * @param memory the {@link Memory} + * @param baseAddress the base address + * @param firstBlockOnly boolean flag, if true, only the first memory block will be accessible, + * if false, all memory blocks of the address space will be accessible */ - public Address getAddress(long index) { - return baseAddress.add(index); + public MemoryByteProvider(Memory memory, Address baseAddress, boolean firstBlockOnly) { + this(memory, baseAddress, firstBlockOnly + ? findEndOfBlock(memory, baseAddress) + : findAddressSpaceMax(memory, baseAddress)); + } + + /** + * Constructs a new {@link MemoryByteProvider} relative to the specified base address, with + * the specified length. + * + * @param memory the {@link Memory} + * @param baseAddress the base address + * @param maxAddress the highest address accessible by this provider (inclusive), or null + * if there is no memory + */ + private MemoryByteProvider(Memory memory, Address baseAddress, Address maxAddress) { + this.memory = memory; + this.baseAddress = baseAddress; + this.maxOffset = maxAddress != null + ? maxAddress.subtract(baseAddress) + : 0; + this.isEmtpy = maxAddress == null; + } + + private Address getAddress(long index) throws IOException { + if (index == 0) { + return baseAddress; + } + long base = baseAddress.getOffset(); + long newAddress = base + index; + if (Long.compareUnsigned(base, newAddress) > 0) { + throw new IOException("Invalid index: %s".formatted(Long.toUnsignedString(index))); + } + return baseAddress.getNewAddress(newAddress); + } + + /** + * Returns the address of the first byte of this provider. + * + * @return address of the first byte returned by this provider (at index 0) + */ + public Address getStartAddress() { + return baseAddress; + } + + /** + * Returns the address of the last byte of this provider. + * + * @return address of the last byte returned by this provider + */ + public Address getEndAddress() { + return baseAddress.getNewAddress(baseAddress.getOffset() + maxOffset); + } + + /** + * Returns the address range of the bytes of this provider. + * + * @return address range of first byte to last byte of this provider + */ + public AddressSetView getAddressSet() { + return new AddressSet(baseAddress, getEndAddress()); } @Override - public InputStream getInputStream(long index) throws IOException { - return new MemoryByteProviderInputStream(memory, baseAddress.add(index)); + public boolean isEmpty() { + return isEmtpy; } @Override @@ -100,28 +221,36 @@ public class MemoryByteProvider implements ByteProvider { @Override public long length() throws IOException { - MemoryBlock block = memory.getBlock(baseAddress); - if (block == null || !block.isInitialized()) { + if (isEmtpy) { return 0; } - return block.getEnd().subtract(baseAddress) + 1; + + // clamp the max length to Long.MAX_VALUE + return Long.compareUnsigned(maxOffset, Long.MAX_VALUE - 1) >= 0 + ? Long.MAX_VALUE + : maxOffset + 1; } @Override public boolean isValidIndex(long index) { + // this method treats the index as an unsigned int64, and will give accurate results + // for the entire range of the underlying AddressSpace try { - Address indexAddress = baseAddress.add(index); - return memory.contains(indexAddress); + if (isEmtpy || Long.compareUnsigned(index, maxOffset) > 0) { + return false; + } + return memory.contains(getAddress(index)); } - catch (AddressOutOfBoundsException e) { + catch (IOException | AddressOutOfBoundsException e) { return false; } } @Override public byte readByte(long index) throws IOException { + ensureBounds(index, 1); try { - return memory.getByte(baseAddress.add(index)); + return memory.getByte(getAddress(index)); } catch (Exception e) { throw new IOException(e.getMessage()); @@ -130,11 +259,13 @@ public class MemoryByteProvider implements ByteProvider { @Override public byte[] readBytes(long index, long length) throws IOException { + ensureBounds(index, length); try { byte[] bytes = new byte[(int) length]; - int nRead = memory.getBytes(baseAddress.add(index), bytes); + int nRead = memory.getBytes(getAddress(index), bytes); if (nRead != length) { - throw new IOException("Unable to read " + length + " bytes at index " + index); + throw new IOException("Unable to read %d bytes at index %s".formatted(length, + Long.toUnsignedString(index))); } return bytes; } @@ -150,4 +281,70 @@ public class MemoryByteProvider implements ByteProvider { public void close() { // don't do anything for now } + + //-------------------------------------------------------------------------------------------- + private void ensureBounds(long index, long length) throws IOException { + // ensure length is valid + if (length < 0 || length > Integer.MAX_VALUE) { + throw new IOException( + "Unable to read more than Integer.MAX_VALUE bytes in one operation: %s" + .formatted(Long.toUnsignedString(length))); + } + if (index == 0 && length == 0) { + return; // success for read of 0 bytes at offset 0 + } + + // ensure read start index is valid + if (isEmtpy || Long.compareUnsigned(index, maxOffset) > 0) { + throw new EOFException("Invalid index: %s".formatted(Long.toUnsignedString(index))); + } + + // NOTE: there should be a +1 on "remaining" to accurately model the count of remaining bytes + // Because it could cause an overflow, adjust "length" by -1 instead + long remaining = maxOffset - index /* + 1 -> becomes length - 1 */; + + // ensure length of read is within bounds + if (length != 0 && Long.compareUnsigned(length - 1, remaining) > 0) { + throw new EOFException( + "Unable to read past EOF: %s, %d".formatted(Long.toUnsignedString(index), length)); + } + } + + private static Address findEndOfBlock(Memory memory, Address minAddr) { + MemoryBlock block = memory.getBlock(minAddr); + if (block != null) { + // address was inside a block, return it's end + return block.getEnd(); + } + + // address was outside all blocks. try to find a block that contains it and return its end + AddressSpace space = minAddr.getAddressSpace(); + for (MemoryBlock block2 : memory.getBlocks()) { + Address end = block2.getEnd(); + if (end.getAddressSpace().equals(space) && end.compareTo(minAddr) >= 0) { + return end; + } + } + return null; + } + + private static Address findAddressSpaceMax(Memory memory, Address minAddr) { + if (minAddr == null) { + return null; + } + AddressSpace space = minAddr.getAddressSpace(); + Address maxAddr = null; + for (AddressRange range : memory.getAddressRanges()) { + if (!range.getAddressSpace().equals(space)) { + continue; + } + Address rangeEnd = range.getMaxAddress(); + if (rangeEnd.compareTo(minAddr) >= 0 && + (maxAddr == null || rangeEnd.compareTo(maxAddr) >= 0)) { + maxAddr = rangeEnd; + } + } + return maxAddr; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProviderInputStream.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProviderInputStream.java deleted file mode 100644 index 8796e9f6aa..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProviderInputStream.java +++ /dev/null @@ -1,76 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.util.bin; - -import ghidra.program.model.address.Address; -import ghidra.program.model.mem.Memory; - -import java.io.IOException; -import java.io.InputStream; - -class MemoryByteProviderInputStream extends InputStream { - private Memory memory; - private Address startAddress; - private Address address; - - MemoryByteProviderInputStream(Memory memory, Address address) { - this.memory = memory; - this.startAddress = address; - this.address = address; - } - - @Override - public int read() throws IOException { - try { - byte b = memory.getByte(address); - address = address.add(1); - return b & 0xff; - } - catch (Exception e) { - throw new IOException(e.getMessage()); - } - } - - @Override - public int read(byte [] b, int off, int len) throws IOException { - try { - int nRead = memory.getBytes(address, b, off, len); - address = address.add(len); - return nRead; - } - catch (Exception e) { - throw new IOException(e.getMessage()); - } - } - - @Override - public int available() throws IOException { - return (int)memory.getMaxAddress().subtract(address); - } - - @Override - public synchronized void reset() throws IOException { - address = startAddress; - } - - @Override - public void close() throws IOException { - super.close(); - memory = null; - address = null; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java index 9c7d7a46d5..63fa871324 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java @@ -849,7 +849,7 @@ public class DIEAggregate { ? getCompilationUnit().getCompileUnit().getLowPC().longValue() : 0L; - while (reader.getPointerIndex() < reader.length()) { + while (reader.hasNext()) { // Read the beginning and ending addresses Number beginning = DWARFUtil.readAddress(reader, pointerSize); Number ending = DWARFUtil.readAddress(reader, pointerSize); // dwarf end addrs are exclusive diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java index f0d0ee8b70..497fe9a9cd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompilationUnit.java @@ -15,9 +15,10 @@ */ package ghidra.app.util.bin.format.dwarf4; -import java.io.IOException; import java.util.*; +import java.io.IOException; + import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.dwarf4.DWARFUtil.LengthResult; import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; @@ -195,7 +196,7 @@ public class DWARFCompilationUnit { private static boolean isAllZerosUntilEOF(BinaryReader reader) throws IOException { reader = reader.clone(); - while (reader.getPointerIndex() < reader.length()) { + while (reader.hasNext()) { if (reader.readNextByte() != 0) { return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java index 3b27cfa59c..7dc220501f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java @@ -31,8 +31,6 @@ import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException; import ghidra.app.util.bin.format.dwarf4.external.ExternalDebugInfo; import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.*; import ghidra.app.util.opinion.*; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.CategoryPath; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Program; @@ -289,12 +287,8 @@ public class DWARFProgram implements Closeable { return false; } ByteProvider bp = br.getByteProvider(); - if (bp instanceof MemoryByteProvider && bp.length() > 0) { - MemoryByteProvider mbp = (MemoryByteProvider) bp; - Address startAddr = mbp.getAddress(0); - Address endAddr = mbp.getAddress(mbp.length() - 1); - if (program.getRelocationTable().getRelocations( - new AddressSet(startAddr, endAddr)).hasNext()) { + if (bp instanceof MemoryByteProvider mbp && !mbp.isEmpty()) { + if (program.getRelocationTable().getRelocations(mbp.getAddressSet()).hasNext()) { return true; } } @@ -584,7 +578,7 @@ public class DWARFProgram implements Closeable { BinaryReader br = debugInfoBR; br.setPointerIndex(0); - while (br.getPointerIndex() < br.getByteProvider().length()) { + while (br.hasNext()) { monitor.checkCanceled(); monitor.setMessage("Bootstrapping DWARF Compilation Unit #" + compUnits.size()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macos/rm/ResourceMap.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macos/rm/ResourceMap.java index afbffa40e3..03eea1a149 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macos/rm/ResourceMap.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macos/rm/ResourceMap.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,14 @@ */ package ghidra.app.util.bin.format.macos.rm; +import java.util.*; + +import java.io.IOException; + import ghidra.app.util.bin.*; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; -import java.io.IOException; -import java.util.*; - public class ResourceMap implements StructConverter { private ResourceHeader copy; private int handleToNextResourceMap; @@ -70,7 +70,7 @@ public class ResourceMap implements StructConverter { private void parseResourceNameList(BinaryReader reader) throws IOException { long start = _mapStartIndex + resourceNameListOffset; reader.setPointerIndex(_mapStartIndex + resourceNameListOffset); - while (reader.getPointerIndex() < reader.length()) { + while (reader.hasNext()) { long offset = reader.getPointerIndex(); int length = reader.readNextByte() & 0xff; String name = reader.readNextAsciiString(length); @@ -125,6 +125,7 @@ public class ResourceMap implements StructConverter { return _mapStartIndex; } + @Override public DataType toDataType() throws DuplicateNameException, IOException { return StructConverterUtil.toDataType(ResourceMap.class); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/MemoryByteProviderTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/MemoryByteProviderTest.java new file mode 100644 index 0000000000..3d5fef9d96 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/MemoryByteProviderTest.java @@ -0,0 +1,240 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin; + +import static org.junit.Assert.assertEquals; + +import java.io.EOFException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.junit.*; + +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.util.task.TaskMonitor; + +public class MemoryByteProviderTest extends AbstractGhidraHeadedIntegrationTest { + protected ProgramDB program; + protected AddressSpace space; + protected Memory memory; + protected TaskMonitor monitor = TaskMonitor.DUMMY; + private ProgramBuilder builder; + + @Before + public void setUp() throws Exception { + builder = new ProgramBuilder(testName.getMethodName(), ProgramBuilder._X64, this); + program = builder.getProgram(); + memory = program.getMemory(); + space = program.getAddressFactory().getDefaultAddressSpace(); + } + + protected Address addr(long l) { + return space.getAddress(l); + } + + private void setBlockStartEndBytes(MemoryBlock memblk, String start, String end) + throws Exception { + builder.setBytes(memblk.getStart().toString(true), + start.getBytes(StandardCharsets.US_ASCII)); + byte[] endBytes = end.getBytes(StandardCharsets.US_ASCII); + builder.setBytes(memblk.getEnd().subtract(endBytes.length - 1).toString(true), endBytes); + } + + private MemoryBlock addRam0() throws Exception { + MemoryBlock memblk = builder.createMemory(space.getName(), "0", 0x50); + setBlockStartEndBytes(memblk, "startram0\0", "endram0\0"); + return memblk; + } + + private MemoryBlock addRam1() throws Exception { + MemoryBlock memblk = builder.createMemory(space.getName(), "50", 0x50); + setBlockStartEndBytes(memblk, "startram1\0", "endram1\0"); + return memblk; + } + + private MemoryBlock addRam2() throws Exception { + MemoryBlock memblk = builder.createMemory(space.getName(), "a0", 0x50); + setBlockStartEndBytes(memblk, "startram2\0", "endram2\0"); + return memblk; + } + + private MemoryBlock addRamEnd() throws Exception { + MemoryBlock memblk = builder.createMemory(space.getName(), "ffffffffffffffb0", 0x50); + setBlockStartEndBytes(memblk, "highstart\0", "highend\0"); + return memblk; + } + + @After + public void tearDown() throws Exception { + builder.dispose(); + } + + @Test + public void testNoMemory() throws IOException { + MemoryByteProvider mbp = MemoryByteProvider.createProgramHeaderByteProvider(program, false); + assertEquals(0, mbp.length()); + + mbp = MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); + assertEquals(0, mbp.length()); + } + + @Test + public void testCreateProgramHeader_Offset() throws Exception { + addRam1(); + MemoryByteProvider mbp = MemoryByteProvider.createProgramHeaderByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals(0x50, mbp.length()); + assertEquals("startram1", reader.readAsciiString(0)); + } + + @Test + public void testCreateDefaultAddressSpace_Offset() throws Exception { + addRam1(); + MemoryByteProvider mbp = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals(0x50 + 0x50, mbp.length()); + assertEquals("startram1", reader.readAsciiString(0x50)); + } + + @Test + public void testMinAddrNotInBlock() throws Exception { + addRam1(); + MemoryByteProvider mbp = new MemoryByteProvider(memory, addr(1)); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals(0x50 + 0x50 - 1, mbp.length()); + assertEquals("startram1", reader.readAsciiString(0x50 - 1)); + } + + @Test + public void testMinAddrInBlock() throws Exception { + addRam1(); + MemoryByteProvider mbp = new MemoryByteProvider(memory, addr(0x51)); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals(0x50 - 1, mbp.length()); + assertEquals(/* missing 's' */ "tartram1", reader.readAsciiString(0)); + } + + @Test + public void testMinAddrAfterBlock() throws Exception { + addRam1(); + MemoryByteProvider mbp = new MemoryByteProvider(memory, addr(0x5000)); + + assertEquals(0, mbp.length()); + } + + @Test + public void testMultiblock_adjacent() throws Exception { + MemoryBlock blk1 = addRam1(); + MemoryBlock blk2 = addRam2(); + setBlockStartEndBytes(blk1, "blah", "aaaa"); + setBlockStartEndBytes(blk2, "bbbb", "blah"); + MemoryByteProvider mbp = + MemoryByteProvider.createProgramHeaderByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals(blk2.getEnd().getOffset() - blk1.getStart().getOffset() + 1, mbp.length()); + assertEquals(0x61616161, reader.readInt(0x50 - 4)); + assertEquals(0x62626262, reader.readInt(0x50)); + assertEquals(0x62626161, reader.readInt(0x50 - 2)); + } + + @Test + public void testMultiblockLength_disjoint() throws Exception { + MemoryBlock blk0 = addRam0(); + MemoryBlock blk2 = addRam2(); + MemoryByteProvider mbp = + MemoryByteProvider.createProgramHeaderByteProvider(program, false); + + assertEquals(blk2.getEnd().getOffset() - blk0.getStart().getOffset() + 1, mbp.length()); + } + + @Test + public void testLength_block_at_end_of_64bits() throws Exception { + MemoryBlock blk = addRamEnd(); + MemoryByteProvider mbp = + MemoryByteProvider.createProgramHeaderByteProvider(program, false); + + assertEquals(blk.getEnd().getOffset() - blk.getStart().getOffset() + 1, mbp.length()); + } + + @Test + public void testLength_all64bits() throws Exception { + addRam0(); + addRamEnd(); + MemoryByteProvider mbp = + MemoryByteProvider.createProgramHeaderByteProvider(program, false); + + assertEquals(Long.MAX_VALUE, mbp.length()); + } + + @Test + public void testFull64bitAddressSpace() throws Exception { + addRamEnd(); + MemoryByteProvider mbp = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals(Long.MAX_VALUE, mbp.length()); + assertEquals("highstart", reader.readAsciiString(0xffffffffffffffb0L)); + assertEquals("end", reader.readAsciiString(0xfffffffffffffffcL)); + } + + @Test(expected = EOFException.class) + public void testFull64bitAddressSpace_fail_when_wrap_string() throws Exception { + MemoryBlock blk = addRamEnd(); + setBlockStartEndBytes(blk, "blah", "fail"); + MemoryByteProvider mbp = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + reader.readAsciiString(0xfffffffffffffffcL); + } + + @Test(expected = EOFException.class) + public void testFull64bitAddressSpace_fail_when_wrap_int() throws Exception { + addRamEnd(); + MemoryByteProvider mbp = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + reader.readInt(0xfffffffffffffffeL); + } + + @Test(expected = EOFException.class) + public void testEOFExceptionWhenCrossingMemBlockBoundary() throws Exception { + MemoryBlock blk1 = addRam1(); + addRamEnd(); + MemoryByteProvider mbp = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); + BinaryReader reader = new BinaryReader(mbp, true); + + assertEquals("endram1", reader.readAsciiString(blk1.getEnd().getOffset() - 7)); + + setBlockStartEndBytes(blk1, "blah", "fail"); + reader.readAsciiString(blk1.getEnd().getOffset() - 3); + } +} diff --git a/Ghidra/Features/FileFormats/ghidra_scripts/BTreeAnnotationScript.java b/Ghidra/Features/FileFormats/ghidra_scripts/BTreeAnnotationScript.java index 2a5f840340..cbfe6a1452 100644 --- a/Ghidra/Features/FileFormats/ghidra_scripts/BTreeAnnotationScript.java +++ b/Ghidra/Features/FileFormats/ghidra_scripts/BTreeAnnotationScript.java @@ -32,9 +32,8 @@ public class BTreeAnnotationScript extends GhidraScript { @Override public void run() throws Exception { - Address address = currentProgram.getMinAddress(); - - ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), address); + ByteProvider provider = + MemoryByteProvider.createProgramHeaderByteProvider(currentProgram, false); BinaryReader reader = new BinaryReader(provider, false); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootimg/BootImageAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootimg/BootImageAnalyzer.java index 628dee3b6a..970ad23c08 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootimg/BootImageAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootimg/BootImageAnalyzer.java @@ -19,9 +19,7 @@ import java.io.IOException; import ghidra.app.plugin.core.analysis.AnalysisWorker; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.MemoryByteProvider; +import ghidra.app.util.bin.*; import ghidra.app.util.importer.MessageLog; import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.program.model.address.Address; @@ -30,9 +28,7 @@ import ghidra.program.model.data.DataType; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.program.model.util.CodeUnitInsertionException; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.NotFoundException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWorker { @@ -89,9 +85,7 @@ public class BootImageAnalyzer extends FileFormatAnalyzer implements AnalysisWor TaskMonitor monitor) throws Exception, CancelledException { - Address address = program.getMinAddress(); - - ByteProvider provider = new MemoryByteProvider(program.getMemory(), address); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); if (BootImageUtil.isBootImage(program)) { diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootldr/AndroidBootLoaderAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootldr/AndroidBootLoaderAnalyzer.java index 4bff86449c..5091f17557 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootldr/AndroidBootLoaderAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/bootldr/AndroidBootLoaderAnalyzer.java @@ -58,7 +58,8 @@ public class AndroidBootLoaderAnalyzer extends AbstractAnalyzer { AddressSpace addressSpace = program.getAddressFactory().getDefaultAddressSpace(); Address headerAddress = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), headerAddress); + + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); try { AndroidBootLoaderHeader header = new AndroidBootLoaderHeader(reader); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexCondenseFillerBytesAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexCondenseFillerBytesAnalyzer.java index 154b46c608..5531535db7 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexCondenseFillerBytesAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexCondenseFillerBytesAnalyzer.java @@ -74,8 +74,7 @@ public class DexCondenseFillerBytesAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexExceptionHandlersAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexExceptionHandlersAnalyzer.java index 15ce673ca4..a7f824ef19 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexExceptionHandlersAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexExceptionHandlersAnalyzer.java @@ -49,8 +49,7 @@ public class DexExceptionHandlersAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexHeaderFormatAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexHeaderFormatAnalyzer.java index 9d3369ec8d..98fc84fc08 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexHeaderFormatAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexHeaderFormatAnalyzer.java @@ -65,8 +65,7 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupDataAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupDataAnalyzer.java index 676e6e39a8..7d1d2322f5 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupDataAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupDataAnalyzer.java @@ -69,8 +69,7 @@ public class DexMarkupDataAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupInstructionsAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupInstructionsAnalyzer.java index 466ddc9095..b9b0cc6e68 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupInstructionsAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupInstructionsAnalyzer.java @@ -45,8 +45,7 @@ public class DexMarkupInstructionsAnalyzer extends FileFormatAnalyzer { DexHeader header = analysisState.getHeader(); // Set-up reader for fill_array_data - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); Listing listing = program.getListing(); @@ -145,8 +144,7 @@ public class DexMarkupInstructionsAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupSwitchTableAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupSwitchTableAnalyzer.java index bb6b77436c..9423375089 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupSwitchTableAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/dex/analyzer/DexMarkupSwitchTableAnalyzer.java @@ -18,29 +18,19 @@ package ghidra.file.formats.android.dex.analyzer; import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.app.services.AnalysisPriority; import ghidra.app.services.AnalyzerType; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.MemoryByteProvider; +import ghidra.app.util.bin.*; import ghidra.app.util.importer.MessageLog; import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.file.formats.android.cdex.CDexConstants; -import ghidra.file.formats.android.dex.format.DexConstants; -import ghidra.file.formats.android.dex.format.PackedSwitchPayload; -import ghidra.file.formats.android.dex.format.SparseSwitchPayload; +import ghidra.file.formats.android.dex.format.*; import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.data.DataType; -import ghidra.program.model.listing.CodeUnit; -import ghidra.program.model.listing.Instruction; -import ghidra.program.model.listing.InstructionIterator; -import ghidra.program.model.listing.Listing; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.scalar.Scalar; -import ghidra.program.model.symbol.Namespace; -import ghidra.program.model.symbol.RefType; -import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.*; import ghidra.util.task.TaskMonitor; public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer { @@ -51,8 +41,7 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer { monitor.setMaximum(set == null ? program.getMemory().getSize() : set.getNumAddresses()); monitor.setProgress(0); - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); Listing listing = program.getListing(); @@ -126,8 +115,7 @@ public class DexMarkupSwitchTableAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return DexConstants.isDexFile(provider) || CDexConstants.isCDEX(program); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/fbpk/FBPK_Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/fbpk/FBPK_Analyzer.java index 683324676f..46364c84a6 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/fbpk/FBPK_Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/fbpk/FBPK_Analyzer.java @@ -15,9 +15,7 @@ */ package ghidra.file.formats.android.fbpk; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.MemoryByteProvider; +import ghidra.app.util.bin.*; import ghidra.app.util.importer.MessageLog; import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.program.model.address.Address; @@ -59,7 +57,8 @@ public class FBPK_Analyzer extends FileFormatAnalyzer { throws Exception { Address headerAddress = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), headerAddress); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); + BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); try { FBPK header = FBPK_Factory.getFBPK(reader); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/lz4/LZ4ArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/lz4/LZ4ArchiveFileSystem.java index a286f19b88..8c8952e255 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/lz4/LZ4ArchiveFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/lz4/LZ4ArchiveFileSystem.java @@ -15,9 +15,10 @@ */ package ghidra.file.formats.android.lz4; -import java.io.*; import java.util.*; +import java.io.*; + import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorInputStream; import ghidra.app.util.bin.BinaryReader; @@ -89,7 +90,7 @@ public class LZ4ArchiveFileSystem extends GFileSystemBase { upwtm.setMessage("Decompressing LZ4 archive..."); upwtm.setProgress(0); - while (reader.getPointerIndex() < reader.length()) { + while (reader.hasNext()) { monitor.checkCanceled(); int compressedChunkSize = reader.readNextInt(); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/oat/bundle/FullOatBundle.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/oat/bundle/FullOatBundle.java index cb31d3073e..b15601f7e4 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/oat/bundle/FullOatBundle.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/oat/bundle/FullOatBundle.java @@ -15,9 +15,10 @@ */ package ghidra.file.formats.android.oat.bundle; -import java.io.IOException; import java.util.*; +import java.io.IOException; + import org.apache.commons.io.FilenameUtils; import ghidra.app.util.bin.*; @@ -89,6 +90,7 @@ public class FullOatBundle implements OatBundle { return null;//could NOT find matching dex header, probably not imported yet } + @Override public ArtHeader getArtHeader() { return artHeader; } @@ -98,10 +100,12 @@ public class FullOatBundle implements OatBundle { return oatHeader; } + @Override public List getDexHeaders() { return dexHeaders; } + @Override public VdexHeader getVdexHeader() { return vdexHeader; } @@ -208,7 +212,7 @@ public class FullOatBundle implements OatBundle { try { program = (Program) child.getDomainObject(this, true, true, monitor); ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + MemoryByteProvider.createProgramHeaderByteProvider(program, false); return makeHeader(type, programName, provider, monitor); } catch (Exception e) { diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/odex/OdexHeaderFormatAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/odex/OdexHeaderFormatAnalyzer.java index c45516810c..2e8a773ac5 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/odex/OdexHeaderFormatAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/odex/OdexHeaderFormatAnalyzer.java @@ -17,16 +17,12 @@ package ghidra.file.formats.android.odex; import ghidra.app.services.AnalysisPriority; import ghidra.app.services.AnalyzerType; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.MemoryByteProvider; +import ghidra.app.util.bin.*; import ghidra.app.util.importer.MessageLog; import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.data.DWordDataType; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.StringDataType; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; @@ -51,8 +47,7 @@ public class OdexHeaderFormatAnalyzer extends FileFormatAnalyzer { block.setWrite(false); block.setExecute(false); - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); OdexHeader header = new OdexHeader(reader); @@ -80,8 +75,7 @@ public class OdexHeaderFormatAnalyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); return OdexConstants.isOdexFile(provider); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/vdex/VdexConstants.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/vdex/VdexConstants.java index 6874a245b6..c17369326b 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/vdex/VdexConstants.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/vdex/VdexConstants.java @@ -15,8 +15,6 @@ */ package ghidra.file.formats.android.vdex; -import java.io.IOException; - import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.MemoryByteProvider; import ghidra.program.model.address.Address; @@ -112,7 +110,7 @@ public final class VdexConstants { for (MemoryBlock block : program.getMemory().getBlocks()) { try (ByteProvider provider = - new MemoryByteProvider(program.getMemory(), block.getStart())) { + MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), block)) { String magic = new String(provider.readBytes(0, VdexConstants.MAGIC.length())); if (VdexConstants.MAGIC.equals(magic)) { return true; @@ -127,30 +125,21 @@ public final class VdexConstants { } public final static Address findVDEX(Program program) { - try { - if (program != null) { - for (MemoryBlock block : program.getMemory().getBlocks()) { - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), block.getStart()); - try { - String magic = - new String(provider.readBytes(0, VdexConstants.MAGIC.length())); - if (VdexConstants.MAGIC.equals(magic)) { - return block.getStart(); - } - } - catch (Exception e) { - //ignore - } - finally { - provider.close(); + if (program != null) { + for (MemoryBlock block : program.getMemory().getBlocks()) { + try (ByteProvider provider = MemoryByteProvider + .createMemoryBlockByteProvider(program.getMemory(), block)) { + String magic = + new String(provider.readBytes(0, VdexConstants.MAGIC.length())); + if (VdexConstants.MAGIC.equals(magic)) { + return block.getStart(); } } + catch (Exception e) { + //ignore + } } } - catch (IOException e) { - //ignore - } return null; } } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4Analyzer.java index b8ba92b2a2..54ead1ac16 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4Analyzer.java @@ -49,7 +49,8 @@ public class Ext4Analyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze(Program program) { - ByteProvider provider = new MemoryByteProvider( program.getMemory(), program.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); int start = getSuperBlockStart(reader); if( start == -1 ) { @@ -77,7 +78,8 @@ public class Ext4Analyzer extends FileFormatAnalyzer { @Override public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws Exception { - ByteProvider provider = new MemoryByteProvider( program.getMemory(), program.getAddressFactory().getDefaultAddressSpace()); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); int start = getSuperBlockStart(reader); int groupStart = 0; diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/NewExt4Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/NewExt4Analyzer.java index f8920df620..c49435b12c 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/NewExt4Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/NewExt4Analyzer.java @@ -15,23 +15,20 @@ */ package ghidra.file.formats.ext4; -import java.io.IOException; import java.util.List; +import java.io.IOException; + import ghidra.app.cmd.comments.SetCommentCmd; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.services.ProgramManager; -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.MemoryByteProvider; +import ghidra.app.util.bin.*; import ghidra.app.util.importer.MessageLog; import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.data.DataType; -import ghidra.program.model.listing.CodeUnit; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.symbol.SourceType; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.exception.DuplicateNameException; @@ -63,7 +60,8 @@ public class NewExt4Analyzer extends FileFormatAnalyzer { @Override public boolean canAnalyze( Program program ) { - ByteProvider provider = new MemoryByteProvider( program.getMemory( ), program.getAddressFactory( ).getDefaultAddressSpace( ) ); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader( provider, true ); int start = getSuperBlockStart( reader ); if ( start == -1 ) { diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipUtil.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipUtil.java index 593695aab2..94fc58b7fe 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipUtil.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipUtil.java @@ -15,17 +15,17 @@ */ package ghidra.file.formats.gzip; +import java.util.Arrays; + import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.MemoryByteProvider; import ghidra.program.model.listing.Program; -import java.util.Arrays; - public class GZipUtil { public final static boolean isGZip( Program program ) { - ByteProvider provider = new MemoryByteProvider( program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace() ); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, true); return isGZip( provider ); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/apple8900/Apple8900Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/apple8900/Apple8900Analyzer.java index 49dc37f2a0..55029401a0 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/apple8900/Apple8900Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/apple8900/Apple8900Analyzer.java @@ -27,33 +27,38 @@ import ghidra.util.task.TaskMonitor; public class Apple8900Analyzer extends FileFormatAnalyzer { + @Override public boolean canAnalyze(Program program) { return Apple8900Util.is8900(program); } + @Override public boolean getDefaultEnablement(Program program) { return Apple8900Util.is8900(program); } + @Override public String getDescription() { return "Annotates an Apple 8900 file."; } + @Override public String getName() { return "Apple 8900 Annotation"; } + @Override public boolean isPrototype() { return true; } + @Override public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws Exception { monitor.setMessage("Processing Apple 8900 header..."); ByteProvider provider = - new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); Apple8900Header header = new Apple8900Header(reader); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgAnalyzer.java index 28dbaffc39..581402e068 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgAnalyzer.java @@ -15,6 +15,8 @@ */ package ghidra.file.formats.ios.dmg; +import java.util.Arrays; + import ghidra.app.plugin.core.analysis.AnalysisWorker; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.util.bin.*; @@ -28,8 +30,6 @@ import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import java.util.Arrays; - public class DmgAnalyzer extends FileFormatAnalyzer implements AnalysisWorker { @Override @@ -44,7 +44,7 @@ public class DmgAnalyzer extends FileFormatAnalyzer implements AnalysisWorker { throws Exception, CancelledException { Address address = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), address); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, false); DmgHeader header = new DmgHeaderV2(reader); @@ -67,22 +67,27 @@ public class DmgAnalyzer extends FileFormatAnalyzer implements AnalysisWorker { return getName(); } + @Override public boolean canAnalyze(Program program) { return DmgUtil.isDMG(program); } + @Override public boolean getDefaultEnablement(Program program) { return DmgUtil.isDMG(program); } + @Override public String getDescription() { return "Annotates an DMG file."; } + @Override public String getName() { return "DMG"; } + @Override public boolean isPrototype() { return true; } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheAnalyzer.java index 59c150d1b2..d2c19679a5 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheAnalyzer.java @@ -39,8 +39,7 @@ public class DyldCacheAnalyzer extends FileFormatAnalyzer { throws Exception { Address headerAddress = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), headerAddress); - + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); DyldArchitecture architecture = DyldArchitecture.getArchitecture(provider); if (architecture == null) { log.appendMsg("Invalid DYLD cache file."); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ibootim/iBootImAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ibootim/iBootImAnalyzer.java index f56aa6be48..9c5580eaf2 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ibootim/iBootImAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ibootim/iBootImAnalyzer.java @@ -43,8 +43,7 @@ public class iBootImAnalyzer extends FileFormatAnalyzer implements AnalysisWorke throws Exception, CancelledException { Address address = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), address); - + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); iBootImHeader header = new iBootImHeader(provider); if (!header.getSignature().equals(iBootImConstants.SIGNATURE)) { @@ -69,22 +68,27 @@ public class iBootImAnalyzer extends FileFormatAnalyzer implements AnalysisWorke return getName(); } + @Override public boolean canAnalyze(Program program) { return iBootImUtil.isiBootIm(program); } + @Override public boolean getDefaultEnablement(Program program) { return iBootImUtil.isiBootIm(program); } + @Override public String getDescription() { return "Annotates an iBoot Image (iBootIm) file."; } + @Override public String getName() { return "iBoot Image (iBootIm) Annotation"; } + @Override public boolean isPrototype() { return true; } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2Analyzer.java index 143b5fa595..cabad6d36e 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2Analyzer.java @@ -27,6 +27,7 @@ import ghidra.util.task.TaskMonitor; public class Img2Analyzer extends FileFormatAnalyzer { + @Override public boolean canAnalyze(Program program) { try { return Img2Util.isIMG2(program); @@ -37,28 +38,32 @@ public class Img2Analyzer extends FileFormatAnalyzer { return false; } + @Override public boolean getDefaultEnablement(Program program) { return Img2Util.isIMG2(program); } + @Override public String getDescription() { return "Annotates an IMG2 file."; } + @Override public String getName() { return "IMG2 Annotation"; } + @Override public boolean isPrototype() { return true; } + @Override public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws Exception { ByteProvider provider = - new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); Img2 header = new Img2(reader); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3Analyzer.java index ff8e12129d..e14977ce2a 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3Analyzer.java @@ -15,6 +15,8 @@ */ package ghidra.file.formats.ios.img3; +import java.util.List; + import ghidra.app.plugin.core.analysis.AnalysisWorker; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.util.bin.*; @@ -28,30 +30,34 @@ import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import java.util.List; - public class Img3Analyzer extends FileFormatAnalyzer implements AnalysisWorker { + @Override public boolean canAnalyze(Program program) { return Img3Util.isIMG3(program); } + @Override public boolean getDefaultEnablement(Program program) { return Img3Util.isIMG3(program); } + @Override public String getDescription() { return "Annotates an IMG3 file."; } + @Override public String getName() { return "IMG3 Annotation"; } + @Override public boolean isPrototype() { return true; } + @Override public boolean analyze(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws Exception { AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program); @@ -63,7 +69,7 @@ public class Img3Analyzer extends FileFormatAnalyzer implements AnalysisWorker { throws Exception, CancelledException { Address address = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), address); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); Img3 header = new Img3(reader); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Analyzer.java deleted file mode 100644 index 5c52f833cf..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Analyzer.java +++ /dev/null @@ -1,488 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import java.io.IOException; -import java.util.*; - -import ghidra.app.cmd.comments.SetCommentCmd; -import ghidra.app.cmd.data.CreateDataCmd; -import ghidra.app.cmd.data.CreateStringCmd; -import ghidra.app.services.AbstractAnalyzer; -import ghidra.app.services.AnalyzerType; -import ghidra.app.util.bin.*; -import ghidra.app.util.importer.MessageLog; -import ghidra.app.util.opinion.BinaryLoader; -import ghidra.framework.options.Options; -import ghidra.program.model.address.*; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.*; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.util.Msg; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.task.TaskMonitor; - -public class ISO9660Analyzer extends AbstractAnalyzer { - - private enum Offset { - Offset1, //0x8001 - Offset2, //0x8801 - Offset3, //0x9001 - NotFound - } - - public ISO9660Analyzer() { - super("ISO9660 File Format Annotation", "Annotates an ISO9660 File Format", - AnalyzerType.BYTE_ANALYZER); - super.setPrototype(); - - } - - @Override - public boolean canAnalyze(Program program) { - - Offset result = checkSignatures(program); - if (result.equals(Offset.NotFound)) { - return false; - } - return true; - - } - - private Offset checkSignatures(Program program) { - int magicLen = ISO9660Constants.MAGIC_BYTES.length; - byte[] signatureArray = new byte[magicLen]; - - try { - Options options = program.getOptions("Program Information"); - String format = options.getString("Executable Format", null); - if (!BinaryLoader.BINARY_NAME.equals(format)) { - return Offset.NotFound; - } - - MemoryBlock[] blocks = program.getMemory().getBlocks(); - if (blocks.length != 1) { - return Offset.NotFound; - } - - AddressSpace addressSpace = program.getAddressFactory().getDefaultAddressSpace(); - if (!(blocks[0].getStart().getAddressSpace().equals(addressSpace))) { - return Offset.NotFound; - } - - long blockSize = blocks[0].getSize(); - - //block must start at zero - if (blocks[0].getStart().getOffset() != 0L) { - return Offset.NotFound; - } - - //is the block initialized - if (!blocks[0].isInitialized()) { - return Offset.NotFound; - } - - ByteProvider provider = new MemoryByteProvider(program.getMemory(), addressSpace); - BinaryReader reader = new BinaryReader(provider, true); - - //Make sure that the current programs max offset is at least big enough to check - //for the ISO's max address location of a signature - if (blockSize < ISO9660Constants.MIN_ISO_LENGTH1) { - return Offset.NotFound; - } - - //Check first possible signature location - reader.setPointerIndex(ISO9660Constants.SIGNATURE_OFFSET1_0x8001); - signatureArray = reader.readNextByteArray(magicLen); - if (Arrays.equals(signatureArray, ISO9660Constants.MAGIC_BYTES)) { - //Where to start the reader during mark up - return Offset.Offset1; - } - - if (blockSize < ISO9660Constants.MIN_ISO_LENGTH2) { - return Offset.NotFound; - } - - //Check second possible signature location - reader.setPointerIndex(ISO9660Constants.SIGNATURE_OFFSET2_0x8801); - signatureArray = reader.readNextByteArray(magicLen); - if (Arrays.equals(signatureArray, ISO9660Constants.MAGIC_BYTES)) { - //Where to start the reader during mark up - return Offset.Offset2; - } - - if (blockSize < ISO9660Constants.MIN_ISO_LENGTH3) { - return Offset.NotFound; - } - //Check third possible signature location - reader.setPointerIndex(ISO9660Constants.SIGNATURE_OFFSET3_0x9001); - signatureArray = reader.readNextByteArray(magicLen); - if (Arrays.equals(signatureArray, ISO9660Constants.MAGIC_BYTES)) { - //Where to start the reader during mark up - return Offset.Offset3; - } - - } - catch (Exception e) { - Msg.error(this, "Error when checking for ISO9660 file signatures", e); - } - - //Signature is not found at any of the three possible address locations - return Offset.NotFound; - } - - @Override - public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) - throws CancelledException { - - ByteProvider provider = new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); - BinaryReader reader = new BinaryReader(provider, true); - try { - - Offset signatureOffset = checkSignatures(program); - setPointerOffset(signatureOffset, reader); - - monitor.setMessage("Processing ISO9660 Header"); - - //Get the full header (contains all volume descriptors) - ISO9660Header isoHeader = new ISO9660Header(reader); - - //Get the list of volumes from the header - List volumes = isoHeader.getVolumeDescriptorSet(); - - //Set the overall plate comment at the top of this file - setPlateComment(program, toAddress(program, 0), isoHeader.toString()); - - //Create a new module for the volume descriptor fragments - ProgramModule descriptorModule = - program.getListing().getDefaultRootModule().createModule("Volume Descriptors"); - - //For each volume, set the volumes plate comment and data at the address it exists - setDescriptorData(program, volumes, descriptorModule); - - processPathTables(isoHeader, reader, program); - - //Create an alignment over the null characters from start to the first volume - int offset = getOffsetValue(signatureOffset); - program.getListing().createData(toAddress(program, 0), new AlignmentDataType(), - offset - 1); - - ISO9660VolumeDescriptor pvd = isoHeader.getPrimaryVolumeDescriptor(); - ISO9660Directory entryDir = isoHeader.getPrimaryDirectory(); - - int logicalBlockSize = pvd.getLogicalBlockSizeLE(); - - List dirList = - createDirectoryList(reader, entryDir, logicalBlockSize); - - createDirectories(reader, program, dirList, logicalBlockSize); - - } - catch (Exception e) { - log.appendException(e); - return false; - } - return true; - - } - - private void setPointerOffset(Offset offset, BinaryReader reader) { - if (offset.equals(Offset.Offset1)) { - reader.setPointerIndex(ISO9660Constants.SIGNATURE_OFFSET1_0x8001 - 1); - } - else if (offset.equals(Offset.Offset2)) { - reader.setPointerIndex(ISO9660Constants.SIGNATURE_OFFSET2_0x8801 - 1); - } - else { - reader.setPointerIndex(ISO9660Constants.SIGNATURE_OFFSET3_0x9001 - 1); - } - } - - private int getOffsetValue(Offset offsetEnum) { - - if (offsetEnum.equals(Offset.Offset1)) { - return ISO9660Constants.SIGNATURE_OFFSET1_0x8001; - } - else if (offsetEnum.equals(Offset.Offset2)) { - return ISO9660Constants.SIGNATURE_OFFSET2_0x8801; - } - else { - return ISO9660Constants.SIGNATURE_OFFSET3_0x9001; - } - } - - private void setDescriptorData(Program program, List volumes, - ProgramModule descriptorModule) throws DuplicateNameException, IOException, Exception { - - for (ISO9660BaseVolume descriptor : volumes) { - - long volumeIndex = descriptor.getVolumeIndex(); - DataType descriptorDataType = descriptor.toDataType(); - Address volumeAddress = toAddress(program, volumeIndex); - Data descriptorData = - createData(program, toAddress(program, volumeIndex), descriptorDataType); - - setPlateComment(program, volumeAddress, descriptor.toString()); - - //Add fragment to module - createFragment(program, descriptorModule, descriptorDataType.getName(), - descriptorData.getMinAddress(), descriptorData.getMaxAddress().next()); - } - } - - /* - * Process the normal and supplementary path tables in the binary - */ - private void processPathTables(ISO9660Header isoHeader, BinaryReader reader, Program program) - throws DuplicateNameException { - - //Create module to add path table fragments to - ProgramModule pathTableModule = - program.getListing().getDefaultRootModule().createModule("Path Tables"); - - try { - - //Get the tables which hold the index and size pairs of path tables - //for little-endian values - HashMap typeLTable = isoHeader.getTypeLIndexSizeTable(); - createPathTableData(reader, program, pathTableModule, typeLTable, true); - - //Get the tables which hold the index and size pairs of path tables - //for big-endian values - HashMap typeMTable = isoHeader.getTypeMIndexSizeTable(); - createPathTableData(reader, program, pathTableModule, typeMTable, false); - - //Get the tables which hold the index and size of supplementary path tables - //for little-endian values - HashMap supplTypeLTable = isoHeader.getSupplTypeLIndexSizeTable(); - createPathTableData(reader, program, pathTableModule, supplTypeLTable, true); - - //Get the tables which hold the index and size of supplementary path tables - //for big-endian values - HashMap supplTypeMTable = isoHeader.getSupplTypeMIndexSizeTable(); - createPathTableData(reader, program, pathTableModule, supplTypeMTable, false); - } - catch (Exception e) { - e.printStackTrace(); - } - } - - /* - * From a given parent directory create each child directory - * under that parent directory and add them to a list - */ - private List createDirectoryList(BinaryReader reader, - ISO9660Directory parentDir, long blockSize) throws IOException { - - List directoryList = new ArrayList<>(); - ISO9660Directory childDir = null; - - //Get location from parent into child directory - long dirIndex = parentDir.getLocationOfExtentLE() * blockSize; - long endIndex = dirIndex + parentDir.getDataLengthLE(); - - //while there is still more data in the current directory level - while (dirIndex < endIndex) { - reader.setPointerIndex(dirIndex); - - //If the next byte is not zero then create the directory - if (reader.peekNextByte() != 0) { - childDir = new ISO9660Directory(reader, parentDir); - directoryList.add(childDir); - } - - //Otherwise there is a gap in the data so keep looking forward - //while still under the end index and create directory when data is - //reached - else { - while (reader.peekNextByte() == 0) { - //keep reading if all zeros until non zero is met or - //end index reached - if (reader.getPointerIndex() < endIndex) { - reader.readNextByte(); - } - else { - break; - } - } - - //Create the data once the reader finds the next position - //and not reached end index - if (reader.getPointerIndex() < endIndex) { - childDir = new ISO9660Directory(reader, parentDir); - dirIndex = childDir.getVolumeIndex(); - directoryList.add(childDir); - } - } - - dirIndex += childDir.getDirectoryRecordLength(); - } - - return directoryList; - } - - /* - * Recurses though each level of a directory structure - * in a depth-first manner - * and creates each directory also marking them in the binary - */ - private void createDirectories(BinaryReader reader, Program program, - List directoryList, long blockSize) - throws DuplicateNameException, Exception { - - Address volumeAddress; - - //If the directory size is over two then there are actual - //new directories in that level. The first two are always - //the 'self' directory and the parent directory - if (directoryList.size() > 2) { - ISO9660Directory selfDir = null; - ISO9660Directory parentDir = null; - - // The 'self' describing directory entry - selfDir = directoryList.remove(0); - volumeAddress = toAddress(program, selfDir.getVolumeIndex()); - createDataAndPlateComment(program, selfDir, volumeAddress); - - // The parent directory - parentDir = directoryList.remove(0); - volumeAddress = toAddress(program, parentDir.getVolumeIndex()); - createDataAndPlateComment(program, parentDir, volumeAddress); - - //For everything else not a self or parent directory - for (ISO9660Directory dir : directoryList) { - - //If this directory is not pointing to a file - //Create the directory data - if (selfDir.isDirectoryFlagSet()) { - volumeAddress = toAddress(program, dir.getVolumeIndex()); - setPlateComment(program, volumeAddress, dir.toString()); - DataType volumeDataType = dir.toDataType(); - createData(program, volumeAddress, volumeDataType); - - //If the directory is a new level of directories - //recurse down into the next level - if (dir.isDirectoryFlagSet()) { - List dirs; - dirs = createDirectoryList(reader, dir, blockSize); - createDirectories(reader, program, dirs, blockSize); - } - } - } - } - - return; - } - - private void createDataAndPlateComment(Program program, ISO9660Directory dir, - Address volumeAddress) throws DuplicateNameException, IOException, Exception { - - setPlateComment(program, volumeAddress, dir.toString()); - createData(program, volumeAddress, dir.toDataType()); - } - - /* - * Creates path table plate comments and lays mark up data down on the binary - */ - private void createPathTableData(BinaryReader reader, Program program, ProgramModule module, - HashMap pathTableMap, boolean littleEndian) throws Exception { - - //Enumeration over the indexes of the path tables in the table - - Set pathTableIndexes = pathTableMap.keySet(); - Iterator pathIter = pathTableIndexes.iterator(); - - while (pathIter.hasNext()) { - - //Index of current path table - int pathTableIndex = pathIter.next(); - - //Logical block size of current path table - short logicalBlockSize = pathTableMap.get(pathTableIndex); - - //Calculate address from logical index - int pathAddress = logicalBlockSize * pathTableIndex; - - //Move reader to the path table address - reader.setPointerIndex(pathAddress); - - ISO9660PathTable pathTable = new ISO9660PathTable(reader, littleEndian); - DataType pathTableDataType = pathTable.toDataType(); - - Address volumeAddress = toAddress(program, pathTable.getVolumeIndex()); - - setPlateComment(program, volumeAddress, pathTable.toString()); - Data pathTableData = createData(program, volumeAddress, pathTableDataType); - createFragment(program, module, pathTableDataType.getName(), - pathTableData.getMinAddress(), pathTableData.getMaxAddress().next()); - } - } - - /* - * Marks up the binary with data - */ - private Data createData(Program program, Address address, DataType datatype) throws Exception { - if (datatype instanceof StringDataType) { - CreateStringCmd cmd = new CreateStringCmd(address); - if (!cmd.applyTo(program)) { - throw new RuntimeException(cmd.getStatusMsg()); - } - } - else { - CreateDataCmd cmd = new CreateDataCmd(address, datatype); - if (!cmd.applyTo(program)) { - throw new RuntimeException(cmd.getStatusMsg()); - } - } - return program.getListing().getDefinedDataAt(address); - } - - private Address toAddress(Program program, long offset) { - return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); - } - - private boolean setPlateComment(Program program, Address address, String comment) { - SetCommentCmd cmd = new SetCommentCmd(address, CodeUnit.PLATE_COMMENT, comment); - return cmd.applyTo(program); - } - - private ProgramFragment createFragment(Program program, ProgramModule module, - String fragmentName, Address start, Address end) throws Exception { - - ProgramFragment fragment = getFragment(module, fragmentName); - if (fragment == null) { - fragment = module.createFragment(fragmentName); - } - fragment.move(start, end.subtract(1)); - return fragment; - } - - private ProgramFragment getFragment(ProgramModule module, String fragmentName) { - Group[] groups = module.getChildren(); - if (groups != null) { - for (Group group : groups) { - if (group.getName().equals(fragmentName) && group instanceof ProgramFragment) { - return (ProgramFragment) group; - } - } - } - - return null; - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660BaseVolume.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660BaseVolume.java deleted file mode 100644 index 83ceac8054..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660BaseVolume.java +++ /dev/null @@ -1,105 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.StructConverter; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -import java.io.IOException; - -/** - * Parent class used for all other types of volume descriptors - */ -public class ISO9660BaseVolume implements StructConverter { - - private long volumeIndex; - private byte typeCode; - private byte[] identifier; - private byte version; - - public ISO9660BaseVolume(BinaryReader reader) throws IOException { - volumeIndex = reader.getPointerIndex(); - typeCode = reader.readNextByte(); - identifier = reader.readNextByteArray(ISO9660Constants.MAGIC_BYTES.length); - version = reader.readNextByte(); - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc = new StructureDataType("ISO9660VolumeDescriptor", 0); - - struc.add(BYTE, "Type Code", "Type of volume descriptor"); - struc.add(new ArrayDataType(BYTE, identifier.length, 1), "Standard Identifier", - "Always 'CD001'"); - struc.add(BYTE, "Version", "Always 0x01"); - - return struc; - } - - /** - * Creates a string representation of this class filling in field specifics - * when applicable. - * @return the string representation of this class - */ - @Override - public String toString() { - StringBuffer buff = new StringBuffer(); - - buff.append("Type Code: 0x" + Integer.toHexString(typeCode) + " => " + getTypeCodeString() + - "\n"); - buff.append("Standard Identifier: " + new String(identifier).trim() + "\n"); - buff.append("Version: 0x" + Integer.toHexString(version) + "\n"); - - return buff.toString(); - } - - public String getTypeCodeString() { - - switch (typeCode) { - case ISO9660Constants.VOLUME_DESC_BOOT_RECORD: - return "Boot Record"; - case ISO9660Constants.VOLUME_DESC_PRIMARY_VOLUME_DESC: - return "Primary Volume Descriptor"; - case ISO9660Constants.VOLUME_DESC_SUPPL_VOLUME_DESC: - return "Supplementary Volume Descriptor"; - case ISO9660Constants.VOLUME_PARTITION_DESC: - return "Volume Partition Descriptor"; - case ISO9660Constants.VOLUME_DESC_SET_TERMINATOR: - return "Volume Descriptor Set Terminator"; - default: - return ""; - - } - } - - public byte getTypeCode() { - return typeCode; - } - - public byte[] getIdentifier() { - return identifier; - } - - public byte getVersion() { - return version; - } - - public long getVolumeIndex() { - return volumeIndex; - } -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660BootRecordVolumeDescriptor.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660BootRecordVolumeDescriptor.java deleted file mode 100644 index bdcbb27c5b..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660BootRecordVolumeDescriptor.java +++ /dev/null @@ -1,86 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -import java.io.IOException; - -public class ISO9660BootRecordVolumeDescriptor extends ISO9660BaseVolume { - - private byte[] bootSystemIdentifier;// Length 0x20 - private byte[] bootIdentifier; // Length 0x20 - private byte[] bootSystemUse; // Length 0x7b9; - - public ISO9660BootRecordVolumeDescriptor(BinaryReader reader) throws IOException { - super(reader); - bootSystemIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_32); - bootIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_32); - bootSystemUse = reader.readNextByteArray(ISO9660Constants.BOOT_SYSTEM_USE_LENGTH); - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc = new StructureDataType("ISO9600BootRecord", 0); - - struc.add(BYTE, "Type", "Volume Descriptor Type"); - struc.add(new ArrayDataType(BYTE, super.getIdentifier().length, 1), "Identifier", - "Identifier"); - struc.add(BYTE, "Version", "Volume Descriptor Version"); - struc.add(new ArrayDataType(BYTE, bootSystemIdentifier.length, 1), - "Boot System Identifier", "ID of the system which can act on and boot the system"); - struc.add(new ArrayDataType(BYTE, bootIdentifier.length, 1), "Boot Identifier", - "Identification of the boot system"); - struc.add(new ArrayDataType(BYTE, bootSystemUse.length, 1), "Boot System Use", - "Custom - used by the boot system"); - - return struc; - } - - /** - * Creates a string representation of this class filling in field specifics - * when applicable. - * @return the string representation of this class - */ - @Override - public String toString() { - StringBuffer buff = new StringBuffer(); - - buff.append("Type: 0x" + Integer.toHexString(super.getTypeCode()) + " => " + - getTypeCodeString() + "\n"); - buff.append("Identifier: " + new String(super.getIdentifier()).trim() + "\n"); - buff.append("Version: 0x" + Integer.toHexString(super.getVersion()) + "\n"); - buff.append("Boot System Identifier: " + new String(bootSystemIdentifier).trim() + "\n"); - buff.append("Boot Identifier: " + new String(bootIdentifier).trim() + "\n"); - - return buff.toString(); - } - - public byte[] getBootSystemIdentifier() { - return bootSystemIdentifier; - } - - public byte[] getBootIdentifier() { - return bootIdentifier; - } - - public byte[] getBootSystemUse() { - return bootSystemUse; - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Constants.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Constants.java deleted file mode 100644 index ef536918a1..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Constants.java +++ /dev/null @@ -1,77 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -/* - * Documentation gathered from http://wiki.osdev.org/ISO_9660 - */ -public final class ISO9660Constants { - - /* - * Volume Descriptor Type Codes - */ - public final static byte VOLUME_DESC_BOOT_RECORD = 0x0; - public final static byte VOLUME_DESC_PRIMARY_VOLUME_DESC = 0x1; - public final static byte VOLUME_DESC_SUPPL_VOLUME_DESC = 0x2; - public final static byte VOLUME_PARTITION_DESC = 0x3; - public final static byte VOLUME_DESC_SET_TERMINATOR = (byte) 0xff; - - /* - * Magic number identifier - */ - public final static String MAGIC_STRING = "CD001"; - public final static byte[] MAGIC_BYTES = { 0x43, 0x44, 0x30, 0x30, 0x31 }; - - public final static int HIDDEN_FILE_FLAG = 0; - public final static int DIRECTORY_FLAG = 1; - public final static int ASSOCIATED_FILE_FLAG = 2; - public final static int EXTENDED_ATTRIBUTE_RECORD_INFO_FLAG = 3; - public final static int OWNER_GROUP_PERMISSIONS_FLAG = 4; - public final static int NOT_FINAL_DIRECTORY_RECORD_FLAG = 5; - - public final static Short SECTOR_LENGTH = 0x800; - - public final static Byte FILE_STRUCTURE_VERISON = 0x01; - - public final static Short APPLICATION_USED_LENGTH = 0x200; - - /* - * Lists the three possible address offsets where the ISO9660 - * file signature can be located - */ - public final static int SIGNATURE_OFFSET1_0x8001 = 0x8001; - public final static int SIGNATURE_OFFSET2_0x8801 = 0x8801; - public final static int SIGNATURE_OFFSET3_0x9001 = 0x9001; - - public final static int MIN_ISO_LENGTH1 = 0x8800; - public final static int MIN_ISO_LENGTH2 = 0x9000; - public final static int MIN_ISO_LENGTH3 = 0x9800; - - public final static byte BAD_TYPE = -2; - - public final static int UNUSED_SPACER_LEN_32 = 32; - public final static int UNUSED_SPACER_LEN_512 = 512; - public final static int RESERVED_SIZE = 653; - public final static int IDENTIFIER_LENGTH_32 = 32; - public final static int IDENTIFIER_LENGTH_36 = 36; - public final static int IDENTIFIER_LENGTH_37 = 37; - public final static int IDENTIFIER_LENGTH_38 = 38; - public final static int IDENTIFIER_LENGTH_128 = 128; - public final static int BOOT_SYSTEM_USE_LENGTH = 1977; - public final static int DATE_TIME_LENGTH_7 = 7; - public final static int DATE_TIME_LENGTH_17 = 17; - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Directory.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Directory.java deleted file mode 100644 index e33764dde6..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Directory.java +++ /dev/null @@ -1,368 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import java.io.IOException; -import java.time.DateTimeException; -import java.time.LocalDateTime; - -import ghidra.app.util.bin.*; -import ghidra.formats.gfilesystem.FSRL; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -public class ISO9660Directory implements StructConverter { - - private int directoryRecordLength; - private byte extendedAttributeRecordLen; - private int locationOfExtentLE; - private int locationOfExtentBE; - private int dataLengthLE; - private int dataLengthBE; - private byte[] recordingDateTime; - private byte fileFlag; - private byte fileUnitSize; - private byte interleaveGapSize; - private short volumeSequenceNumberLE; - private short volumeSequenceNumberBE; - private byte fileIdentLength; - private byte[] fileIdentifier; - private byte paddingField; - private boolean paddingFieldPresent; - private long volumeIndex; - private String name; - private ISO9660Directory parentDir; - - public ISO9660Directory(BinaryReader reader) throws IOException { - this(reader, null); - } - - public ISO9660Directory(BinaryReader reader, ISO9660Directory parentDir) throws IOException { - this.parentDir = parentDir; - volumeIndex = reader.getPointerIndex(); - - directoryRecordLength = reader.readNextByte() & 0xff; - extendedAttributeRecordLen = reader.readNextByte(); - locationOfExtentLE = reader.readNextInt(); - locationOfExtentBE = readIntBigEndian(reader); - dataLengthLE = reader.readNextInt(); - dataLengthBE = readIntBigEndian(reader); - recordingDateTime = reader.readNextByteArray(ISO9660Constants.DATE_TIME_LENGTH_7); - fileFlag = reader.readNextByte(); - fileUnitSize = reader.readNextByte(); - interleaveGapSize = reader.readNextByte(); - volumeSequenceNumberLE = reader.readNextShort(); - volumeSequenceNumberBE = readShortBigEndian(reader); - fileIdentLength = reader.readNextByte(); - fileIdentifier = reader.readNextByteArray(fileIdentLength); - name = analyzeName(fileIdentifier); - - //The padding field will only be present if the - //fileIdentLength is even, otherwise it is not used - if (fileIdentLength % 2 == 0) { - paddingField = reader.readNextByte(); - paddingFieldPresent = true; - } - else { - paddingFieldPresent = false; - } - } - - private int readIntBigEndian(BinaryReader reader) throws IOException { - setReaderToBigEndian(reader); - int tmp = reader.readNextInt(); - setReaderToLittleEndian(reader); - - return tmp; - } - - private short readShortBigEndian(BinaryReader reader) throws IOException { - setReaderToBigEndian(reader); - short tmp = reader.readNextShort(); - setReaderToLittleEndian(reader); - - return tmp; - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc; - struc = new StructureDataType("ISO9600Directory", 0); - - struc.add(BYTE, "Directory Record Length", "Length of the Directory Record"); - struc.add(BYTE, "Extended Attribute Record Length", - "Length of the Extended Attribute Record"); - struc.add(QWORD, "Location of Extent", "LBA in (Little/Big)Endian (4 bytes each)"); - struc.add(QWORD, "Data Length", "Size of extent. (Little/Big)Endian (4 bytes each"); - struc.add(new ArrayDataType(BYTE, recordingDateTime.length, 1), "Recording date/time", - "Recording date and time"); - struc.add(BYTE, "File flags", "File flags"); - struc.add(BYTE, "File Unit Size", "File unit size for files recoraded in interleaved mode"); - struc.add(BYTE, "Interleave gap size", - "Interleave gap size for files recorded in interleaved mode"); - struc.add(DWORD, "Volume Sequence Number", "The clume that this extent is recorded in"); - struc.add(BYTE, "File Identifier Length", "Length of the file identifier"); - struc.add(new ArrayDataType(BYTE, fileIdentifier.length, 1), "File Identifier", - "File Identifier"); - if (paddingFieldPresent) { - struc.add(BYTE, "Padding Field", "Padding Field"); - } - - return struc; - } - - /** - * Creates a string representation of this class filling in field specifics - * when applicable. - * @return the string representation of this class - */ - @Override - public String toString() { - StringBuilder buff = new StringBuilder(); - - buff.append("Directory Record Length: 0x" + Integer.toHexString(directoryRecordLength) + - "\n"); - buff.append("Extended Attribute Record Length: 0x" + - Integer.toHexString(extendedAttributeRecordLen) + "\n"); - buff.append("Extent Location: 0x" + Integer.toHexString(getLocationOfExtentLE()) + "\n"); - buff.append("Data Length: 0x" + Integer.toHexString(getDataLengthLE()) + "\n"); - buff.append("Recording Date/Time: " + createDateTimeString(recordingDateTime) + "\n"); - buff.append(getFileFlagString() + "\n"); - buff.append("File Unit Size Interleaved Mode: 0x" + Integer.toHexString(fileUnitSize) + - "\n"); - buff.append("Interleave Gap Size: 0x" + Integer.toHexString(interleaveGapSize) + "\n"); - buff.append("Volume Sequence Number: 0x" + - Integer.toHexString(getVolumeSequenceNumberLE()) + "\n"); - buff.append("Length of File Identifier: 0x" + Integer.toHexString(fileIdentLength) + "\n"); - buff.append("File Identifier: " + new String(fileIdentifier).trim() + "\n"); - if (paddingFieldPresent) { - buff.append("Padding Field: 0x" + Integer.toHexString(paddingField) + "\n"); - } - - return buff.toString(); - } - - /* - * Looks at the fileIdentifier and checks if it is made up - * of visible not null ascii characters otherwise returns null - */ - private String analyzeName(byte[] bArr) { - for (int i = 0; i < bArr.length; i++) { - if (bArr[i] < 32) { - return null; - } - } - String tmp = new String(bArr); - return tmp; - } - - public boolean isDirectoryFlagSet() { - if (getFlagBit(fileFlag, ISO9660Constants.DIRECTORY_FLAG) == 1) { - return true; - } - return false; - } - - ByteProvider getByteProvider(ByteProvider provider, long logicalBlockSize, FSRL fsrl) { - - if (!this.isDirectoryFlagSet()) { - long index = locationOfExtentLE * logicalBlockSize; - return new ByteProviderWrapper(provider, index, dataLengthLE, fsrl); - } - return null; - } - - /* - * Parses the flag byte to return the string representation - * of the flags bits which are set - */ - private String getFileFlagString() { - String flagString = ""; - flagString += "File Flags:\n"; - if (getFlagBit(fileFlag, ISO9660Constants.HIDDEN_FILE_FLAG) == 1) { - flagString += "\tHidden File Flag Set"; - } - if (getFlagBit(fileFlag, ISO9660Constants.DIRECTORY_FLAG) == 1) { - flagString += "\tDirectory Flag Set"; - } - if (getFlagBit(fileFlag, ISO9660Constants.ASSOCIATED_FILE_FLAG) == 1) { - flagString += "\tAssociated File Flag Set"; - } - if (getFlagBit(fileFlag, ISO9660Constants.EXTENDED_ATTRIBUTE_RECORD_INFO_FLAG) == 1) { - flagString += "\tExtended Attribute Record Info Flag Set"; - } - if (getFlagBit(fileFlag, ISO9660Constants.NOT_FINAL_DIRECTORY_RECORD_FLAG) == 1) { - flagString += "\tNot Final Directory Record Flag"; - } - - return flagString; - } - - private byte getFlagBit(byte flagByte, int flagIndex) { - return (byte) ((flagByte >>> flagIndex) & 1); - } - - /** - * Parses the given buffer as an ISO9660 timestamp and returns it as a - * human readable string representation. - * - * Invalid buffers that are still big enough to hold a timestamp are - * still parsed and converted, albeit they are marked as invalid when - * presented to the user. - * - * @param byteArray the buffer to parse (both standard and extended - * formats are handled). - * @return a string with the human readable timestamp. - */ - private String createDateTimeString(byte[] byteArray) { - if (byteArray == null || byteArray.length < 7) { - return "INVALID (truncated or missing)"; - } - - // Time zone offset from GMT in 15 minute intervals, - // starting at interval -48 (west) and running up to - // interval 52 (east) - int timeOffset = byteArray[byteArray.length - 1]; - - int i1, i2, i3, i4, i5, i6; - i1 = 1900 + byteArray[0]; // Years since 1900 - i2 = byteArray[1]; // Month of year - i3 = byteArray[2]; // Day of month - i4 = byteArray[3]; // Hour of day - i5 = byteArray[4]; // Minute of hour - i6 = byteArray[5]; // Second of minute - - // The buffer contains an invalid timezone offset. - boolean validBuffer = true; - if (timeOffset < -48 || timeOffset > 52) { - validBuffer = false; - } - - // The buffer contains an invalid date/time. - try { - LocalDateTime.of(i1, i2, i3, i4, i5, i6); - } catch (DateTimeException exception) { - validBuffer = false; - } - - StringBuilder builder = new StringBuilder(); - if (!validBuffer) { - builder.append("INVALID ("); - } - int timezoneIntegral = timeOffset / 4; - int timezoneFractional = (Math.abs(timeOffset) % 4) * 15; - builder.append(String.format("%04d-%02d-%02d %02d:%02d:%02d GMT%c%02d%02d", i1, i2, i3, - i4, i5, i6, timezoneIntegral < 0 ? '-' : '+', timezoneIntegral, timezoneFractional)); - if (!validBuffer) { - builder.append(")"); - } - - return builder.toString(); - } - - private void setReaderToBigEndian(BinaryReader reader) { - reader.setLittleEndian(false); - } - - private void setReaderToLittleEndian(BinaryReader reader) { - reader.setLittleEndian(true); - } - - public long getVolumeIndex() { - return volumeIndex; - } - - public int getDirectoryRecordLength() { - return directoryRecordLength; - } - - public byte getExtendedAttributeRecordLen() { - return extendedAttributeRecordLen; - } - - public byte[] getRecordingDateTime() { - return recordingDateTime; - } - - public byte getFileFlag() { - return fileFlag; - } - - public byte getFileUnitSize() { - return fileUnitSize; - } - - public byte getInterleaveGapSize() { - return interleaveGapSize; - } - - public int getLocationOfExtentLE() { - return locationOfExtentLE; - } - - public int getLocationOfExtentBE() { - return locationOfExtentBE; - } - - public int getDataLengthLE() { - return dataLengthLE; - } - - public int getDataLengthBE() { - return dataLengthBE; - } - - public short getVolumeSequenceNumberLE() { - return volumeSequenceNumberLE; - } - - public short getVolumeSequenceNumberBE() { - return volumeSequenceNumberBE; - } - - public byte getFileIdentLength() { - return fileIdentLength; - } - - public byte[] getFileIdentifier() { - return fileIdentifier; - } - - public byte getPaddingField() { - return paddingField; - } - - public boolean isPaddingFieldPresent() { - return paddingFieldPresent; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public ISO9660Directory getParentDirectory() { - return parentDir; - } - - public void setParentDirectory(ISO9660Directory parentDir) { - this.parentDir = parentDir; - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660FileSystemFactory.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660FileSystemFactory.java index c3c88b4bbb..5c09a8aa49 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660FileSystemFactory.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660FileSystemFactory.java @@ -15,9 +15,10 @@ */ package ghidra.file.formats.iso9660; -import java.io.IOException; import java.util.Arrays; +import java.io.IOException; + import ghidra.app.util.bin.ByteProvider; import ghidra.formats.gfilesystem.FSRLRoot; import ghidra.formats.gfilesystem.FileSystemService; @@ -28,6 +29,8 @@ import ghidra.util.task.TaskMonitor; public class ISO9660FileSystemFactory implements GFileSystemFactoryByteProvider, GFileSystemProbeByteProvider { + private static final byte[] MAGIC_BYTES = { 0x43, 0x44, 0x30, 0x30, 0x31 }; // "CD001" + private static final long[] SIGNATURE_PROBE_OFFSETS = new long[] { 0x8000L, 0x8800L, 0x9000L }; @Override @@ -43,10 +46,10 @@ public class ISO9660FileSystemFactory } private boolean isMagicSignatureAt(ByteProvider provider, long offset) throws IOException { - int magicLen = ISO9660Constants.MAGIC_BYTES.length; + int magicLen = MAGIC_BYTES.length; long providerLen = provider.length(); return (providerLen > offset + magicLen) && - Arrays.equals(provider.readBytes(offset, magicLen), ISO9660Constants.MAGIC_BYTES); + Arrays.equals(provider.readBytes(offset, magicLen), MAGIC_BYTES); } @Override diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Header.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Header.java deleted file mode 100644 index cd3e09ed51..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660Header.java +++ /dev/null @@ -1,160 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.StructConverter; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - -public class ISO9660Header implements StructConverter { - - //Hold all volume descriptors - private ArrayList volumeDescriptorSet; - //HashMaps to hold the LBA index and index size of each path table location - private HashMap typeLIndexSizeTable; - private HashMap typeMIndexSizeTable; - private HashMap supplTypeLIndexSizeTable; - private HashMap supplTypeMIndexSizeTable; - - //Hold the directory from the primary volume descriptor - //This will be used as a starting point to recurse though the directory tree structure - //inside of the analyzer - private ISO9660Directory directory; - private ISO9660VolumeDescriptor primaryDesc; - - private byte type; - - public ISO9660Header(BinaryReader reader) throws IOException { - volumeDescriptorSet = new ArrayList(); - typeLIndexSizeTable = new HashMap(); - typeMIndexSizeTable = new HashMap(); - supplTypeLIndexSizeTable = new HashMap(); - supplTypeMIndexSizeTable = new HashMap(); - - type = ISO9660Constants.BAD_TYPE; //Bad type to fall into loop - - while (type != ISO9660Constants.VOLUME_DESC_SET_TERMINATOR) { - - // not terminator set - type = reader.readNextByte(); - reader.setPointerIndex(reader.getPointerIndex() - 1); - if (type == ISO9660Constants.VOLUME_DESC_BOOT_RECORD) { - - volumeDescriptorSet.add(new ISO9660BootRecordVolumeDescriptor(reader)); - } - else if (type == ISO9660Constants.VOLUME_DESC_PRIMARY_VOLUME_DESC) { - primaryDesc = new ISO9660VolumeDescriptor(reader); - - directory = primaryDesc.getDirectoryEntry(); - - volumeDescriptorSet.add(primaryDesc); - - typeLIndexSizeTable.put(primaryDesc.getTypeLPathTableLocation(), - primaryDesc.getLogicalBlockSizeLE()); - - typeMIndexSizeTable.put(primaryDesc.getTypeMPathTableLocation(), - primaryDesc.getLogicalBlockSizeBE()); - - if (primaryDesc.getDirectoryEntry().isPaddingFieldPresent()) { - reader.setPointerIndex(reader.getPointerIndex() - 1); - } - } - else if (type == ISO9660Constants.VOLUME_DESC_SUPPL_VOLUME_DESC) { - ISO9660VolumeDescriptor supplDesc = new ISO9660VolumeDescriptor(reader); - - volumeDescriptorSet.add(supplDesc); - - supplTypeLIndexSizeTable.put(supplDesc.getTypeLPathTableLocation(), - supplDesc.getLogicalBlockSizeLE()); - - supplTypeMIndexSizeTable.put(supplDesc.getTypeMPathTableLocation(), - supplDesc.getLogicalBlockSizeBE()); - - if (supplDesc.getDirectoryEntry().isPaddingFieldPresent()) { - reader.setPointerIndex(reader.getPointerIndex() - 1); - } - } - } - // got terminator set - volumeDescriptorSet.add(new ISO9660SetTerminator(reader)); - } - - public ISO9660Directory getPrimaryDirectory() { - - return directory; - } - - public ArrayList getVolumeDescriptorSet() { - return volumeDescriptorSet; - } - - public HashMap getTypeLIndexSizeTable() { - return typeLIndexSizeTable; - } - - public HashMap getTypeMIndexSizeTable() { - return typeMIndexSizeTable; - } - - public HashMap getSupplTypeLIndexSizeTable() { - return supplTypeLIndexSizeTable; - } - - public HashMap getSupplTypeMIndexSizeTable() { - return supplTypeMIndexSizeTable; - } - - public ISO9660VolumeDescriptor getPrimaryVolumeDescriptor() { - return primaryDesc; - } - - /** - * Creates a string representation of this class filling in field specifics - * when applicable. - * @return the string representation of this class - */ - @Override - public String toString() { - StringBuffer buff = new StringBuffer(); - - for (ISO9660BaseVolume volume : volumeDescriptorSet) { - - buff.append(volume.toString()); - } - - return buff.toString(); - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc = new StructureDataType("ISO9660Header", 0); - DataType data; - for (ISO9660BaseVolume volume : volumeDescriptorSet) { - - data = volume.toDataType(); - - struc.add(data); - } - - return struc; - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660PathTable.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660PathTable.java deleted file mode 100644 index 01ec178583..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660PathTable.java +++ /dev/null @@ -1,140 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.StructConverter; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -import java.io.IOException; - -public class ISO9660PathTable implements StructConverter { - - private byte dirIdentifierLength; - private byte extendedAttributeRecordLength; - private int locationOfExtent; - private short directoryNumberPathIndex; - private byte[] directoryIdentifier; - private byte paddingField; - private boolean paddingFieldPresent; - private long volumeIndex; - private boolean littleEndian; - - public ISO9660PathTable(BinaryReader reader, boolean littleEndian) throws IOException { - reader.setLittleEndian(littleEndian); - this.littleEndian = littleEndian; - volumeIndex = reader.getPointerIndex(); - dirIdentifierLength = reader.readNextByte(); - extendedAttributeRecordLength = reader.readNextByte(); - locationOfExtent = reader.readNextInt(); - directoryNumberPathIndex = reader.readNextShort(); - directoryIdentifier = reader.readNextByteArray(dirIdentifierLength); - - //The padding field is only present if the directoryIdentifierLength - //is odd, otherwise it is not used. - if (dirIdentifierLength % 2 != 0) { - paddingField = reader.readNextByte(); - paddingFieldPresent = true; - } - else { - paddingFieldPresent = false; - } - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc; - if (littleEndian) { - struc = new StructureDataType("ISO9660TypeLPathTable", 0); - } - else { - struc = new StructureDataType("ISO9660TypeMPathTable", 0); - } - - struc.add(BYTE, "Directory Identifier Length", "Length of Directory Identifier"); - struc.add(BYTE, "Extended Attribute Record Length", "Length of Extended Attribute Record"); - struc.add(DWORD, "Location of Extent", "Location of Extent in Little-endian format"); - struc.add(WORD, "Directory Number", - "Number of parent directory (an index in to the path table)"); - struc.add(new ArrayDataType(BYTE, directoryIdentifier.length, 1), "Directory Identifier", - "Directory Identifier"); - if (paddingFieldPresent) { - struc.add(BYTE, "Padding Field", "Padding Field"); - } - return struc; - } - - /** - * Creates a string representation of this class filling in field specifics - * when applicable. - * @return the string representation of this class - */ - @Override - public String toString() { - StringBuffer buff = new StringBuffer(); - - buff.append("Directory Identifier Length: 0x" + Integer.toHexString(dirIdentifierLength) + - "\n"); - buff.append("Extended Attribute Record Length: " + - Integer.toHexString(extendedAttributeRecordLength) + "\n"); - buff.append("Location of Extent (LBA): 0x" + Integer.toHexString(locationOfExtent) + "\n"); - buff.append("Directory Number: 0x" + Integer.toHexString(directoryNumberPathIndex) + "\n"); - buff.append("Directory Identifier: " + new String(directoryIdentifier).trim() + "\n"); - if (paddingFieldPresent) { - buff.append("PaddingF ield: 0x" + Integer.toHexString(paddingField) + "\n"); - } - - return buff.toString(); - } - - public byte getDirIdentifierLength() { - return dirIdentifierLength; - } - - public byte getExtendedAttributeRecordLength() { - return extendedAttributeRecordLength; - } - - public int getLocationOfExtent() { - return locationOfExtent; - } - - public short getDirectoryNumberPathIndex() { - return directoryNumberPathIndex; - } - - public byte[] getDirectoryIdentifier() { - return directoryIdentifier; - } - - public byte getPaddingField() { - return paddingField; - } - - public boolean isPaddingFieldPresent() { - return paddingFieldPresent; - } - - public long getVolumeIndex() { - return volumeIndex; - } - - public boolean isLittleEndian() { - return littleEndian; - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660SetTerminator.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660SetTerminator.java deleted file mode 100644 index 2974dd7837..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660SetTerminator.java +++ /dev/null @@ -1,54 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -import java.io.IOException; - -/** - * The terminator flag to note the end of the set of volume descriptors - * on this ISO - */ -public class ISO9660SetTerminator extends ISO9660BaseVolume { - - private long endVolumeIndex; - - public ISO9660SetTerminator(BinaryReader reader) throws IOException { - - super(reader); - endVolumeIndex = reader.getPointerIndex(); - - } - - public long getEndVolumeIndex() { - return endVolumeIndex; - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc = new StructureDataType("ISO9600SetTerminator", 0); - struc.add(BYTE, "Type", "Volume Descriptor Type"); - struc.add(new ArrayDataType(BYTE, super.getIdentifier().length, 1), "Identifier", - "Identifier"); - struc.add(BYTE, "Version", "Volume Descriptor Version"); - - return struc; - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660VolumeDescriptor.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660VolumeDescriptor.java deleted file mode 100644 index b1811b6af9..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/iso9660/ISO9660VolumeDescriptor.java +++ /dev/null @@ -1,489 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.iso9660; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.program.model.data.*; -import ghidra.util.exception.DuplicateNameException; - -import java.io.IOException; -import java.time.DateTimeException; -import java.time.LocalDateTime; - -public class ISO9660VolumeDescriptor extends ISO9660BaseVolume { - private byte unused; // Always 0x00 - private byte[] systemIdentifier; // Length 0x20 - private byte[] volumeIdentifier; // Length 0x20 - private long unused2; // All 0x00 - private int volumeSpaceSizeLE; // Little-endian - private int volumeSpaceSizeBE; // Big-endian - private byte[] unused3; // Length 0x20 - private short volumeSetSizeLE; // Little-endian - private short volumeSetSizeBE; // Big-endian - private short volumeSeqNumberLE; // Little-endian - private short volumeSeqNumberBE; // Big-endian - private short logicalBlockSizeLE; // Little-endian - private short logicalBlockSizeBE; // Big-endian - private int pathTableSizeLE; // Litte-endian - private int pathTableSizeBE; // Big-endian - private int typeLPathTableLocation; // -int32_LSB- - private int optionalTypeLPathTableLocation; // -int32_LSB- - private int typeMPathTableLocation; // -int32_MSB- - private int optionalTypeMPathTableLocation; // -int32_MSB- - private ISO9660Directory directoryEntry; // Length 0x20 - private byte[] volumeSetIdentifier; // Length 0x80 - private byte[] publisherIdentifier; // Length 0x80 - private byte[] dataPreparerIdentifier; // Length 0x80 - private byte[] applicationIdentifier; // Length 0x80 - private byte[] copyrightFileIdentifier; // Length 0x26 - private byte[] abstractFileIdentifier; // Length 0x24 - private byte[] bibliographicFileIdentifier; // Length 0x25 - private byte[] volumeCreationDateTime; // Length 0x11 - private byte[] volumeModifyDateTime; // Length 0x11 - private byte[] volumeExpirationDateTime; // Length 0x11 - private byte[] volumeEffectiveDateTime; // length 0x11 - private byte fileStructureVersion; // -int8- - private byte unused4; // Always 0x00 - private byte[] applicationUsed; // Length 0x200 - private byte[] reserved; // Length 0x28D - - public ISO9660VolumeDescriptor(BinaryReader reader) throws IOException { - - super(reader); - unused = reader.readNextByte(); - systemIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_32); - volumeIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_32); - unused2 = reader.readNextLong(); - volumeSpaceSizeLE = reader.readNextInt(); - volumeSpaceSizeBE = readIntBigEndian(reader); - unused3 = reader.readNextByteArray(ISO9660Constants.UNUSED_SPACER_LEN_32); - volumeSetSizeLE = reader.readNextShort(); - volumeSetSizeBE = readShortBigEndian(reader); - volumeSeqNumberLE = reader.readNextShort(); - volumeSeqNumberBE = readShortBigEndian(reader); - logicalBlockSizeLE = reader.readNextShort(); - logicalBlockSizeBE = readShortBigEndian(reader); - pathTableSizeLE = reader.readNextInt(); - pathTableSizeBE = readIntBigEndian(reader); - typeLPathTableLocation = reader.readNextInt(); - optionalTypeLPathTableLocation = reader.readNextInt(); - typeMPathTableLocation = readIntBigEndian(reader); - optionalTypeMPathTableLocation = readIntBigEndian(reader); - directoryEntry = new ISO9660Directory(reader); - volumeSetIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_128); - publisherIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_128); - dataPreparerIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_128); - applicationIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_128); - copyrightFileIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_38); - abstractFileIdentifier = reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_36); - bibliographicFileIdentifier = - reader.readNextByteArray(ISO9660Constants.IDENTIFIER_LENGTH_37); - volumeCreationDateTime = reader.readNextByteArray(ISO9660Constants.DATE_TIME_LENGTH_17); - volumeModifyDateTime = reader.readNextByteArray(ISO9660Constants.DATE_TIME_LENGTH_17); - volumeExpirationDateTime = reader.readNextByteArray(ISO9660Constants.DATE_TIME_LENGTH_17); - volumeEffectiveDateTime = reader.readNextByteArray(ISO9660Constants.DATE_TIME_LENGTH_17); - fileStructureVersion = reader.readNextByte(); - unused4 = reader.readNextByte(); - applicationUsed = reader.readNextByteArray(ISO9660Constants.UNUSED_SPACER_LEN_512); - reserved = reader.readNextByteArray(ISO9660Constants.RESERVED_SIZE); - - } - - private int readIntBigEndian(BinaryReader reader) throws IOException { - - setReaderToBigEndian(reader); - int tmp = reader.readNextInt(); - setReaderToLittleEndian(reader); - - return tmp; - } - - private short readShortBigEndian(BinaryReader reader) throws IOException { - - setReaderToBigEndian(reader); - short tmp = reader.readNextShort(); - setReaderToLittleEndian(reader); - - return tmp; - } - - @Override - public DataType toDataType() throws DuplicateNameException, IOException { - Structure struc; - if (super.getTypeCode() == ISO9660Constants.VOLUME_DESC_PRIMARY_VOLUME_DESC) { - struc = new StructureDataType("ISO9600PrimaryVolumeDescriptor", 0); - } - else if (super.getTypeCode() == ISO9660Constants.VOLUME_DESC_SUPPL_VOLUME_DESC) { - struc = new StructureDataType("ISO9600SupplementaryVolumeDescriptor", 0); - } - else { - struc = null; - } - - struc.add(BYTE, "Type Code", "Type of volume descriptor"); - struc.add(new ArrayDataType(BYTE, super.getIdentifier().length, 1), "Standard Identifier", - "Always 'CD001'"); - struc.add(BYTE, "Version", "Always 0x01"); - struc.add(BYTE, "Unused", "Always 0x00"); - struc.add(new ArrayDataType(BYTE, systemIdentifier.length, 1), "System Identifier", - "Name of the system to act upon sectors 0x00-0x0F"); - struc.add(new ArrayDataType(BYTE, volumeIdentifier.length, 1), "Volume Identifier", - "Identification for this volume"); - struc.add(QWORD, "Unused", "Always 0x00"); - struc.add(QWORD, "Volume Space Size", "Number of logical blocks the volume is recorded"); - struc.add(new ArrayDataType(BYTE, unused3.length, 1), "Unused", "Always 0x00"); - struc.add(DWORD, "Volume Set Size", "Size of the set in this logical volume"); - struc.add(DWORD, "Volume Sequence Number", "Number of disks in volume set"); - struc.add(DWORD, "Logical block Size", "Size of the logical block"); - struc.add(QWORD, "Path Table Size", "Size of the path table"); - struc.add(DWORD, "Location of Type-L Path Dable", - "LBA location of the path table containing only litle-endian values"); - struc.add(DWORD, "Location of Optional Type-L Path Table", - "LBA location of the optional path table containing only little-endian values"); - struc.add(DWORD, "Location of Type-M Path Table", - "LBA location of the path table containing only big-endian values"); - struc.add(DWORD, "Location of Optional Type-M Path Table", - "LBA location of the optional path table containing only big-endian values"); - struc.add(directoryEntry.toDataType()); - struc.add(new ArrayDataType(BYTE, volumeSetIdentifier.length, 1), "Volume Set Identifier", - "Identifier of the volume set which this volume is a member"); - struc.add(new ArrayDataType(BYTE, publisherIdentifier.length, 1), "Publisher Identifier", - "The volume publisher"); - struc.add(new ArrayDataType(BYTE, dataPreparerIdentifier.length, 1), - "Data Preparer Identifier", "Identifier of person(s) who prepared data for this volume"); - struc.add(new ArrayDataType(BYTE, applicationIdentifier.length, 1), - "Application Identifier", "How the data are recorded on this volume"); - struc.add(new ArrayDataType(BYTE, copyrightFileIdentifier.length, 1), - "Copyright File Identifier", - "Filename of file that contains copyright information on volume set"); - struc.add(new ArrayDataType(BYTE, abstractFileIdentifier.length, 1), - "Abstract File Identifier", - "Filename of file that contains abstract information on volume set"); - struc.add(new ArrayDataType(BYTE, bibliographicFileIdentifier.length, 1), - "Bibliographic File Identifier", - "Filename of file that contians bibliographic information on volume set"); - struc.add(new ArrayDataType(BYTE, volumeCreationDateTime.length, 1), - "Volume Creation Date and Time", "Date and time volume was created"); - struc.add(new ArrayDataType(BYTE, volumeModifyDateTime.length, 1), - "Volume Modification Date and Time", "Date and time volume was modified"); - struc.add(new ArrayDataType(BYTE, volumeExpirationDateTime.length, 1), - "Volume Expiration Date and Time", "Date and time volume was created"); - struc.add(new ArrayDataType(BYTE, volumeEffectiveDateTime.length, 1), - "Volume Effective Date and Time", "Date and time after which the volume may be used"); - struc.add(BYTE, "File Structure Version", "Directory records and path table version"); - struc.add(BYTE, "Unused", "Always 0x00"); - struc.add(new ArrayDataType(BYTE, applicationUsed.length, 1), "Application Used", - "Contents not defined by ISO 9660"); - struc.add(new ArrayDataType(BYTE, reserved.length, 1), "Reserved", "Reserved by ISO"); - - return struc; - } - - @Override - public String toString() { - StringBuilder buff = new StringBuilder(); - - buff.append("Type Code: 0x" + Integer.toHexString(super.getTypeCode()) + " => " + - getTypeCodeString() + "\n"); - buff.append("Standard Identifier: " + new String(super.getIdentifier()).trim() + "\n"); - buff.append("Version: 0x" + Integer.toHexString(super.getVersion()) + "\n"); - buff.append("Unused: 0x" + Integer.toHexString(unused) + "\n"); - buff.append("System Identifier: " + new String(systemIdentifier).trim() + "\n"); - buff.append("Volume Identifier: " + new String(volumeIdentifier).trim() + "\n"); - buff.append("Unused Field: 0x" + Long.toHexString(unused2) + "\n"); - buff.append("Volume Space Size: 0x" + Integer.toHexString(getVolumeSpaceSizeLE()) + "\n"); - buff.append("Unused: " + new String(unused3).trim() + "\n"); - buff.append("Volume Set Size: 0x" + Integer.toHexString(getVolumeSetSizeLE()) + "\n"); - buff.append("Volume Sequence Number: 0x" + Integer.toHexString(getVolumeSeqNumberLE()) + - "\n"); - buff.append("Logical Block Size: 0x" + Integer.toHexString(getLogicalBlockSizeLE()) + "\n"); - buff.append("Path Table Size: 0x" + Integer.toHexString(getPathTableSizeLE()) + "\n"); - buff.append("LBA Location of Type-L Path Table: 0x" + - Integer.toHexString(typeLPathTableLocation) + "\n"); - buff.append("LBA Location of Optional Type-L Path Table: 0x" + - Integer.toHexString(optionalTypeLPathTableLocation) + "\n"); - buff.append("LBA Location of Type-M Path Table: 0x" + - Integer.toHexString(typeMPathTableLocation) + "\n"); - buff.append("LBA Location of Optional Type-M Path Table: 0x" + - Integer.toHexString(optionalTypeMPathTableLocation) + "\n"); - buff.append("Calculated Location of Type-L Path Table: 0x" + - Integer.toHexString(typeLPathTableLocation * getLogicalBlockSizeLE()) + "\n"); - buff.append("Calculated Location of Type-M Path Table: 0x" + - Integer.toHexString(typeMPathTableLocation * getLogicalBlockSizeBE()) + "\n"); - buff.append("Directory Entry for Root Directory: \n" + directoryEntry.toString() + "\n"); - buff.append("Volume Set Identifier: " + new String(volumeSetIdentifier).trim() + "\n"); - buff.append("Publisher Identifier: " + new String(publisherIdentifier).trim() + "\n"); - buff.append("Data Preparer Identifier: " + new String(dataPreparerIdentifier).trim() + "\n"); - buff.append("Application Identifier: " + new String(applicationIdentifier).trim() + "\n"); - buff.append("Copyright File Identifier: " + new String(copyrightFileIdentifier).trim() + - "\n"); - buff.append("Abstract File Identifier: " + new String(abstractFileIdentifier).trim() + "\n"); - buff.append("Biliographic File Identifier: " + new String(bibliographicFileIdentifier) + - "\n"); - buff.append("Volume Creation Date/Time: " + createDateTimeString(volumeCreationDateTime) + - "\n"); - buff.append("Volume Modification Date/Time: " + createDateTimeString(volumeModifyDateTime) + - "\n"); - buff.append("Volume Expiration Date/Time: " + createDateTimeString(volumeCreationDateTime) + - "\n"); - buff.append("Volume Effective Date/Time: " + createDateTimeString(volumeEffectiveDateTime) + - "\n"); - buff.append("File Structure Version: 0x" + Integer.toHexString(fileStructureVersion) + "\n"); - buff.append("Unused: 0x" + Integer.toHexString(unused4) + "\n"); - - return buff.toString(); - } - - /** - * Checks whether the given string is entirely made up of ASCII digits. - * - * @param string the string to check. - * @return true if all characters in the string are ASCII digits, false - * otherwise. - */ - private boolean isDigitsStringValid(String string) { - for (int i = 0; i < string.length(); i++) { - char c = string.charAt(i); - if (c < '0' || c > '9') { - return false; - } - } - - return true; - } - - /** - * Parses the given buffer as an ISO9660 timestamp and returns it as a - * human readable string representation. - * - * Invalid buffers that are still big enough to hold a timestamp are - * still parsed and converted, albeit they are marked as invalid when - * presented to the user. - * - * @param byteArray the buffer to parse (only extended timestamp format - * is handled). - * @return a string with the human readable timestamp. - */ - protected String createDateTimeString(byte[] byteArray) { - if (byteArray == null || byteArray.length < 17) { - return "INVALID (truncated or missing)"; - } - - String s1, s2, s3, s4, s5, s6, s7; - - // Time zone offset from GMT in 15 minute intervals, - // starting at interval -48 (west) and running up to - // interval 52 (east) - int timeOffset = byteArray[byteArray.length - 1]; - - String bString = new String(byteArray); - s1 = bString.substring(0, 4); //year 1 to 9999 - s2 = bString.substring(4, 6); //month 1 to 12 - s3 = bString.substring(6, 8); //day 1 to 31 - s4 = bString.substring(8, 10); //hour 0 to 23 - s5 = bString.substring(10, 12); //minute 0 to 59 - s6 = bString.substring(12, 14); //second 0 to 59 - s7 = bString.substring(14, 16); //ms 0 to 99 - - // Validate strings first. - boolean validBuffer = isDigitsStringValid(s1) && isDigitsStringValid(s2) && isDigitsStringValid(s3) && - isDigitsStringValid(s4) && isDigitsStringValid(s5) && isDigitsStringValid(s6) && isDigitsStringValid(s7); - - try { - // The buffer contains an invalid date/time. - LocalDateTime.of(Integer.parseInt(s1), Integer.parseInt(s2), Integer.parseInt(s3), - Integer.parseInt(s4), Integer.parseInt(s5), Integer.parseInt(s6)); - } catch (NumberFormatException | DateTimeException e) { - validBuffer = false; - } - - // The buffer contains an invalid timezone offset. - if (timeOffset < -48 || timeOffset > 52) { - validBuffer = false; - } - - /* - * Time zone offset from GMT in 15 minute intervals, - * starting at interval -48 (west) and running up to - * interval 52 (east). - */ - int timezoneIntegral = timeOffset / 4; - int timezoneFractional = (Math.abs(timeOffset) % 4) * 15; - - StringBuilder builder = new StringBuilder(); - if (!validBuffer) { - builder.append("INVALID("); - } - - builder.append(String.format("%s-%s-%s %s:%s:%s.%s GMT%c%02d%02d", s1, s2, s3, s4, s5, s6, s7, - timezoneIntegral < 0 ? '-' : '+', timezoneIntegral, timezoneFractional)); - - if (!validBuffer) { - builder.append(")"); - } - - return builder.toString(); - } - - public byte getUnused() { - return unused; - } - - public byte[] getSystemIdentifier() { - return systemIdentifier; - } - - public byte[] getVolumeIdentifier() { - return volumeIdentifier; - } - - public long getUnused2() { - return unused2; - } - - public byte[] getUnused3() { - return unused3; - } - - public int getVolumeSpaceSizeLE() { - return volumeSpaceSizeLE; - } - - public int getVolumeSpaceSizeBE() { - return volumeSpaceSizeBE; - } - - public short getVolumeSetSizeLE() { - return volumeSetSizeLE; - } - - public short getVolumeSetSizeBE() { - return volumeSetSizeBE; - } - - public short getVolumeSeqNumberLE() { - return volumeSeqNumberLE; - } - - public short getVolumeSeqNumberBE() { - return volumeSeqNumberBE; - } - - public short getLogicalBlockSizeLE() { - return logicalBlockSizeLE; - } - - public short getLogicalBlockSizeBE() { - return logicalBlockSizeBE; - } - - public int getPathTableSizeLE() { - return pathTableSizeLE; - } - - public int getPathTableSizeBE() { - return pathTableSizeBE; - } - - public int getTypeLPathTableLocation() { - return typeLPathTableLocation; - } - - public int getOptionalTypeLPathTableLocation() { - return optionalTypeLPathTableLocation; - } - - public int getTypeMPathTableLocation() { - return typeMPathTableLocation; - } - - public int getOptionalTypeMPathTableLocation() { - return optionalTypeMPathTableLocation; - } - - public ISO9660Directory getDirectoryEntry() { - return directoryEntry; - } - - public byte[] getVolumeSetIdentifier() { - return volumeSetIdentifier; - } - - public byte[] getPublisherIdentifier() { - return publisherIdentifier; - } - - public byte[] getDataPreparerIdentifier() { - return dataPreparerIdentifier; - } - - public byte[] getApplicationIdentifier() { - return applicationIdentifier; - } - - public byte[] getCopyrightFileIdentifier() { - return copyrightFileIdentifier; - } - - public byte[] getAbstractFileIdentifier() { - return abstractFileIdentifier; - } - - public byte[] getBibliographicFileIdentifier() { - return bibliographicFileIdentifier; - } - - public byte[] getVolumeCreationDateTime() { - return volumeCreationDateTime; - } - - public byte[] getVolumeModifyDateTime() { - return volumeModifyDateTime; - } - - public byte[] getVolumeExpirationDateTime() { - return volumeExpirationDateTime; - } - - public byte[] getVolumeEffectiveDateTime() { - return volumeEffectiveDateTime; - } - - public byte getFileStructureVersion() { - return fileStructureVersion; - } - - public byte getUnused4() { - return unused4; - } - - public byte[] getApplicationUsed() { - return applicationUsed; - } - - public byte[] getReserved() { - return reserved; - } - - private void setReaderToBigEndian(BinaryReader reader) { - reader.setLittleEndian(false); - } - - private void setReaderToLittleEndian(BinaryReader reader) { - reader.setLittleEndian(true); - } - -} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/lzss/LzssAnalyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/lzss/LzssAnalyzer.java index 778d402d9f..1917c140e1 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/lzss/LzssAnalyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/lzss/LzssAnalyzer.java @@ -43,8 +43,7 @@ public class LzssAnalyzer extends FileFormatAnalyzer implements AnalysisWorker { throws Exception, CancelledException { Address address = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), address); - + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); LzssCompressionHeader header = new LzssCompressionHeader(provider); if (header.getSignature() != LzssConstants.SIGNATURE_COMPRESSION) { @@ -72,22 +71,27 @@ public class LzssAnalyzer extends FileFormatAnalyzer implements AnalysisWorker { return getName(); } + @Override public boolean canAnalyze(Program program) { return LzssUtil.isLZSS(program); } + @Override public boolean getDefaultEnablement(Program program) { return LzssUtil.isLZSS(program); } + @Override public String getDescription() { return "Annotates an LZSS compression file."; } + @Override public String getName() { return "LZSS Compression Annotation"; } + @Override public boolean isPrototype() { return true; } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/xar/XARUtil.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/xar/XARUtil.java index 2d2cca748a..8cf375f4cb 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/xar/XARUtil.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/xar/XARUtil.java @@ -15,17 +15,17 @@ */ package ghidra.file.formats.xar; +import java.util.Arrays; + import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.MemoryByteProvider; import ghidra.program.model.listing.Program; -import java.util.Arrays; - public class XARUtil { public final static boolean isXAR( Program program ) { - ByteProvider provider = new MemoryByteProvider( program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace() ); + ByteProvider provider = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, true); return isXAR( provider ); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/yaffs2/YAFFS2Analyzer.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/yaffs2/YAFFS2Analyzer.java index 6a1e12a945..c9e923c94c 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/yaffs2/YAFFS2Analyzer.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/yaffs2/YAFFS2Analyzer.java @@ -81,7 +81,7 @@ public class YAFFS2Analyzer extends FileFormatAnalyzer implements AnalysisWorker Address address = program.getMinAddress(); - ByteProvider provider = new MemoryByteProvider(program.getMemory(), address); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, true); int index = 0; diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEUtil.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEUtil.java index 0a1d163c38..da46056d63 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEUtil.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEUtil.java @@ -34,8 +34,8 @@ public class PEUtil { return true; } if (format.equals(BinaryLoader.BINARY_NAME)) { - MemoryByteProvider mbp = new MemoryByteProvider(program.getMemory(), - program.getAddressFactory().getDefaultAddressSpace()); + MemoryByteProvider mbp = + MemoryByteProvider.createDefaultAddressSpaceByteProvider(program, false); try { BinaryReader reader = new BinaryReader(mbp, true/*LittleEndian*/); DOSHeader dosHeader = new DOSHeader(reader); diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JvmSwitchAnalyzer.java b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JvmSwitchAnalyzer.java index 902e7bede4..d4feea8c22 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JvmSwitchAnalyzer.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/javaclass/analyzers/JvmSwitchAnalyzer.java @@ -15,10 +15,11 @@ */ package ghidra.javaclass.analyzers; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.io.IOException; + import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.services.AnalysisPriority; @@ -97,8 +98,7 @@ public class JvmSwitchAnalyzer extends AbstractJavaAnalyzer { monitor.setMaximum(set.getNumAddresses()); monitor.setProgress(0); - ByteProvider provider = - new MemoryByteProvider(program.getMemory(), program.getMinAddress()); + ByteProvider provider = MemoryByteProvider.createProgramHeaderByteProvider(program, false); BinaryReader reader = new BinaryReader(provider, false); Listing listing = program.getListing();