GP-4625 - Modify dev PDB Dump script and add scripts to dump PDB mangled names; realign mangled name for complex type hierarchy

This commit is contained in:
ghizard 2024-05-24 07:55:28 -04:00
parent a29a59488a
commit cb3a6ced93
7 changed files with 322 additions and 36 deletions

View File

@ -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");
}
}
}
}

View File

@ -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");
}
}
}
}
}

View File

@ -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);

View File

@ -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.
@ -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.

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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;
}
}