GP-4025 - PDB - Use ByteProvider in place of RandomAccessFile

This commit is contained in:
ghizard 2023-11-26 12:40:10 -05:00
parent c225fac124
commit 4da04423bd
15 changed files with 152 additions and 114 deletions

View file

@ -113,12 +113,10 @@ public class Pagedump extends DumpFile {
props.setString("Executable Format", PeLoader.PE_NAME); props.setString("Executable Format", PeLoader.PE_NAME);
initManagerList(addins); initManagerList(addins);
createBlocks = createBlocks = OptionUtils.getBooleanOptionValue(CREATE_MEMORY_BLOCKS_OPTION_NAME, options,
OptionUtils.getBooleanOptionValue(CREATE_MEMORY_BLOCKS_OPTION_NAME, CREATE_MEMORY_BLOCKS_OPTION_DEFAULT);
options, CREATE_MEMORY_BLOCKS_OPTION_DEFAULT); String pdbLocation = OptionUtils.getOption(DEBUG_DATA_PATH_OPTION_NAME, options,
String pdbLocation = DEBUG_DATA_PATH_OPTION_DEFAULT);
OptionUtils.getOption(DEBUG_DATA_PATH_OPTION_NAME, options,
DEBUG_DATA_PATH_OPTION_DEFAULT);
if (!pdbLocation.equals("")) { if (!pdbLocation.equals("")) {
loadKernelPDB(pdbLocation, monitor); loadKernelPDB(pdbLocation, monitor);
} }
@ -152,8 +150,7 @@ public class Pagedump extends DumpFile {
data.add(new DumpData(hdrLen, dt)); data.add(new DumpData(hdrLen, dt));
data.add(new DumpData(full.getHeaderSize(), "Physical_Memory", 0)); data.add(new DumpData(full.getHeaderSize(), "Physical_Memory", 0));
offset = (int) full.getHeaderSize(); offset = (int) full.getHeaderSize();
addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, offset - hdrLen);
offset - hdrLen);
if (createBlocks) { if (createBlocks) {
mapPages(monitor); mapPages(monitor);
} }
@ -164,20 +161,17 @@ public class Pagedump extends DumpFile {
break; break;
} }
addInteriorAddressObject("Unknown", offset, offset, addInteriorAddressObject("Unknown", offset, offset, reader.length() - offset);
reader.length() - offset);
break; break;
case DUMP_TYPE_TRIAGE: case DUMP_TYPE_TRIAGE:
triage = new TriageDump(reader, hdrLen); triage = new TriageDump(reader, hdrLen);
dt = triage.toDataType(); dt = triage.toDataType();
data.add(new DumpData(hdrLen, dt)); data.add(new DumpData(hdrLen, dt));
addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, triage.getSizeOfDump());
triage.getSizeOfDump());
long next = hdrLen + triage.getSizeOfDump(); long next = hdrLen + triage.getSizeOfDump();
addInteriorAddressObject("Unknown", next, addInteriorAddressObject("Unknown", next, next, reader.length() - next);
next, reader.length() - next);
buildKernelStructures(); buildKernelStructures();
break; break;
@ -199,12 +193,12 @@ public class Pagedump extends DumpFile {
PdbReaderOptions readerOptions = new PdbReaderOptions(); PdbReaderOptions readerOptions = new PdbReaderOptions();
PdbApplicatorOptions applicatorOptions = new PdbApplicatorOptions(); PdbApplicatorOptions applicatorOptions = new PdbApplicatorOptions();
applicatorOptions.setProcessingControl(PdbApplicatorControl.DATA_TYPES_ONLY); applicatorOptions.setProcessingControl(PdbApplicatorControl.DATA_TYPES_ONLY);
try (AbstractPdb pdb = PdbParser.parse(pdbFile.getPath(), readerOptions, monitor)) { try (AbstractPdb pdb = PdbParser.parse(pdbFile, readerOptions, monitor)) {
monitor.setMessage("PDB: Parsing " + pdbFile + "..."); monitor.setMessage("PDB: Parsing " + pdbFile + "...");
pdb.deserialize(); pdb.deserialize();
DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb);
applicator.applyTo(program, dtm, program.getImageBase(), applicator.applyTo(program, dtm, program.getImageBase(), applicatorOptions,
applicatorOptions, (MessageLog) null); (MessageLog) null);
} }
catch (PdbException | IOException | CancelledException e) { catch (PdbException | IOException | CancelledException e) {
Msg.error(this, e.getMessage()); Msg.error(this, e.getMessage());
@ -218,16 +212,16 @@ public class Pagedump extends DumpFile {
long runLength = run.getPageCount() * PAGE_SIZE; long runLength = run.getPageCount() * PAGE_SIZE;
boolean outOfBounds = runLength + total * PAGE_SIZE > reader.length(); boolean outOfBounds = runLength + total * PAGE_SIZE > reader.length();
long bound = (outOfBounds) ? (reader.length() - total * PAGE_SIZE) : runLength; long bound = (outOfBounds) ? (reader.length() - total * PAGE_SIZE) : runLength;
ArrayDataType adt = ArrayDataType adt = new ArrayDataType(StructConverter.BYTE, (int) bound, 1);
new ArrayDataType(StructConverter.BYTE, (int) bound, 1);
data.add(new DumpData(total * PAGE_SIZE, adt)); data.add(new DumpData(total * PAGE_SIZE, adt));
// NB: Not sure if or where to place these // NB: Not sure if or where to place these
//addInteriorAddressObject(DumpFileLoader.LOCAL, total * PAGE_SIZE, //addInteriorAddressObject(DumpFileLoader.LOCAL, total * PAGE_SIZE,
// run.getBasePage() * PAGE_SIZE, run.getPageCount() * PAGE_SIZE); // run.getBasePage() * PAGE_SIZE, run.getPageCount() * PAGE_SIZE);
total += run.getPageCount(); total += run.getPageCount();
if (outOfBounds) if (outOfBounds) {
break; break;
}
} }
} }
@ -313,8 +307,7 @@ public class Pagedump extends DumpFile {
} }
} }
uds.add(new ArrayDataType(udt, (int) count, udt.getLength()), uds.add(new ArrayDataType(udt, (int) count, udt.getLength()),
udt.getLength() * (int) count, udt.getLength() * (int) count, "UnloadedDrivers", null);
"UnloadedDrivers", null);
} }
data.add(new DumpData(offset, uds)); data.add(new DumpData(offset, uds));
} }
@ -325,8 +318,9 @@ public class Pagedump extends DumpFile {
while (offset < end) { while (offset < end) {
int len = reader.readInt(offset); int len = reader.readInt(offset);
data.add(new DumpData(offset, StructConverter.DWORD, "", false, false)); data.add(new DumpData(offset, StructConverter.DWORD, "", false, false));
if (len == 0 || len == 0xFFFFFFFF) if (len == 0 || len == 0xFFFFFFFF) {
break; break;
}
offset += 4; offset += 4;
DumpData dd = new DumpData(offset, new TerminatedUnicodeDataType(), "", false, false); DumpData dd = new DumpData(offset, new TerminatedUnicodeDataType(), "", false, false);
dd.setSize(len * 2 + 2); dd.setSize(len * 2 + 2);
@ -348,8 +342,8 @@ public class Pagedump extends DumpFile {
DataType db = null; DataType db = null;
for (int i = 0; i < triage.getDataBlocksCount(); i++) { for (int i = 0; i < triage.getDataBlocksCount(); i++) {
TriageDataBlock tdb = new TriageDataBlock(reader, reader.getPointerIndex()); TriageDataBlock tdb = new TriageDataBlock(reader, reader.getPointerIndex());
addInteriorAddressObject(DumpFileLoader.MEMORY, tdb.getOffset(), addInteriorAddressObject(DumpFileLoader.MEMORY, tdb.getOffset(), tdb.getAddress(),
tdb.getAddress(), tdb.getSize()); tdb.getSize());
VA2fileOffset.put(tdb.getAddress(), tdb.getOffset()); VA2fileOffset.put(tdb.getAddress(), tdb.getOffset());
db = tdb.toDataType(); db = tdb.toDataType();
} }
@ -387,8 +381,7 @@ public class Pagedump extends DumpFile {
if (namePtr != 0) { if (namePtr != 0) {
long fileOffset = virtualToRva(namePtr); long fileOffset = virtualToRva(namePtr);
String name = reader.readUnicodeString(fileOffset); String name = reader.readUnicodeString(fileOffset);
addExteriorAddressObject(name, 0, entry.getDllBase(), addExteriorAddressObject(name, 0, entry.getDllBase(), entry.getSizeOfImage());
entry.getSizeOfImage());
} }
next = entry.getList_Flink(); next = entry.getList_Flink();
if (entryKeys.contains(next)) { if (entryKeys.contains(next)) {
@ -464,10 +457,8 @@ public class Pagedump extends DumpFile {
@Override @Override
public void analyze(TaskMonitor monitor) { public void analyze(TaskMonitor monitor) {
boolean analyzeEmbeddedObjects = boolean analyzeEmbeddedObjects = OptionUtils.getBooleanOptionValue(
OptionUtils.getBooleanOptionValue(ANALYZE_EMBEDDED_OBJECTS_OPTION_NAME, ANALYZE_EMBEDDED_OBJECTS_OPTION_NAME, options, ANALYZE_EMBEDDED_OBJECTS_OPTION_DEFAULT);
options,
ANALYZE_EMBEDDED_OBJECTS_OPTION_DEFAULT);
if (analyzeEmbeddedObjects) { if (analyzeEmbeddedObjects) {
ModuleToPeHelper.queryModules(program, monitor); ModuleToPeHelper.queryModules(program, monitor);
} }

View file

@ -77,7 +77,7 @@ public class PdbDeveloperApplyDummyScript extends GhidraScript {
memory.createUninitializedBlock(pdbFileName, program.getImageBase(), memory.createUninitializedBlock(pdbFileName, program.getImageBase(),
Memory.MAX_BLOCK_SIZE / 16, false); Memory.MAX_BLOCK_SIZE / 16, false);
try (AbstractPdb pdb = PdbParser.parse(pdbFile.getPath(), pdbReaderOptions, monitor)) { try (AbstractPdb pdb = PdbParser.parse(pdbFile, pdbReaderOptions, monitor)) {
monitor.setMessage("PDB: Parsing " + pdbFile + "..."); monitor.setMessage("PDB: Parsing " + pdbFile + "...");
pdb.deserialize(); pdb.deserialize();
DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb);

View file

@ -62,7 +62,7 @@ public class PdbDeveloperDumpScript extends GhidraScript {
String message = "Processing PDB Dump of: " + pdbFileName; String message = "Processing PDB Dump of: " + pdbFileName;
monitor.setMessage(message); monitor.setMessage(message);
Msg.info(this, message); Msg.info(this, message);
try (AbstractPdb pdb = PdbParser.parse(pdbFileName, new PdbReaderOptions(), monitor)) { try (AbstractPdb pdb = PdbParser.parse(pdbFile, new PdbReaderOptions(), monitor)) {
pdb.deserialize(); pdb.deserialize();
FileWriter fileWriter = new FileWriter(dumpFile); FileWriter fileWriter = new FileWriter(dumpFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

View file

@ -84,8 +84,8 @@ public class PdbDeveloperDumpSetScript extends GhidraScript {
for (IOEntry entry : entries) { for (IOEntry entry : entries) {
monitor.checkCancelled(); monitor.checkCancelled();
println("Processing PDB Dump of: " + entry.input()); println("Processing PDB Dump of: " + entry.input());
try (AbstractPdb pdb = File pdbFile = new File(entry.input());
PdbParser.parse(entry.input(), new PdbReaderOptions(), monitor)) { try (AbstractPdb pdb = PdbParser.parse(pdbFile, new PdbReaderOptions(), monitor)) {
pdb.deserialize(); pdb.deserialize();
try (BufferedWriter bufferedWriter = try (BufferedWriter bufferedWriter =
new BufferedWriter(new FileWriter(new File(entry.output())))) { new BufferedWriter(new FileWriter(new File(entry.output())))) {

View file

@ -15,6 +15,7 @@
*/ */
package pdbquery; package pdbquery;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -59,7 +60,8 @@ public class PdbFactory {
println(script, "Opening PDB: " + filename); println(script, "Opening PDB: " + filename);
try { try {
AbstractPdb pdb = PdbParser.parse(filename, new PdbReaderOptions(), monitor); File pdbFile = new File(filename);
AbstractPdb pdb = PdbParser.parse(pdbFile, new PdbReaderOptions(), monitor);
PdbIdentifiers identifiers = pdb.getIdentifiers(); PdbIdentifiers identifiers = pdb.getIdentifiers();
pdb.deserialize(); pdb.deserialize();
PdbReaderMetrics metrics = pdb.getPdbReaderMetrics(); PdbReaderMetrics metrics = pdb.getPdbReaderMetrics();

View file

@ -178,7 +178,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
PdbLog.message(DESCRIPTION); PdbLog.message(DESCRIPTION);
PdbLog.message("PDB Filename: " + pdbFile + "\n"); PdbLog.message("PDB Filename: " + pdbFile + "\n");
try (AbstractPdb pdb = PdbParser.parse(pdbFile.getPath(), pdbReaderOptions, monitor)) { try (AbstractPdb pdb = PdbParser.parse(pdbFile, pdbReaderOptions, monitor)) {
monitor.setMessage("PDB: Parsing " + pdbFile + "..."); monitor.setMessage("PDB: Parsing " + pdbFile + "...");
pdb.deserialize(); pdb.deserialize();
DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb);

View file

@ -15,11 +15,16 @@
*/ */
package ghidra.app.util.bin.format.pdb2.pdbreader; package ghidra.app.util.bin.format.pdb2.pdbreader;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.AccessMode;
import java.util.Objects; import java.util.Objects;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.FileByteProvider;
import ghidra.app.util.bin.format.pdb2.pdbreader.msf.Msf; import ghidra.app.util.bin.format.pdb2.pdbreader.msf.Msf;
import ghidra.app.util.bin.format.pdb2.pdbreader.msf.MsfParser; import ghidra.app.util.bin.format.pdb2.pdbreader.msf.MsfParser;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -54,15 +59,63 @@ public class PdbParser {
* @throws PdbException on parsing issues * @throws PdbException on parsing issues
* @throws CancelledException upon user cancellation * @throws CancelledException upon user cancellation
*/ */
@Deprecated
public static AbstractPdb parse(String filename, PdbReaderOptions pdbOptions, public static AbstractPdb parse(String filename, PdbReaderOptions pdbOptions,
TaskMonitor monitor) throws IOException, PdbException, CancelledException { TaskMonitor monitor) throws IOException, PdbException, CancelledException {
Objects.requireNonNull(filename, "filename cannot be null"); Objects.requireNonNull(filename, "filename cannot be null");
Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null"); Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null");
Objects.requireNonNull(monitor, "monitor cannot be null"); Objects.requireNonNull(monitor, "monitor cannot be null");
File file = new File(filename);
return parse(file, pdbOptions, monitor);
}
/**
* Static method to open a PDB file, determine its version, and return an {@link AbstractPdb}
* appropriate for that version; it will not have been deserialized. The main method
* to deserialize it is {@link AbstractPdb#deserialize()}; the method
* used to deserialize its main identifiers (signature, age, guid (if available)) is
* {@link AbstractPdb#deserializeIdentifiersOnly(TaskMonitor monitor)}
* @param file of the PDB file to parse
* @param pdbOptions {@link PdbReaderOptions} used for processing the PDB
* @param monitor {@link TaskMonitor} used for checking cancellation
* @return {@link AbstractPdb} class object for the file
* @throws IOException on file I/O issues
* @throws PdbException on parsing issues
* @throws CancelledException upon user cancellation
*/
public static AbstractPdb parse(File file, PdbReaderOptions pdbOptions, TaskMonitor monitor)
throws IOException, PdbException, CancelledException {
Objects.requireNonNull(file, "file cannot be null");
Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null");
Objects.requireNonNull(monitor, "monitor cannot be null");
ByteProvider byteProvider = new FileByteProvider(file,
FileSystemService.getInstance().getLocalFSRL(file), AccessMode.READ);
return parse(byteProvider, pdbOptions, monitor);
}
/**
* Static method to open a PDB file, determine its version, and return an {@link AbstractPdb}
* appropriate for that version; it will not have been deserialized. The main method
* to deserialize it is {@link AbstractPdb#deserialize()}; the method
* used to deserialize its main identifiers (signature, age, guid (if available)) is
* {@link AbstractPdb#deserializeIdentifiersOnly(TaskMonitor monitor)}
* @param byteProvider the ByteProvider providing bytes for the PDB
* @param pdbOptions {@link PdbReaderOptions} used for processing the PDB
* @param monitor {@link TaskMonitor} used for checking cancellation
* @return {@link AbstractPdb} class object for the file
* @throws IOException on file I/O issues
* @throws PdbException on parsing issues
* @throws CancelledException upon user cancellation
*/
public static AbstractPdb parse(ByteProvider byteProvider, PdbReaderOptions pdbOptions,
TaskMonitor monitor) throws IOException, PdbException, CancelledException {
Objects.requireNonNull(byteProvider, "byteProvider cannot be null");
Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null");
Objects.requireNonNull(monitor, "monitor cannot be null");
// Do not do a try with resources here, as the msf must live within the PDB that is // Do not do a try with resources here, as the msf must live within the PDB that is
// created below. // created below.
Msf msf = MsfParser.parse(filename, pdbOptions, monitor); Msf msf = MsfParser.parse(byteProvider, pdbOptions, monitor);
int versionNumber = AbstractPdb.deserializeVersionNumber(msf, monitor); int versionNumber = AbstractPdb.deserializeVersionNumber(msf, monitor);

View file

@ -19,6 +19,7 @@ import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.Objects; import java.util.Objects;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -134,18 +135,16 @@ public abstract class AbstractMsf implements Msf {
//============================================================================================== //==============================================================================================
/** /**
* Constructor * Constructor
* @param file the {@link RandomAccessFile} to process for this class * @param byteProvider the ByteProvider providing bytes for the MSF
* @param filename name of {@code #file}
* @param monitor the TaskMonitor * @param monitor the TaskMonitor
* @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB
* @throws IOException upon file IO seek/read issues * @throws IOException upon file IO seek/read issues
* @throws PdbException upon unknown value for configuration * @throws PdbException upon unknown value for configuration
*/ */
public AbstractMsf(RandomAccessFile file, String filename, TaskMonitor monitor, public AbstractMsf(ByteProvider byteProvider, TaskMonitor monitor, PdbReaderOptions pdbOptions)
PdbReaderOptions pdbOptions)
throws IOException, PdbException { throws IOException, PdbException {
Objects.requireNonNull(file, "file may not be null"); Objects.requireNonNull(byteProvider, "ByteProvider may not be null");
this.filename = Objects.requireNonNull(filename, "filename may not be null"); this.filename = byteProvider.getAbsolutePath();
this.monitor = TaskMonitor.dummyIfNull(monitor); this.monitor = TaskMonitor.dummyIfNull(monitor);
this.pdbOptions = Objects.requireNonNull(pdbOptions, "PdbOptions may not be null"); this.pdbOptions = Objects.requireNonNull(pdbOptions, "PdbOptions may not be null");
// Do initial configuration with largest possible page size. ConfigureParameters will // Do initial configuration with largest possible page size. ConfigureParameters will
@ -159,7 +158,7 @@ public abstract class AbstractMsf implements Msf {
pageSize = 0x1000; pageSize = 0x1000;
configureParameters(); configureParameters();
// Create components. // Create components.
fileReader = new MsfFileReader(this, file); fileReader = new MsfFileReader(this, byteProvider);
create(); create();
} }
@ -320,8 +319,7 @@ public abstract class AbstractMsf implements Msf {
* @throws CancelledException upon user cancellation * @throws CancelledException upon user cancellation
*/ */
@Override @Override
public void deserialize() public void deserialize() throws IOException, PdbException, CancelledException {
throws IOException, PdbException, CancelledException {
byte[] bytes = new byte[getPageSize()]; byte[] bytes = new byte[getPageSize()];
fileReader.read(getHeaderPageNumber(), 0, getPageSize(), bytes, 0); fileReader.read(getHeaderPageNumber(), 0, getPageSize(), bytes, 0);

View file

@ -16,9 +16,9 @@
package ghidra.app.util.bin.format.pdb2.pdbreader.msf; package ghidra.app.util.bin.format.pdb2.pdbreader.msf;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays; import java.util.Arrays;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -41,17 +41,15 @@ public class Msf200 extends AbstractMsf {
//============================================================================================== //==============================================================================================
/** /**
* Constructor * Constructor
* @param file the {@link RandomAccessFile} to process as a {@link Msf200} * @param byteProvider the ByteProvider providing bytes for the MSF
* @param filename name of {@code #file}
* @param monitor the TaskMonitor * @param monitor the TaskMonitor
* @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB
* @throws IOException upon file IO seek/read issues * @throws IOException upon file IO seek/read issues
* @throws PdbException upon unknown value for configuration * @throws PdbException upon unknown value for configuration
*/ */
public Msf200(RandomAccessFile file, String filename, TaskMonitor monitor, public Msf200(ByteProvider byteProvider, TaskMonitor monitor, PdbReaderOptions pdbOptions)
PdbReaderOptions pdbOptions)
throws IOException, PdbException { throws IOException, PdbException {
super(file, filename, monitor, pdbOptions); super(byteProvider, monitor, pdbOptions);
} }
//============================================================================================== //==============================================================================================
@ -59,14 +57,12 @@ public class Msf200 extends AbstractMsf {
//============================================================================================== //==============================================================================================
/** /**
* Static method used to detect the header that belongs to this class * Static method used to detect the header that belongs to this class
* @param file the {@link RandomAccessFile} to process as a {@link Msf200} * @param byteProvider the ByteProvider to process as a {@link Msf200}
* @return {@code true} if the header for this class is positively identified * @return {@code true} if the header for this class is positively identified
* @throws IOException upon file IO seek/read issues * @throws IOException upon file IO seek/read issues
*/ */
static boolean detected(RandomAccessFile file) throws IOException { static boolean detected(ByteProvider byteProvider) throws IOException {
byte[] bytes = new byte[IDENTIFICATION.length]; byte[] bytes = byteProvider.readBytes(0, IDENTIFICATION.length);
file.seek(0);
file.read(bytes, 0, IDENTIFICATION.length);
return Arrays.equals(bytes, IDENTIFICATION); return Arrays.equals(bytes, IDENTIFICATION);
} }

View file

@ -16,9 +16,9 @@
package ghidra.app.util.bin.format.pdb2.pdbreader.msf; package ghidra.app.util.bin.format.pdb2.pdbreader.msf;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays; import java.util.Arrays;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -40,17 +40,15 @@ public class Msf700 extends AbstractMsf {
//============================================================================================== //==============================================================================================
/** /**
* Constructor * Constructor
* @param file the {@link RandomAccessFile} to process as a {@link Msf700} * @param byteProvider the ByteProvider providing bytes for the MSF
* @param filename name of {@code #file}
* @param monitor the TaskMonitor * @param monitor the TaskMonitor
* @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB
* @throws IOException upon file IO seek/read issues * @throws IOException upon file IO seek/read issues
* @throws PdbException upon unknown value for configuration * @throws PdbException upon unknown value for configuration
*/ */
public Msf700(RandomAccessFile file, String filename, TaskMonitor monitor, public Msf700(ByteProvider byteProvider, TaskMonitor monitor, PdbReaderOptions pdbOptions)
PdbReaderOptions pdbOptions)
throws IOException, PdbException { throws IOException, PdbException {
super(file, filename, monitor, pdbOptions); super(byteProvider, monitor, pdbOptions);
} }
//============================================================================================== //==============================================================================================
@ -107,14 +105,12 @@ public class Msf700 extends AbstractMsf {
//============================================================================================== //==============================================================================================
/** /**
* Static method used to detect the header that belongs to this class * Static method used to detect the header that belongs to this class
* @param file the RandomAccessFile to process as a {@link Msf700} * @param byteProvider the ByteProvider to process as a {@link Msf700}
* @return {@code true} if the header for this class is positively identified * @return {@code true} if the header for this class is positively identified
* @throws IOException upon file IO seek/read issues * @throws IOException upon file IO seek/read issues
*/ */
static boolean detected(RandomAccessFile file) throws IOException { static boolean detected(ByteProvider byteProvider) throws IOException {
byte[] bytes = new byte[IDENTIFICATION.length]; byte[] bytes = byteProvider.readBytes(0, IDENTIFICATION.length);
file.seek(0);
file.read(bytes, 0, IDENTIFICATION.length);
return Arrays.equals(bytes, IDENTIFICATION); return Arrays.equals(bytes, IDENTIFICATION);
} }

View file

@ -18,6 +18,8 @@ package ghidra.app.util.bin.format.pdb2.pdbreader.msf;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import ghidra.app.util.bin.ByteProvider;
/** /**
* This class is responsible for reading pages from a {@link RandomAccessFile} for the * This class is responsible for reading pages from a {@link RandomAccessFile} for the
* {@link Msf} class and its underlying classes. * {@link Msf} class and its underlying classes.
@ -27,7 +29,7 @@ class MsfFileReader implements AutoCloseable {
//============================================================================================== //==============================================================================================
// Internals // Internals
//============================================================================================== //==============================================================================================
private RandomAccessFile file; private ByteProvider byteProvider;
private Msf msf; private Msf msf;
//============================================================================================== //==============================================================================================
@ -39,8 +41,8 @@ class MsfFileReader implements AutoCloseable {
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (file != null) { if (byteProvider != null) {
file.close(); byteProvider.close();
} }
} }
@ -50,11 +52,11 @@ class MsfFileReader implements AutoCloseable {
/** /**
* Constructor * Constructor
* @param msf the {@link Msf} for which this class is to be associated * @param msf the {@link Msf} for which this class is to be associated
* @param file {@link RandomAccessFile} underlying this class * @param byteProvider the ByteProvider providing bytes for the MSF
*/ */
MsfFileReader(Msf msf, RandomAccessFile file) { MsfFileReader(Msf msf, ByteProvider byteProvider) {
this.msf = msf; this.msf = msf;
this.file = file; this.byteProvider = byteProvider;
} }
/** /**
@ -95,18 +97,13 @@ class MsfFileReader implements AutoCloseable {
// Fail if file does not contain enough pages for the read--boundary case that assumes // Fail if file does not contain enough pages for the read--boundary case that assumes
// everything beyond the offset in the file belongs to this read. // everything beyond the offset in the file belongs to this read.
if (Msf.floorDivisionWithLog2Divisor(offset + numToRead, if (Msf.floorDivisionWithLog2Divisor(offset + numToRead, msf.getLog2PageSize()) > msf
msf.getLog2PageSize()) > msf.getNumPages()) { .getNumPages()) {
throw new IOException("Invalid MSF configuration"); throw new IOException("Invalid MSF configuration");
} }
int numBytesRead = 0; System.arraycopy(byteProvider.readBytes(fileOffset, numToRead), 0, bytes, bytesOffset,
file.seek(fileOffset); numToRead);
numBytesRead = file.read(bytes, bytesOffset, numToRead);
if (numBytesRead != numToRead) {
throw new IOException("Could not read required bytes from MSF");
}
} }
} }

View file

@ -16,9 +16,9 @@
package ghidra.app.util.bin.format.pdb2.pdbreader.msf; package ghidra.app.util.bin.format.pdb2.pdbreader.msf;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Objects; import java.util.Objects;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException; import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbReaderOptions; import ghidra.app.util.bin.format.pdb2.pdbreader.PdbReaderOptions;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -33,7 +33,7 @@ public class MsfParser {
/** /**
* Detects, creates, and returns the appropriate {@link Msf} object found for * Detects, creates, and returns the appropriate {@link Msf} object found for
* the filename given * the filename given
* @param filename name of the file to process * @param byteProvider the ByteProvider providing bytes for the MSF
* @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB
* @param monitor {@link TaskMonitor} used for checking cancellation * @param monitor {@link TaskMonitor} used for checking cancellation
* @return derived {@link Msf} object * @return derived {@link Msf} object
@ -41,24 +41,23 @@ public class MsfParser {
* @throws PdbException if an appropriate object cannot be created * @throws PdbException if an appropriate object cannot be created
* @throws CancelledException upon user cancellation * @throws CancelledException upon user cancellation
*/ */
public static Msf parse(String filename, PdbReaderOptions pdbOptions, public static Msf parse(ByteProvider byteProvider, PdbReaderOptions pdbOptions,
TaskMonitor monitor) throws IOException, PdbException, CancelledException { TaskMonitor monitor) throws IOException, PdbException, CancelledException {
Objects.requireNonNull(filename, "filename cannot be null"); Objects.requireNonNull(byteProvider, "byteProvider cannot be null");
Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null"); Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null");
Objects.requireNonNull(monitor, "monitor cannot be null"); Objects.requireNonNull(monitor, "monitor cannot be null");
Msf msf; Msf msf;
RandomAccessFile file = new RandomAccessFile(filename, "r"); if (Msf200.detected(byteProvider)) {
if (Msf200.detected(file)) { msf = new Msf200(byteProvider, monitor, pdbOptions);
msf = new Msf200(file, filename, monitor, pdbOptions);
} }
else if (Msf700.detected(file)) { else if (Msf700.detected(byteProvider)) {
msf = new Msf700(file, filename, monitor, pdbOptions); msf = new Msf700(byteProvider, monitor, pdbOptions);
} }
else { else {
// Must close the file here. In cases where MSF is created, the MSF takes // Must close the ByteProvider here. In cases where MSF is created, the MSF takes
// responsibility for closing the file. // responsibility for closing the file.
file.close(); byteProvider.close();
throw new PdbException("MSF format not detected"); throw new PdbException("MSF format not detected");
} }
msf.deserialize(); msf.deserialize();

View file

@ -141,12 +141,11 @@ class LoadPdbTask extends Task {
pdbApplicatorOptions.setProcessingControl(control); pdbApplicatorOptions.setProcessingControl(control);
try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse( try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse(pdbFile,
pdbFile.getAbsolutePath(), pdbReaderOptions, monitor)) { pdbReaderOptions, monitor)) {
monitor.setMessage("PDB: Parsing " + pdbFile + "..."); monitor.setMessage("PDB: Parsing " + pdbFile + "...");
pdb.deserialize(); pdb.deserialize();
DefaultPdbApplicator applicator = DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb);
new DefaultPdbApplicator(pdb);
applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(), applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(),
pdbApplicatorOptions, log); pdbApplicatorOptions, log);

View file

@ -15,9 +15,8 @@
*/ */
package pdb; package pdb;
import java.util.List;
import java.io.*; import java.io.*;
import java.util.List;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -46,8 +45,7 @@ public class PdbUtils {
String extension = FilenameUtils.getExtension(file.getName()).toLowerCase(); String extension = FilenameUtils.getExtension(file.getName()).toLowerCase();
switch (extension) { switch (extension) {
case "pdb": case "pdb":
try (AbstractPdb pdb = try (AbstractPdb pdb = PdbParser.parse(file, new PdbReaderOptions(), monitor)) {
PdbParser.parse(file.getPath(), new PdbReaderOptions(), monitor)) {
PdbIdentifiers identifiers = pdb.getIdentifiers(); PdbIdentifiers identifiers = pdb.getIdentifiers();
return identifiers; return identifiers;
} }

View file

@ -15,17 +15,20 @@
*/ */
package ghidra.app.util.bin.format.pdb2.pdbreader.msf; package ghidra.app.util.bin.format.pdb2.pdbreader.msf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.fail;
import java.io.*; import java.io.*;
import java.nio.file.AccessMode;
import java.util.*; import java.util.*;
import org.junit.*; import org.junit.*;
import generic.test.AbstractGenericTest; import generic.test.AbstractGenericTest;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.FileByteProvider;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbByteWriter; import ghidra.app.util.bin.format.pdb2.pdbreader.PdbByteWriter;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbReaderOptions; import ghidra.app.util.bin.format.pdb2.pdbreader.PdbReaderOptions;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -109,8 +112,11 @@ public class MsfReaderUnitTest extends AbstractGenericTest {
//============================================================================================== //==============================================================================================
@Test @Test
public void testStreamFile200Header() { public void testStreamFile200Header() {
try (Msf streamFile = File file = new File(testFileName200);
MsfParser.parse(testFileName200, new PdbReaderOptions(), TaskMonitor.DUMMY)) { try (ByteProvider byteProvider = new FileByteProvider(file,
FileSystemService.getInstance().getLocalFSRL(file), AccessMode.READ);
Msf streamFile =
MsfParser.parse(byteProvider, new PdbReaderOptions(), TaskMonitor.DUMMY)) {
int numStreams = streamFile.getNumStreams(); int numStreams = streamFile.getNumStreams();
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("NumStreams: " + numStreams + "\n"); builder.append("NumStreams: " + numStreams + "\n");
@ -127,8 +133,11 @@ public class MsfReaderUnitTest extends AbstractGenericTest {
@Test @Test
public void testStreamFile700Header() { public void testStreamFile700Header() {
try (Msf streamFile = File file = new File(testFileName700);
MsfParser.parse(testFileName700, new PdbReaderOptions(), TaskMonitor.DUMMY)) { try (ByteProvider byteProvider = new FileByteProvider(file,
FileSystemService.getInstance().getLocalFSRL(file), AccessMode.READ);
Msf streamFile =
MsfParser.parse(byteProvider, new PdbReaderOptions(), TaskMonitor.DUMMY)) {
int numStreams = streamFile.getNumStreams(); int numStreams = streamFile.getNumStreams();
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("NumStreams: " + numStreams + "\n"); builder.append("NumStreams: " + numStreams + "\n");