From cb3a6ced93a67afe3ad81bafefa53f6950866155 Mon Sep 17 00:00:00 2001 From: ghizard <50744617+ghizard@users.noreply.github.com> Date: Fri, 24 May 2024 07:55:28 -0400 Subject: [PATCH] GP-4625 - Modify dev PDB Dump script and add scripts to dump PDB mangled names; realign mangled name for complex type hierarchy --- ...DeveloperDumpMangledSymbolNamesScript.java | 166 ++++++++++++++++++ ...dbDeveloperDumpMangledTypeNamesScript.java | 141 +++++++++++++++ .../PdbDeveloperDumpScript.java | 10 +- .../pdbreader/type/AbstractComplexMsType.java | 12 +- .../type/AbstractCompositeMsType.java | 9 - .../pdb2/pdbreader/type/Enum19MsType.java | 10 -- .../pdb2/pdbreader/type/EnumMsType.java | 10 -- 7 files changed, 322 insertions(+), 36 deletions(-) create mode 100644 Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledSymbolNamesScript.java create mode 100644 Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledTypeNamesScript.java diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledSymbolNamesScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledSymbolNamesScript.java new file mode 100644 index 0000000000..18da61d449 --- /dev/null +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledSymbolNamesScript.java @@ -0,0 +1,166 @@ +/* ### + * 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. + */ +// Dump PDB mangled symbol names for PDB developer use +// +//@category PDB + +import java.io.*; + +import docking.widgets.values.GValuesMap; +import ghidra.app.script.GhidraScript; +import ghidra.app.util.bin.format.pdb2.pdbreader.*; +import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*; +import ghidra.app.util.pdb.pdbapplicator.SymbolGroup; +import ghidra.features.base.values.GhidraValuesMap; +import ghidra.util.*; +import ghidra.util.exception.CancelledException; + +public class PdbDeveloperDumpMangledSymbolNamesScript extends GhidraScript { + + private static final String TITLE = "Dump PDB Mangled Symbol Names"; + private static final String PDB_PROMPT = "Choose a PDB file"; + private static final String OUTPUT_PROMPT = "Choose an output file"; + + private static boolean validatePdb(GValuesMap valueMap, StatusListener status) { + File file = valueMap.getFile(PDB_PROMPT); + if (file == null) { + status.setStatusText("PDB file must be selected.", MessageType.ERROR); + return false; + } + if (!file.exists()) { + status.setStatusText(file.getAbsolutePath() + " is not a valid file.", + MessageType.ERROR); + return false; + } + String fileName = file.getAbsolutePath(); + if (!fileName.endsWith(".pdb") && !fileName.endsWith(".PDB")) { + status.setStatusText("Expected .pdb file extenstion (got '" + fileName + "').", + MessageType.ERROR); + return false; + } + // We do not need to check the existence of an image base because we provide a default + // value + return true; + } + + private static boolean validateOutput(GValuesMap valueMap, StatusListener status) { + File file = valueMap.getFile(OUTPUT_PROMPT); + // File will exist, as we supplied a default value + String fileName = file.getAbsolutePath(); + if (fileName.endsWith(".pdb") || fileName.endsWith(".PDB")) { + status.setStatusText("Output file may not end with .pdb (got '" + fileName + "').", + MessageType.ERROR); + return false; + } + return true; + } + + @Override + protected void run() throws Exception { + + GhidraValuesMap values = new GhidraValuesMap(); + + values.defineFile(PDB_PROMPT, null); + values.setValidator((valueMap, status) -> { + return validatePdb(valueMap, status); + }); + values = askValues(TITLE, null, values); + File pdbFile = values.getFile(PDB_PROMPT); + String pdbFileName = pdbFile.getAbsolutePath(); + + // creating a default output and asking again; PDB file should retain its current value + // from above + String outputFileName = pdbFileName + ".MangledSymbolNames.txt"; + values.defineFile(OUTPUT_PROMPT, new File(outputFileName)); + values.setValidator((valueMap, status) -> { + return validatePdb(valueMap, status) && validateOutput(valueMap, status); + }); + values = askValues(TITLE, null, values); + pdbFile = values.getFile(PDB_PROMPT); // might have changed + pdbFileName = pdbFile.getAbsolutePath(); // might have changed + File dumpFile = values.getFile(OUTPUT_PROMPT); + + if (dumpFile.exists()) { + if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) { + Msg.info(this, "Operation canceled"); + return; + } + } + + String message = "Processing PDB Dump of: " + pdbFileName; + monitor.setMessage(message); + Msg.info(this, message); + try (AbstractPdb pdb = PdbParser.parse(pdbFile, new PdbReaderOptions(), monitor)) { + pdb.deserialize(); + FileWriter fileWriter = new FileWriter(dumpFile); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + dumpMangledSymbolNames(pdb, bufferedWriter); + bufferedWriter.close(); + } + catch (IOException ioe) { + Msg.info(this, ioe.getMessage()); + popup(ioe.getMessage()); + } + message = "Results located in: " + dumpFile.getAbsoluteFile(); + monitor.setMessage(message); + Msg.info(this, message); + } + + private void dumpMangledSymbolNames(AbstractPdb pdb, Writer myWriter) + throws PdbException, CancelledException, IOException { + + PdbDebugInfo debugInfo = pdb.getDebugInfo(); + if (debugInfo == null) { + return; + } + + int num = debugInfo.getNumModules(); + for (int moduleNumber = 0; moduleNumber <= num; moduleNumber++) { + monitor.checkCancelled(); + SymbolGroup symbolGroup = new SymbolGroup(pdb, moduleNumber); + MsSymbolIterator iter = symbolGroup.getSymbolIterator(); + dumpIteratedMangledSymbolNames(iter, myWriter); + } + } + + private void dumpIteratedMangledSymbolNames(MsSymbolIterator iter, Writer myWriter) + throws CancelledException, IOException { + while (iter.hasNext()) { + monitor.checkCancelled(); + AbstractMsSymbol symbol = iter.next(); + if (symbol == null) { + throw new AssertionError("null symbol"); + } + if (!(symbol instanceof NameMsSymbol s)) { + continue; + } + if (!(symbol instanceof AbstractDataMsSymbol || + symbol instanceof AbstractProcedureMsSymbol || + symbol instanceof AbstractUserDefinedTypeMsSymbol || + symbol instanceof AbstractPublicMsSymbol)) { + continue; + } + + String name = s.getName(); + if (name.contains("?") || name.contains("@") || name.contains(".")) { + myWriter.write(name); + myWriter.write("\n"); + } + + } + } + +} diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledTypeNamesScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledTypeNamesScript.java new file mode 100644 index 0000000000..13516242ac --- /dev/null +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpMangledTypeNamesScript.java @@ -0,0 +1,141 @@ +/* ### + * 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. + */ +// Dump PDB mangled type names for PDB developer use +// +//@category PDB + +import java.io.*; + +import docking.widgets.values.GValuesMap; +import ghidra.app.script.GhidraScript; +import ghidra.app.util.bin.format.pdb2.pdbreader.*; +import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractComplexMsType; +import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType; +import ghidra.features.base.values.GhidraValuesMap; +import ghidra.util.*; +import ghidra.util.exception.CancelledException; + +public class PdbDeveloperDumpMangledTypeNamesScript extends GhidraScript { + + private static final String TITLE = "Dump PDB Mangled Type Names"; + private static final String PDB_PROMPT = "Choose a PDB file"; + private static final String OUTPUT_PROMPT = "Choose an output file"; + + private static boolean validatePdb(GValuesMap valueMap, StatusListener status) { + File file = valueMap.getFile(PDB_PROMPT); + if (file == null) { + status.setStatusText("PDB file must be selected.", MessageType.ERROR); + return false; + } + if (!file.exists()) { + status.setStatusText(file.getAbsolutePath() + " is not a valid file.", + MessageType.ERROR); + return false; + } + String fileName = file.getAbsolutePath(); + if (!fileName.endsWith(".pdb") && !fileName.endsWith(".PDB")) { + status.setStatusText("Expected .pdb file extenstion (got '" + fileName + "').", + MessageType.ERROR); + return false; + } + // We do not need to check the existence of an image base because we provide a default + // value + return true; + } + + private static boolean validateOutput(GValuesMap valueMap, StatusListener status) { + File file = valueMap.getFile(OUTPUT_PROMPT); + // File will exist, as we supplied a default value + String fileName = file.getAbsolutePath(); + if (fileName.endsWith(".pdb") || fileName.endsWith(".PDB")) { + status.setStatusText("Output file may not end with .pdb (got '" + fileName + "').", + MessageType.ERROR); + return false; + } + return true; + } + + @Override + protected void run() throws Exception { + + GhidraValuesMap values = new GhidraValuesMap(); + + values.defineFile(PDB_PROMPT, null); + values.setValidator((valueMap, status) -> { + return validatePdb(valueMap, status); + }); + values = askValues(TITLE, null, values); + File pdbFile = values.getFile(PDB_PROMPT); + String pdbFileName = pdbFile.getAbsolutePath(); + + // creating a default output and asking again; PDB file should retain its current value + // from above + String outputFileName = pdbFileName + ".MangledTypeNames.txt"; + values.defineFile(OUTPUT_PROMPT, new File(outputFileName)); + values.setValidator((valueMap, status) -> { + return validatePdb(valueMap, status) && validateOutput(valueMap, status); + }); + values = askValues(TITLE, null, values); + pdbFile = values.getFile(PDB_PROMPT); // might have changed + pdbFileName = pdbFile.getAbsolutePath(); // might have changed + File dumpFile = values.getFile(OUTPUT_PROMPT); + + if (dumpFile.exists()) { + if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) { + Msg.info(this, "Operation canceled"); + return; + } + } + + String message = "Processing PDB Dump of: " + pdbFileName; + Msg.info(this, message); + try (AbstractPdb pdb = PdbParser.parse(pdbFile, new PdbReaderOptions(), monitor)) { + pdb.deserialize(); + FileWriter fileWriter = new FileWriter(dumpFile); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + dumpMangledTypeNames(pdb, bufferedWriter); + bufferedWriter.close(); + } + catch (IOException ioe) { + Msg.info(this, ioe.getMessage()); + popup(ioe.getMessage()); + } + message = "Results located in: " + dumpFile.getAbsoluteFile(); + Msg.info(this, message); + } + + private void dumpMangledTypeNames(AbstractPdb pdb, Writer myWriter) + throws CancelledException, IOException { + TypeProgramInterface tpi = pdb.getTypeProgramInterface(); + if (tpi == null) { + return; + } + for (int indexNumber = tpi.getTypeIndexMin(); indexNumber < tpi + .getTypeIndexMaxExclusive(); indexNumber++) { + monitor.checkCancelled(); + RecordNumber recordNumber = RecordNumber.typeRecordNumber(indexNumber); + AbstractMsType msType = pdb.getTypeRecord(recordNumber); + if (msType instanceof AbstractComplexMsType type) { + String mangled = type.getMangledName(); + if (mangled != null) { + myWriter.write(mangled); + myWriter.write("\n"); + } + } + } + } + +} diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java index de665ea6c9..7f5235ea0d 100644 --- a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java +++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java @@ -27,6 +27,7 @@ import ghidra.util.*; public class PdbDeveloperDumpScript extends GhidraScript { + private static final String TITLE = "PDB Dump"; private static final String PDB_PROMPT = "Choose a PDB file"; private static final String OUTPUT_PROMPT = "Choose an output file"; @@ -73,7 +74,7 @@ public class PdbDeveloperDumpScript extends GhidraScript { values.setValidator((valueMap, status) -> { return validatePdb(valueMap, status); }); - values = askValues("Enter Values", null, values); + values = askValues(TITLE, null, values); File pdbFile = values.getFile(PDB_PROMPT); String pdbFileName = pdbFile.getAbsolutePath(); @@ -82,12 +83,9 @@ public class PdbDeveloperDumpScript extends GhidraScript { String outputFileName = pdbFileName + ".txt"; values.defineFile(OUTPUT_PROMPT, new File(outputFileName)); values.setValidator((valueMap, status) -> { - if (!validatePdb(valueMap, status)) { - return false; - } - return validateOutput(valueMap, status); + return validatePdb(valueMap, status) && validateOutput(valueMap, status); }); - values = askValues("Enter Values", null, values); + values = askValues(TITLE, null, values); pdbFile = values.getFile(PDB_PROMPT); // might have changed pdbFileName = pdbFile.getAbsolutePath(); // might have changed File dumpFile = values.getFile(OUTPUT_PROMPT); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractComplexMsType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractComplexMsType.java index bf5b41736f..26cf1100a2 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractComplexMsType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractComplexMsType.java @@ -32,6 +32,8 @@ public abstract class AbstractComplexMsType extends AbstractMsType { protected RecordNumber fieldDescriptorListRecordNumber; protected MsProperty property; protected String name; + // Used by MsType and 19MsType; maybe by StMsType; not by 16MsType + protected String mangledName; /** * Constructor for this type. @@ -46,7 +48,7 @@ public abstract class AbstractComplexMsType extends AbstractMsType { * Constructor for this type. * @param pdb {@link AbstractPdb} to which this type belongs. * @param count number of field elements - * @param fieldDescriptorListRecordNumber {@link RecordNumber} of field list + * @param fieldDescriptorListRecordNumber {@link RecordNumber} of field list * @param property {@link MsProperty} of this type * @param name the name of this type */ @@ -100,6 +102,14 @@ public abstract class AbstractComplexMsType extends AbstractMsType { return name; } + /** + * Returns the mangled name within this complex type + * @return Mangled name + */ + public String getMangledName() { + return mangledName; + } + /** * Returns the type name of this complex type. * @return Type of the complex type. diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractCompositeMsType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractCompositeMsType.java index 21f3915864..060455156e 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractCompositeMsType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/AbstractCompositeMsType.java @@ -31,7 +31,6 @@ public abstract class AbstractCompositeMsType extends AbstractComplexMsType { protected RecordNumber vShapeTableRecordNumber; // Not used by union. //TODO: has more... guessing below protected BigInteger size; - protected String mangledName; // Used by MsType (not used by 16MsType or StMsType?) /** * Constructor for this type. @@ -69,14 +68,6 @@ public abstract class AbstractCompositeMsType extends AbstractComplexMsType { this.vShapeTableRecordNumber = vShapeTableRecordNumber; } - /** - * Returns the mangled name within this composite. - * @return Mangled name. - */ - public String getMangledName() { - return mangledName; - } - /** * Returns the record number of the derived-from list of types. * @return Record number of the derived-from list of types. diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/Enum19MsType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/Enum19MsType.java index 189939ea76..ec48b5dcca 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/Enum19MsType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/Enum19MsType.java @@ -37,8 +37,6 @@ public class Enum19MsType extends AbstractEnumMsType { public static final int PDB_ID = 0x160b; - protected String mangledName; - /** * Constructor for this type. * @param pdb {@link AbstractPdb} to which this type belongs. @@ -72,12 +70,4 @@ public class Enum19MsType extends AbstractEnumMsType { return PDB_ID; } - /** - * Returns the mangled name field - * @return Mangled name. - */ - public String getMangledName() { - return mangledName; - } - } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/EnumMsType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/EnumMsType.java index 38d94e3852..3ff6a0c543 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/EnumMsType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/EnumMsType.java @@ -27,8 +27,6 @@ public class EnumMsType extends AbstractEnumMsType { public static final int PDB_ID = 0x1507; - protected String mangledName; - /** * Constructor for this type. * @param pdb {@link AbstractPdb} to which this type belongs. @@ -62,12 +60,4 @@ public class EnumMsType extends AbstractEnumMsType { return PDB_ID; } - /** - * Returns the mangled name field - * @return Mangled name. - */ - public String getMangledName() { - return mangledName; - } - }