diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java new file mode 100644 index 0000000000..c202248c7c --- /dev/null +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpSetScript.java @@ -0,0 +1,113 @@ +/* ### + * 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. + */ +// Developer Script to dump PDB information from a set of PDB files to output files. +// User selects an input file in which each line of text is a tab-separated input/output pair +// of PDB file and output text file. Blank lines and lines beginning with # are ignored. Example: +// myFile1.pdb myFile1.pdb.txt +// myFile2.pdb myFile2.pdb.txt +// +//@category PDB + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.script.GhidraScript; +import ghidra.app.util.bin.format.pdb2.pdbreader.*; +import ghidra.util.exception.CancelledException; + +public class PdbDeveloperDumpSetScript extends GhidraScript { + + private record IOEntry(String input, String output) {}; + + @Override + protected void run() throws Exception { + File controlFile = askFile( + "Choose control file with a tab-separated PDB-input/text-output pair per line", "OK"); + if (controlFile == null) { + printerr("Canceled execution: no input file"); + return; + } + + List entries = parseControlFile(controlFile); + if (entries == null) { + return; + } + + dumpFiles(entries); + } + + private List parseControlFile(File controlFile) throws IOException { + List entries = new ArrayList<>(); + + try (BufferedReader bufferedReader = new BufferedReader(new FileReader(controlFile))) { + String line; + int lineNumber = 0; + while ((line = bufferedReader.readLine()) != null) { + lineNumber++; + if (line.isBlank() || line.trim().startsWith("#")) { + continue; + } + String[] parts = line.split("\\t"); + if (parts.length != 2) { + printerr("Canceled execution (no files processed): control file, line " + + lineNumber + " does not contain a valid, tab-delimited pair: \"" + line + + "\""); + return null; + } + File inputFile = new File(parts[0]); + if (!inputFile.exists()) { + printerr("Canceled execution (no files processed): control file, line " + + lineNumber + ", input file does not exist: " + parts[0]); + return null; + } + entries.add(new IOEntry(parts[0], parts[1])); + } + } + return entries; + } + + private void dumpFiles(List entries) throws CancelledException, PdbException { + for (IOEntry entry : entries) { + monitor.checkCanceled(); + println("Processing PDB Dump of: " + entry.input()); + try (AbstractPdb pdb = + PdbParser.parse(entry.input(), new PdbReaderOptions(), monitor)) { + pdb.deserialize(monitor); + try (BufferedWriter bufferedWriter = + new BufferedWriter(new FileWriter(new File(entry.output())))) { + outputHeaderMessage(bufferedWriter, entry.input()); + pdb.dumpDirectory(bufferedWriter); + pdb.dumpSubStreams(bufferedWriter); + println("Results located in: " + entry.output()); + } + catch (IOException ioe) { + printerr("Error writing output file: " + ioe.getMessage()); + } + } + catch (IOException ioe) { + printerr("Error processing PDB file: " + ioe.getMessage()); + } + } + } + + private void outputHeaderMessage(BufferedWriter bufferedWriter, String name) + throws IOException { + bufferedWriter.append(getClass().getSimpleName() + " dump of: " + name + + "\nWARNING: FORMAT SUBJECT TO CHANGE WITHOUT NOTICE--DO NOT PARSE\n\n"); + } + +}