mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-28 05:20:21 +00:00
GP-2553 fix MemoryByteProvider's length() issues
Previously the impl would only return the length of the memory block where the byte provider started, or return 0 if there wasn't a memoryblock at the base address. Now returns the length of the available memory (in the same addressspace) that is found in the program's memory map, clamped to Long.MAX_VALUE. Updated users of MemoryByteProvider to use helper methods to create. Removed ISO9660 format analyzer (that was using MemorbyByteProvider) because we no longer have our own iso format code after switching to Sevenzip.
This commit is contained in:
parent
2113bdc8b1
commit
14f5cf93da
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
* <p>
|
||||
* 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 <code>nElements</code>
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* <b>Warnings:</b>
|
||||
* <p>
|
||||
* Using this ByteProvider with memory block/address spaces that are not simple "ram" initialized
|
||||
* memory blocks is fraught with peril.
|
||||
* <p>
|
||||
* 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).
|
||||
* <ul>
|
||||
* <li>The return value of {@link #length()} is constrained to a max of Long.MAX_VALUE</li>
|
||||
* <li>{@link #isValidIndex(long)} treats its argument as an unsigned int64, and works
|
||||
* for the entire address space range.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 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.
|
||||
* <ul>
|
||||
* <li>To avoid this situation, the caller will need to use information from the program's Memory
|
||||
* manager to align reads to valid locations.</li>
|
||||
* </ul>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @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}.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<DexHeader> 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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -103,7 +101,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;
|
||||
|
@ -118,30 +116,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<ISO9660BaseVolume> 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<ISO9660Directory> 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<ISO9660BaseVolume> 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<Integer, Short> 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<Integer, Short> 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<Integer, Short> 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<Integer, Short> 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<ISO9660Directory> createDirectoryList(BinaryReader reader,
|
||||
ISO9660Directory parentDir, long blockSize) throws IOException {
|
||||
|
||||
List<ISO9660Directory> 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<ISO9660Directory> 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<ISO9660Directory> 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<Integer, Short> pathTableMap, boolean littleEndian) throws Exception {
|
||||
|
||||
//Enumeration over the indexes of the path tables in the table
|
||||
|
||||
Set<Integer> pathTableIndexes = pathTableMap.keySet();
|
||||
Iterator<Integer> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ISO9660FileSystem>, 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
|
||||
|
|
|
@ -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<ISO9660BaseVolume> volumeDescriptorSet;
|
||||
//HashMaps to hold the LBA index and index size of each path table location
|
||||
private HashMap<Integer, Short> typeLIndexSizeTable;
|
||||
private HashMap<Integer, Short> typeMIndexSizeTable;
|
||||
private HashMap<Integer, Short> supplTypeLIndexSizeTable;
|
||||
private HashMap<Integer, Short> 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<ISO9660BaseVolume>();
|
||||
typeLIndexSizeTable = new HashMap<Integer, Short>();
|
||||
typeMIndexSizeTable = new HashMap<Integer, Short>();
|
||||
supplTypeLIndexSizeTable = new HashMap<Integer, Short>();
|
||||
supplTypeMIndexSizeTable = new HashMap<Integer, Short>();
|
||||
|
||||
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<ISO9660BaseVolume> getVolumeDescriptorSet() {
|
||||
return volumeDescriptorSet;
|
||||
}
|
||||
|
||||
public HashMap<Integer, Short> getTypeLIndexSizeTable() {
|
||||
return typeLIndexSizeTable;
|
||||
}
|
||||
|
||||
public HashMap<Integer, Short> getTypeMIndexSizeTable() {
|
||||
return typeMIndexSizeTable;
|
||||
}
|
||||
|
||||
public HashMap<Integer, Short> getSupplTypeLIndexSizeTable() {
|
||||
return supplTypeLIndexSizeTable;
|
||||
}
|
||||
|
||||
public HashMap<Integer, Short> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue