From 4da04423bd50a524ef8ffc35dd07ce1d26cd2a50 Mon Sep 17 00:00:00 2001 From: ghizard <50744617+ghizard@users.noreply.github.com> Date: Sun, 26 Nov 2023 12:40:10 -0500 Subject: [PATCH] GP-4025 - PDB - Use ByteProvider in place of RandomAccessFile --- .../file/formats/dump/pagedump/Pagedump.java | 57 ++++++++----------- .../PdbDeveloperApplyDummyScript.java | 2 +- .../PdbDeveloperDumpScript.java | 2 +- .../PdbDeveloperDumpSetScript.java | 4 +- .../pdbquery/PdbFactory.java | 4 +- .../core/analysis/PdbUniversalAnalyzer.java | 2 +- .../bin/format/pdb2/pdbreader/PdbParser.java | 55 +++++++++++++++++- .../pdb2/pdbreader/msf/AbstractMsf.java | 16 +++--- .../bin/format/pdb2/pdbreader/msf/Msf200.java | 18 +++--- .../bin/format/pdb2/pdbreader/msf/Msf700.java | 18 +++--- .../pdb2/pdbreader/msf/MsfFileReader.java | 27 ++++----- .../format/pdb2/pdbreader/msf/MsfParser.java | 21 ++++--- .../PDB/src/main/java/pdb/LoadPdbTask.java | 7 +-- .../PDB/src/main/java/pdb/PdbUtils.java | 12 ++-- .../pdb2/pdbreader/msf/MsfReaderUnitTest.java | 21 +++++-- 15 files changed, 152 insertions(+), 114 deletions(-) diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/dump/pagedump/Pagedump.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/dump/pagedump/Pagedump.java index 11c0ce34d1..5abd3308a9 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/dump/pagedump/Pagedump.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/dump/pagedump/Pagedump.java @@ -113,12 +113,10 @@ public class Pagedump extends DumpFile { props.setString("Executable Format", PeLoader.PE_NAME); initManagerList(addins); - createBlocks = - OptionUtils.getBooleanOptionValue(CREATE_MEMORY_BLOCKS_OPTION_NAME, - options, CREATE_MEMORY_BLOCKS_OPTION_DEFAULT); - String pdbLocation = - OptionUtils.getOption(DEBUG_DATA_PATH_OPTION_NAME, options, - DEBUG_DATA_PATH_OPTION_DEFAULT); + createBlocks = OptionUtils.getBooleanOptionValue(CREATE_MEMORY_BLOCKS_OPTION_NAME, options, + CREATE_MEMORY_BLOCKS_OPTION_DEFAULT); + String pdbLocation = OptionUtils.getOption(DEBUG_DATA_PATH_OPTION_NAME, options, + DEBUG_DATA_PATH_OPTION_DEFAULT); if (!pdbLocation.equals("")) { loadKernelPDB(pdbLocation, monitor); } @@ -152,8 +150,7 @@ public class Pagedump extends DumpFile { data.add(new DumpData(hdrLen, dt)); data.add(new DumpData(full.getHeaderSize(), "Physical_Memory", 0)); offset = (int) full.getHeaderSize(); - addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, - offset - hdrLen); + addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, offset - hdrLen); if (createBlocks) { mapPages(monitor); } @@ -164,20 +161,17 @@ public class Pagedump extends DumpFile { break; } - addInteriorAddressObject("Unknown", offset, offset, - reader.length() - offset); + addInteriorAddressObject("Unknown", offset, offset, reader.length() - offset); break; case DUMP_TYPE_TRIAGE: triage = new TriageDump(reader, hdrLen); dt = triage.toDataType(); data.add(new DumpData(hdrLen, dt)); - addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, - triage.getSizeOfDump()); + addInteriorAddressObject("DumpHeader", hdrLen, hdrLen, triage.getSizeOfDump()); long next = hdrLen + triage.getSizeOfDump(); - addInteriorAddressObject("Unknown", next, - next, reader.length() - next); + addInteriorAddressObject("Unknown", next, next, reader.length() - next); buildKernelStructures(); break; @@ -199,12 +193,12 @@ public class Pagedump extends DumpFile { PdbReaderOptions readerOptions = new PdbReaderOptions(); PdbApplicatorOptions applicatorOptions = new PdbApplicatorOptions(); 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 + "..."); pdb.deserialize(); DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); - applicator.applyTo(program, dtm, program.getImageBase(), - applicatorOptions, (MessageLog) null); + applicator.applyTo(program, dtm, program.getImageBase(), applicatorOptions, + (MessageLog) null); } catch (PdbException | IOException | CancelledException e) { Msg.error(this, e.getMessage()); @@ -218,16 +212,16 @@ public class Pagedump extends DumpFile { long runLength = run.getPageCount() * PAGE_SIZE; boolean outOfBounds = runLength + total * PAGE_SIZE > reader.length(); long bound = (outOfBounds) ? (reader.length() - total * PAGE_SIZE) : runLength; - ArrayDataType adt = - new ArrayDataType(StructConverter.BYTE, (int) bound, 1); + ArrayDataType adt = new ArrayDataType(StructConverter.BYTE, (int) bound, 1); data.add(new DumpData(total * PAGE_SIZE, adt)); // NB: Not sure if or where to place these //addInteriorAddressObject(DumpFileLoader.LOCAL, total * PAGE_SIZE, // run.getBasePage() * PAGE_SIZE, run.getPageCount() * PAGE_SIZE); total += run.getPageCount(); - if (outOfBounds) + if (outOfBounds) { break; + } } } @@ -313,8 +307,7 @@ public class Pagedump extends DumpFile { } } uds.add(new ArrayDataType(udt, (int) count, udt.getLength()), - udt.getLength() * (int) count, - "UnloadedDrivers", null); + udt.getLength() * (int) count, "UnloadedDrivers", null); } data.add(new DumpData(offset, uds)); } @@ -325,8 +318,9 @@ public class Pagedump extends DumpFile { while (offset < end) { int len = reader.readInt(offset); data.add(new DumpData(offset, StructConverter.DWORD, "", false, false)); - if (len == 0 || len == 0xFFFFFFFF) + if (len == 0 || len == 0xFFFFFFFF) { break; + } offset += 4; DumpData dd = new DumpData(offset, new TerminatedUnicodeDataType(), "", false, false); dd.setSize(len * 2 + 2); @@ -348,8 +342,8 @@ public class Pagedump extends DumpFile { DataType db = null; for (int i = 0; i < triage.getDataBlocksCount(); i++) { TriageDataBlock tdb = new TriageDataBlock(reader, reader.getPointerIndex()); - addInteriorAddressObject(DumpFileLoader.MEMORY, tdb.getOffset(), - tdb.getAddress(), tdb.getSize()); + addInteriorAddressObject(DumpFileLoader.MEMORY, tdb.getOffset(), tdb.getAddress(), + tdb.getSize()); VA2fileOffset.put(tdb.getAddress(), tdb.getOffset()); db = tdb.toDataType(); } @@ -387,8 +381,7 @@ public class Pagedump extends DumpFile { if (namePtr != 0) { long fileOffset = virtualToRva(namePtr); String name = reader.readUnicodeString(fileOffset); - addExteriorAddressObject(name, 0, entry.getDllBase(), - entry.getSizeOfImage()); + addExteriorAddressObject(name, 0, entry.getDllBase(), entry.getSizeOfImage()); } next = entry.getList_Flink(); if (entryKeys.contains(next)) { @@ -464,10 +457,8 @@ public class Pagedump extends DumpFile { @Override public void analyze(TaskMonitor monitor) { - boolean analyzeEmbeddedObjects = - OptionUtils.getBooleanOptionValue(ANALYZE_EMBEDDED_OBJECTS_OPTION_NAME, - options, - ANALYZE_EMBEDDED_OBJECTS_OPTION_DEFAULT); + boolean analyzeEmbeddedObjects = OptionUtils.getBooleanOptionValue( + ANALYZE_EMBEDDED_OBJECTS_OPTION_NAME, options, ANALYZE_EMBEDDED_OBJECTS_OPTION_DEFAULT); if (analyzeEmbeddedObjects) { ModuleToPeHelper.queryModules(program, monitor); } @@ -566,7 +557,7 @@ public class Pagedump extends DumpFile { private boolean isValid(int flags) { return (flags & 0x1) > 0; } - + private void walkPages(int page, long va, int depth, boolean lp) throws IOException { long fileOffset = fileOffset(page); if (fileOffset < 0) { @@ -615,7 +606,7 @@ public class Pagedump extends DumpFile { * Get default Pagedump loader options. Includes * {@link #DEBUG_DATA_PATH_OPTION_NAME} plus default {@link DumpFile} options (see * {@link DumpFile#getDefaultOptions(DumpFileReader)}). - * + * * @param reader dump file reader * @return default collection of Pagedump loader options */ diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperApplyDummyScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperApplyDummyScript.java index 2b024d9a10..c7f9e3e136 100644 --- a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperApplyDummyScript.java +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperApplyDummyScript.java @@ -77,7 +77,7 @@ public class PdbDeveloperApplyDummyScript extends GhidraScript { memory.createUninitializedBlock(pdbFileName, program.getImageBase(), 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 + "..."); pdb.deserialize(); DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java index 7788966362..871a7269d7 100644 --- a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java @@ -62,7 +62,7 @@ public class PdbDeveloperDumpScript extends GhidraScript { String message = "Processing PDB Dump of: " + pdbFileName; monitor.setMessage(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(); FileWriter fileWriter = new FileWriter(dumpFile); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java index 20ab695836..cff8dfad11 100644 --- a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java @@ -84,8 +84,8 @@ public class PdbDeveloperDumpSetScript extends GhidraScript { for (IOEntry entry : entries) { monitor.checkCancelled(); println("Processing PDB Dump of: " + entry.input()); - try (AbstractPdb pdb = - PdbParser.parse(entry.input(), new PdbReaderOptions(), monitor)) { + File pdbFile = new File(entry.input()); + try (AbstractPdb pdb = PdbParser.parse(pdbFile, new PdbReaderOptions(), monitor)) { pdb.deserialize(); try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File(entry.output())))) { diff --git a/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java b/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java index ab8d6a694f..083ad02ef0 100644 --- a/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java +++ b/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java @@ -15,6 +15,7 @@ */ package pdbquery; +import java.io.File; import java.io.IOException; import java.util.*; import java.util.Map.Entry; @@ -59,7 +60,8 @@ public class PdbFactory { println(script, "Opening PDB: " + filename); 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(); pdb.deserialize(); PdbReaderMetrics metrics = pdb.getPdbReaderMetrics(); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java index c255bdfc95..2c34efeb76 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java @@ -178,7 +178,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { PdbLog.message(DESCRIPTION); 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 + "..."); pdb.deserialize(); DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbParser.java index ffbf026f26..1e1b8e4cf8 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbParser.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbParser.java @@ -15,11 +15,16 @@ */ package ghidra.app.util.bin.format.pdb2.pdbreader; +import java.io.File; import java.io.IOException; +import java.nio.file.AccessMode; 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.MsfParser; +import ghidra.formats.gfilesystem.FileSystemService; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -54,15 +59,63 @@ public class PdbParser { * @throws PdbException on parsing issues * @throws CancelledException upon user cancellation */ + @Deprecated public static AbstractPdb parse(String filename, PdbReaderOptions pdbOptions, TaskMonitor monitor) throws IOException, PdbException, CancelledException { Objects.requireNonNull(filename, "filename cannot be null"); Objects.requireNonNull(pdbOptions, "pdbOptions 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 // created below. - Msf msf = MsfParser.parse(filename, pdbOptions, monitor); + Msf msf = MsfParser.parse(byteProvider, pdbOptions, monitor); int versionNumber = AbstractPdb.deserializeVersionNumber(msf, monitor); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java index a45c8994e0..ceb16291ca 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/AbstractMsf.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.util.Objects; +import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -134,18 +135,16 @@ public abstract class AbstractMsf implements Msf { //============================================================================================== /** * Constructor - * @param file the {@link RandomAccessFile} to process for this class - * @param filename name of {@code #file} + * @param byteProvider the ByteProvider providing bytes for the MSF * @param monitor the TaskMonitor * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @throws IOException upon file IO seek/read issues * @throws PdbException upon unknown value for configuration */ - public AbstractMsf(RandomAccessFile file, String filename, TaskMonitor monitor, - PdbReaderOptions pdbOptions) + public AbstractMsf(ByteProvider byteProvider, TaskMonitor monitor, PdbReaderOptions pdbOptions) throws IOException, PdbException { - Objects.requireNonNull(file, "file may not be null"); - this.filename = Objects.requireNonNull(filename, "filename may not be null"); + Objects.requireNonNull(byteProvider, "ByteProvider may not be null"); + this.filename = byteProvider.getAbsolutePath(); this.monitor = TaskMonitor.dummyIfNull(monitor); this.pdbOptions = Objects.requireNonNull(pdbOptions, "PdbOptions may not be null"); // Do initial configuration with largest possible page size. ConfigureParameters will @@ -159,7 +158,7 @@ public abstract class AbstractMsf implements Msf { pageSize = 0x1000; configureParameters(); // Create components. - fileReader = new MsfFileReader(this, file); + fileReader = new MsfFileReader(this, byteProvider); create(); } @@ -320,8 +319,7 @@ public abstract class AbstractMsf implements Msf { * @throws CancelledException upon user cancellation */ @Override - public void deserialize() - throws IOException, PdbException, CancelledException { + public void deserialize() throws IOException, PdbException, CancelledException { byte[] bytes = new byte[getPageSize()]; fileReader.read(getHeaderPageNumber(), 0, getPageSize(), bytes, 0); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf200.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf200.java index 44cf3c2fc3..5322241c67 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf200.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf200.java @@ -16,9 +16,9 @@ package ghidra.app.util.bin.format.pdb2.pdbreader.msf; import java.io.IOException; -import java.io.RandomAccessFile; import java.util.Arrays; +import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.util.task.TaskMonitor; @@ -41,17 +41,15 @@ public class Msf200 extends AbstractMsf { //============================================================================================== /** * Constructor - * @param file the {@link RandomAccessFile} to process as a {@link Msf200} - * @param filename name of {@code #file} + * @param byteProvider the ByteProvider providing bytes for the MSF * @param monitor the TaskMonitor * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @throws IOException upon file IO seek/read issues * @throws PdbException upon unknown value for configuration */ - public Msf200(RandomAccessFile file, String filename, TaskMonitor monitor, - PdbReaderOptions pdbOptions) + public Msf200(ByteProvider byteProvider, TaskMonitor monitor, PdbReaderOptions pdbOptions) 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 - * @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 * @throws IOException upon file IO seek/read issues */ - static boolean detected(RandomAccessFile file) throws IOException { - byte[] bytes = new byte[IDENTIFICATION.length]; - file.seek(0); - file.read(bytes, 0, IDENTIFICATION.length); + static boolean detected(ByteProvider byteProvider) throws IOException { + byte[] bytes = byteProvider.readBytes(0, IDENTIFICATION.length); return Arrays.equals(bytes, IDENTIFICATION); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf700.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf700.java index 8d3e745651..76ef0b7d3e 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf700.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/Msf700.java @@ -16,9 +16,9 @@ package ghidra.app.util.bin.format.pdb2.pdbreader.msf; import java.io.IOException; -import java.io.RandomAccessFile; import java.util.Arrays; +import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.util.task.TaskMonitor; @@ -40,17 +40,15 @@ public class Msf700 extends AbstractMsf { //============================================================================================== /** * Constructor - * @param file the {@link RandomAccessFile} to process as a {@link Msf700} - * @param filename name of {@code #file} + * @param byteProvider the ByteProvider providing bytes for the MSF * @param monitor the TaskMonitor * @param pdbOptions {@link PdbReaderOptions} used for processing the PDB * @throws IOException upon file IO seek/read issues * @throws PdbException upon unknown value for configuration */ - public Msf700(RandomAccessFile file, String filename, TaskMonitor monitor, - PdbReaderOptions pdbOptions) + public Msf700(ByteProvider byteProvider, TaskMonitor monitor, PdbReaderOptions pdbOptions) 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 - * @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 * @throws IOException upon file IO seek/read issues */ - static boolean detected(RandomAccessFile file) throws IOException { - byte[] bytes = new byte[IDENTIFICATION.length]; - file.seek(0); - file.read(bytes, 0, IDENTIFICATION.length); + static boolean detected(ByteProvider byteProvider) throws IOException { + byte[] bytes = byteProvider.readBytes(0, IDENTIFICATION.length); return Arrays.equals(bytes, IDENTIFICATION); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfFileReader.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfFileReader.java index 372e4b434e..adade2441c 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfFileReader.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfFileReader.java @@ -18,6 +18,8 @@ package ghidra.app.util.bin.format.pdb2.pdbreader.msf; import java.io.IOException; import java.io.RandomAccessFile; +import ghidra.app.util.bin.ByteProvider; + /** * This class is responsible for reading pages from a {@link RandomAccessFile} for the * {@link Msf} class and its underlying classes. @@ -27,7 +29,7 @@ class MsfFileReader implements AutoCloseable { //============================================================================================== // Internals //============================================================================================== - private RandomAccessFile file; + private ByteProvider byteProvider; private Msf msf; //============================================================================================== @@ -39,8 +41,8 @@ class MsfFileReader implements AutoCloseable { */ @Override public void close() throws IOException { - if (file != null) { - file.close(); + if (byteProvider != null) { + byteProvider.close(); } } @@ -50,11 +52,11 @@ class MsfFileReader implements AutoCloseable { /** * Constructor * @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.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 // everything beyond the offset in the file belongs to this read. - if (Msf.floorDivisionWithLog2Divisor(offset + numToRead, - msf.getLog2PageSize()) > msf.getNumPages()) { + if (Msf.floorDivisionWithLog2Divisor(offset + numToRead, msf.getLog2PageSize()) > msf + .getNumPages()) { throw new IOException("Invalid MSF configuration"); } - int numBytesRead = 0; - file.seek(fileOffset); - numBytesRead = file.read(bytes, bytesOffset, numToRead); - - if (numBytesRead != numToRead) { - throw new IOException("Could not read required bytes from MSF"); - } + System.arraycopy(byteProvider.readBytes(fileOffset, numToRead), 0, bytes, bytesOffset, + numToRead); } } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfParser.java index 0c71410093..dca4ebeede 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfParser.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfParser.java @@ -16,9 +16,9 @@ package ghidra.app.util.bin.format.pdb2.pdbreader.msf; import java.io.IOException; -import java.io.RandomAccessFile; 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.PdbReaderOptions; import ghidra.util.exception.CancelledException; @@ -33,7 +33,7 @@ public class MsfParser { /** * Detects, creates, and returns the appropriate {@link Msf} object found for * 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 monitor {@link TaskMonitor} used for checking cancellation * @return derived {@link Msf} object @@ -41,24 +41,23 @@ public class MsfParser { * @throws PdbException if an appropriate object cannot be created * @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 { - Objects.requireNonNull(filename, "filename cannot be null"); + Objects.requireNonNull(byteProvider, "byteProvider cannot be null"); Objects.requireNonNull(pdbOptions, "pdbOptions cannot be null"); Objects.requireNonNull(monitor, "monitor cannot be null"); Msf msf; - RandomAccessFile file = new RandomAccessFile(filename, "r"); - if (Msf200.detected(file)) { - msf = new Msf200(file, filename, monitor, pdbOptions); + if (Msf200.detected(byteProvider)) { + msf = new Msf200(byteProvider, monitor, pdbOptions); } - else if (Msf700.detected(file)) { - msf = new Msf700(file, filename, monitor, pdbOptions); + else if (Msf700.detected(byteProvider)) { + msf = new Msf700(byteProvider, monitor, pdbOptions); } 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. - file.close(); + byteProvider.close(); throw new PdbException("MSF format not detected"); } msf.deserialize(); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java index 55855951e2..5ea66e8d4f 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java @@ -141,12 +141,11 @@ class LoadPdbTask extends Task { pdbApplicatorOptions.setProcessingControl(control); - try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse( - pdbFile.getAbsolutePath(), pdbReaderOptions, monitor)) { + try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse(pdbFile, + pdbReaderOptions, monitor)) { monitor.setMessage("PDB: Parsing " + pdbFile + "..."); pdb.deserialize(); - DefaultPdbApplicator applicator = - new DefaultPdbApplicator(pdb); + DefaultPdbApplicator applicator = new DefaultPdbApplicator(pdb); applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(), pdbApplicatorOptions, log); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java index 0d6548dfd2..d7ae121cb8 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java @@ -15,9 +15,8 @@ */ package pdb; -import java.util.List; - import java.io.*; +import java.util.List; import org.apache.commons.io.FilenameUtils; import org.xml.sax.SAXException; @@ -36,7 +35,7 @@ public class PdbUtils { * Attempts to extract {@link PdbIdentifiers} from the specified file, which * can be either a pdb or pdb.xml file. *

- * + * * @param file File to examine * @param monitor {@link TaskMonitor}to allow cancel and progress * @return new {@link PdbIdentifiers} instance with GUID/ID and age info, or null if @@ -46,8 +45,7 @@ public class PdbUtils { String extension = FilenameUtils.getExtension(file.getName()).toLowerCase(); switch (extension) { case "pdb": - try (AbstractPdb pdb = - PdbParser.parse(file.getPath(), new PdbReaderOptions(), monitor)) { + try (AbstractPdb pdb = PdbParser.parse(file, new PdbReaderOptions(), monitor)) { PdbIdentifiers identifiers = pdb.getIdentifiers(); return identifiers; } @@ -87,9 +85,9 @@ public class PdbUtils { /** * Extracts a singleton file from a cab file that only has 1 file - * + * * @param cabFile Compressed cab file that only has 1 file embedded in it - * @param destFile where to write the extracted file to + * @param destFile where to write the extracted file to * @param monitor {@link TaskMonitor} to allow canceling * @return original name of the file * @throws CancelledException if cancelled diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfReaderUnitTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfReaderUnitTest.java index 79fb1b4030..f51e00a909 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfReaderUnitTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/msf/MsfReaderUnitTest.java @@ -15,17 +15,20 @@ */ package ghidra.app.util.bin.format.pdb2.pdbreader.msf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import java.io.*; +import java.nio.file.AccessMode; import java.util.*; import org.junit.*; 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.PdbReaderOptions; +import ghidra.formats.gfilesystem.FileSystemService; import ghidra.util.Msg; import ghidra.util.exception.AssertException; import ghidra.util.task.TaskMonitor; @@ -109,8 +112,11 @@ public class MsfReaderUnitTest extends AbstractGenericTest { //============================================================================================== @Test public void testStreamFile200Header() { - try (Msf streamFile = - MsfParser.parse(testFileName200, new PdbReaderOptions(), TaskMonitor.DUMMY)) { + File file = new File(testFileName200); + 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(); StringBuilder builder = new StringBuilder(); builder.append("NumStreams: " + numStreams + "\n"); @@ -127,8 +133,11 @@ public class MsfReaderUnitTest extends AbstractGenericTest { @Test public void testStreamFile700Header() { - try (Msf streamFile = - MsfParser.parse(testFileName700, new PdbReaderOptions(), TaskMonitor.DUMMY)) { + File file = new File(testFileName700); + 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(); StringBuilder builder = new StringBuilder(); builder.append("NumStreams: " + numStreams + "\n");