mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-28 05:20:21 +00:00
Merge remote-tracking branch 'origin/GP-2553_dev747368_fix_memorybyteprovider_length_take2--SQUASHED'
This commit is contained in:
commit
d959fe5cb7
|
@ -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;
|
||||
|
@ -112,7 +110,7 @@ public final class VdexConstants {
|
|||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
|
||||
try (ByteProvider provider =
|
||||
new MemoryByteProvider(program.getMemory(), block.getStart())) {
|
||||
MemoryByteProvider.createMemoryBlockByteProvider(program.getMemory(), block)) {
|
||||
String magic = new String(provider.readBytes(0, VdexConstants.MAGIC.length()));
|
||||
if (VdexConstants.MAGIC.equals(magic)) {
|
||||
return true;
|
||||
|
@ -127,30 +125,21 @@ public final class VdexConstants {
|
|||
}
|
||||
|
||||
public final static Address findVDEX(Program program) {
|
||||
try {
|
||||
if (program != null) {
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
ByteProvider provider =
|
||||
new MemoryByteProvider(program.getMemory(), block.getStart());
|
||||
try {
|
||||
String magic =
|
||||
new String(provider.readBytes(0, VdexConstants.MAGIC.length()));
|
||||
if (VdexConstants.MAGIC.equals(magic)) {
|
||||
return block.getStart();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
finally {
|
||||
provider.close();
|
||||
if (program != null) {
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
try (ByteProvider provider = MemoryByteProvider
|
||||
.createMemoryBlockByteProvider(program.getMemory(), block)) {
|
||||
String magic =
|
||||
new String(provider.readBytes(0, VdexConstants.MAGIC.length()));
|
||||
if (VdexConstants.MAGIC.equals(magic)) {
|
||||
return block.getStart();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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