GP-3293 - Added RTTI Analyzer option to the program information called 'RTTI Found' and use it to determine whether to rerun and also when deciding whether to run the RTTI script.

This commit is contained in:
ghidra007 2023-04-14 18:57:55 +00:00
parent 75a185aa9e
commit d8cdcfb068
3 changed files with 161 additions and 97 deletions

View file

@ -78,6 +78,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.service.graph.*; import ghidra.service.graph.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.GraphException; import ghidra.util.exception.GraphException;
@ -85,6 +86,8 @@ import ghidra.util.task.TaskMonitor;
public class RecoverClassesFromRTTIScript extends GhidraScript { public class RecoverClassesFromRTTIScript extends GhidraScript {
public static final String RTTI_FOUND_OPTION = "RTTI Found";
// print c-like class definitions to the console // print c-like class definitions to the console
private static final boolean PRINT_CLASS_DEFINITIONS = false; private static final boolean PRINT_CLASS_DEFINITIONS = false;
@ -138,7 +141,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
private static final String INDETERMINATE_BOOKMARK = "INDETERMINATE"; private static final String INDETERMINATE_BOOKMARK = "INDETERMINATE";
boolean programHasRTTIApplied = false;
boolean hasDebugSymbols; boolean hasDebugSymbols;
boolean isGcc = false; boolean isGcc = false;
boolean isWindows = false; boolean isWindows = false;
@ -166,6 +168,23 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (isWindows()) { if (isWindows()) {
if (!isRttiAnalyzed()) {
println("Running the RTTIAnalyzer...");
analysisMode = AnalysisMode.ENABLED;
runScript("RunRttiAnalyzerScript.java");
analysisMode = AnalysisMode.SUSPENDED;
if (!isRttiAnalyzed()) {
println("The RTTI Analyzer did not complete successfully.");
return;
}
if (!hasRtti()) {
println("This program does not contain RTTI.");
return;
}
}
hasDebugSymbols = isPDBLoadedInProgram(); hasDebugSymbols = isPDBLoadedInProgram();
nameVfunctions = !hasDebugSymbols; nameVfunctions = !hasDebugSymbols;
recoverClassesFromRTTI = recoverClassesFromRTTI =
@ -176,7 +195,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
else if (isGcc()) { else if (isGcc()) {
boolean runGcc = askYesNo("GCC Class Recovery Still Under Development", boolean runGcc = askYesNo("GCC Class Recovery Still Under Development",
"I understand that gcc class recovery is still under development and my results will be incomplete but want to run this anyway."); "I understand that gcc class recovery is still under development and my results " +
"will be incomplete but want to run this anyway.");
if (!runGcc) { if (!runGcc) {
return; return;
} }
@ -187,7 +207,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
hasDebugSymbols = isDwarfLoadedInProgram(); hasDebugSymbols = isDwarfLoadedInProgram();
if (hasDwarf() && !hasDebugSymbols) { if (hasDwarf() && !hasDebugSymbols) {
println( println(
"The program contains DWARF but the DWARF analyzer has not been run. Please run the DWARF analyzer to get best results from this script."); "The program contains DWARF but the DWARF analyzer has not been run. Please " +
"run the DWARF analyzer to get best results from this script.");
return; return;
} }
nameVfunctions = !hasDebugSymbols; nameVfunctions = !hasDebugSymbols;
@ -203,7 +224,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (!recoverClassesFromRTTI.containsRTTI()) { if (!recoverClassesFromRTTI.containsRTTI()) {
println( println(
"This program does not appear to contain any processed RTTI information. Either it does not contain any or the RTTI Analyzer was not run."); "This program does not appear to contain any processed RTTI information. Either " +
"it does not contain any or the RTTI Analyzer was not run.");
return; return;
} }
@ -242,9 +264,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
"/" + RecoveredClassHelper.DTM_CLASS_DATA_FOLDER_NAME); "/" + RecoveredClassHelper.DTM_CLASS_DATA_FOLDER_NAME);
if (FIXUP_PROGRAM) { if (FIXUP_PROGRAM) {
println( println("Checking for missing RTTI information and undefined constructor/destructor " +
"Checking for missing RTTI information and undefined constructor/destructor functions and creating if possible " + "functions and creating if possible " + "to find entry point...");
"to find entry point...");
AddressSetView beforeScriptChanges = currentProgram.getChanges().getAddressSet(); AddressSetView beforeScriptChanges = currentProgram.getChanges().getAddressSet();
analysisMode = AnalysisMode.ENABLED; analysisMode = AnalysisMode.ENABLED;
@ -334,6 +355,15 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return ("There is no open program"); return ("There is no open program");
} }
if (!GhidraProgramUtilities.isAnalyzedFlagSet(currentProgram)) {
return ("The program has not been analyzed. Please run auto-analysis and make sure " +
"the RTTI analzer is one of the analyzers enabled.");
}
if (isRttiAnalyzed() && !hasRtti()) {
return ("This program does not contain RTTI.");
}
CategoryPath path = CategoryPath path =
new CategoryPath(CategoryPath.ROOT, RecoveredClassHelper.DTM_CLASS_DATA_FOLDER_NAME); new CategoryPath(CategoryPath.ROOT, RecoveredClassHelper.DTM_CLASS_DATA_FOLDER_NAME);
if (currentProgram.getDataTypeManager().containsCategory(path)) { if (currentProgram.getDataTypeManager().containsCategory(path)) {
@ -341,7 +371,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
if (!checkGhidraVersion()) { if (!checkGhidraVersion()) {
return ("This script only works with Ghidra version 9.2, 9.2.2 and later. It does not work on Ghidra 9.2.1 or on versions prior to 9.2"); return ("This script only works with Ghidra version 9.2, 9.2.2 and later. It does" +
" not work on Ghidra 9.2.1 or on versions prior to 9.2");
} }
if (!isGcc() && !isWindows()) { if (!isGcc() && !isWindows()) {
@ -491,8 +522,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
.defaultLayoutAlgorithm("Compact Hierarchical") .defaultLayoutAlgorithm("Compact Hierarchical")
.build(); .build();
display.setGraph(graph, graphOptions, display.setGraph(graph, graphOptions, "Recovered Classes Graph", false, TaskMonitor.DUMMY);
"Recovered Classes Graph", false, TaskMonitor.DUMMY);
} }
/** /**
@ -547,12 +577,10 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return false; return false;
} }
boolean isCompilerSpecGcc = boolean isCompilerSpecGcc = currentProgram.getCompilerSpec()
currentProgram.getCompilerSpec()
.getCompilerSpecID() .getCompilerSpecID()
.getIdAsString() .getIdAsString()
.equalsIgnoreCase( .equalsIgnoreCase("gcc");
"gcc");
if (isCompilerSpecGcc) { if (isCompilerSpecGcc) {
return true; return true;
} }
@ -571,8 +599,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
Address found = currentProgram.getMemory() Address found = currentProgram.getMemory()
.findBytes(commentBlock.getStart(), .findBytes(commentBlock.getStart(), commentBlock.getEnd(), gccBytes, maskBytes,
commentBlock.getEnd(), gccBytes, maskBytes, true, monitor); true, monitor);
if (found == null) { if (found == null) {
isGcc = false; isGcc = false;
} }
@ -607,19 +635,15 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
List<Function> allClassDestructors = List<Function> allClassDestructors =
recoverClassesFromRTTI.getAllClassDestructors(recoveredClass); recoverClassesFromRTTI.getAllClassDestructors(recoveredClass);
List<Function> commonFunctions1 = List<Function> commonFunctions1 = allClassConstructors.stream()
allClassConstructors.stream()
.distinct() .distinct()
.filter(allClassDestructors::contains) .filter(allClassDestructors::contains)
.collect( .collect(Collectors.toList());
Collectors.toList());
List<Function> commonFunctions2 = List<Function> commonFunctions2 = allClassDestructors.stream()
allClassDestructors.stream()
.distinct() .distinct()
.filter(allClassConstructors::contains) .filter(allClassConstructors::contains)
.collect( .collect(Collectors.toList());
Collectors.toList());
if (commonFunctions1.isEmpty() && commonFunctions2.isEmpty()) { if (commonFunctions1.isEmpty() && commonFunctions2.isEmpty()) {
return false; return false;
@ -638,6 +662,31 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
analyzer.added(currentProgram, set, monitor, new MessageLog()); analyzer.added(currentProgram, set, monitor, new MessageLog());
} }
/**
* If program has the "RTTI Found" option at all it means RTTI analyzer has been run and
* does not need to be run again. If program does not have the option it means either analyzer
* has not been run or it has been run by an older version (<10.3) so we don't know and should
* rerun to make sure.
* @return true if RTTI analyzer has definitely been run, false otherwise
*/
private boolean isRttiAnalyzed() {
Options programOptions = currentProgram.getOptions(Program.PROGRAM_INFO);
Boolean rttiAnalyzed = (Boolean) programOptions.getObject(RTTI_FOUND_OPTION, null);
if (rttiAnalyzed == null) {
return false;
}
return true;
}
/**
* Method to check to see if current program has RTTI based on option setting
* @return true if program contains RTTI and false if not
*/
private boolean hasRtti() {
Options programOptions = currentProgram.getOptions(Program.PROGRAM_INFO);
return programOptions.getBoolean(RTTI_FOUND_OPTION, false);
}
/** /**
* Get the version of Ghidra that was used to analyze this program * Get the version of Ghidra that was used to analyze this program
* @return a string containing the version number of Ghidra used to analyze the current program * @return a string containing the version number of Ghidra used to analyze the current program
@ -903,12 +952,13 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
/** /**
* Method to print class hierarchies for the given list of classes starting with the lowest child classes in each family of classes * Method to print class hierarchies for the given list of classes starting with the lowest
* child classes in each family of classes
* @param recoveredClasses the list of classes * @param recoveredClasses the list of classes
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
private void printClassHierarchiesFromLowestChildren( private void printClassHierarchiesFromLowestChildren(List<RecoveredClass> recoveredClasses)
List<RecoveredClass> recoveredClasses) throws CancelledException { throws CancelledException {
StringBuffer wholeBuffer = new StringBuffer(); StringBuffer wholeBuffer = new StringBuffer();
wholeBuffer.append("\r\n"); wholeBuffer.append("\r\n");
@ -969,7 +1019,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
/** /**
* Method to print counts of various class items for the given classes, such as number of constructors, destructors, etc... * Method to print counts of various class items for the given classes, such as number of
* constructors, destructors, etc...
* @param recoveredClasses list of classes * @param recoveredClasses list of classes
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
@ -979,13 +1030,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
recoverClassesFromRTTI.getNumberOfConstructors(recoveredClasses)); recoverClassesFromRTTI.getNumberOfConstructors(recoveredClasses));
println("Total number of inlined constructors: " + println("Total number of inlined constructors: " +
getNumberOfInlinedConstructors(recoveredClasses)); getNumberOfInlinedConstructors(recoveredClasses));
println( println("Total number of destructors: " +
"Total number of destructors: " +
recoverClassesFromRTTI.getNumberOfDestructors(recoveredClasses)); recoverClassesFromRTTI.getNumberOfDestructors(recoveredClasses));
println("Total number of inlined destructors: " + println("Total number of inlined destructors: " +
recoverClassesFromRTTI.getNumberOfInlineDestructors(recoveredClasses)); recoverClassesFromRTTI.getNumberOfInlineDestructors(recoveredClasses));
println( println("Total number of virtual functions: " +
"Total number of virtual functions: " +
recoverClassesFromRTTI.getNumberOfVirtualFunctions(recoveredClasses)); recoverClassesFromRTTI.getNumberOfVirtualFunctions(recoveredClasses));
println("Total number of virtual functions that are deleting destructors: " + println("Total number of virtual functions that are deleting destructors: " +
recoverClassesFromRTTI.getNumberOfDeletingDestructors(recoveredClasses)); recoverClassesFromRTTI.getNumberOfDeletingDestructors(recoveredClasses));
@ -1090,7 +1139,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
/** /**
* Method to get formatted string containing the given class, it's parents and it's children * Method to get formatted string containing the given class, it's parents and it's children
* @param recoveredClass the given classes * @param recoveredClass the given classes
* @return StringBuffer containing the formatted string containing the given class, it's parents and it's children * @return StringBuffer containing the formatted string containing the given class, it's parents
* and it's children
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
private StringBuffer printClassParentsandChildren(RecoveredClass recoveredClass) private StringBuffer printClassParentsandChildren(RecoveredClass recoveredClass)
@ -1128,8 +1178,9 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
/** /**
* Method to create a string containing class info for the given class including parents, children, constructors, destructors * Method to create a string containing class info for the given class including parents,
* inlined constructors, inlined destructors, member functions, member data and the same info for each child class * children, constructors, destructors inlined constructors, inlined destructors, member
* functions, member data and the same info for each child class
* @param recoveredClass the given class * @param recoveredClass the given class
* @return string buffer containing class info for the given class * @return string buffer containing class info for the given class
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
@ -1163,12 +1214,10 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor); Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor);
if (isVirtualParent != null && isVirtualParent) { if (isVirtualParent != null && isVirtualParent) {
stringBuffer.append( stringBuffer.append("\t virtual " + ancestor.getName() + "\r\n");
"\t virtual " + ancestor.getName() + "\r\n");
} }
else { else {
stringBuffer.append( stringBuffer.append("\t" + ancestor.getName() + "\r\n");
"\t" + ancestor.getName() + "\r\n");
} }
} }
} }
@ -1251,8 +1300,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions(); List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions();
for (Function vfunction : virtualFunctions) { for (Function vfunction : virtualFunctions) {
monitor.checkCanceled(); monitor.checkCanceled();
stringBuffer.append("\t" + vfunction.getName() + " " + stringBuffer.append(
vfunction.getEntryPoint().toString() + "\r\n"); "\t" + vfunction.getName() + " " + vfunction.getEntryPoint().toString() + "\r\n");
} }
stringBuffer.append("\r\n"); stringBuffer.append("\r\n");
@ -1364,8 +1413,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
List<Function> constructorList = recoveredClass.getConstructorList(); List<Function> constructorList = recoveredClass.getConstructorList();
for (Function constructorFunction : constructorList) { for (Function constructorFunction : constructorList) {
monitor.checkCanceled(); monitor.checkCanceled();
String functionSignatureString = String functionSignatureString = getFunctionSignatureString(constructorFunction, true);
getFunctionSignatureString(constructorFunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
stringBuffer.append("\r\n"); stringBuffer.append("\r\n");
@ -1376,8 +1424,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
List<Function> destructorList = recoveredClass.getDestructorList(); List<Function> destructorList = recoveredClass.getDestructorList();
for (Function destructorFunction : destructorList) { for (Function destructorFunction : destructorList) {
monitor.checkCanceled(); monitor.checkCanceled();
String functionSignatureString = String functionSignatureString = getFunctionSignatureString(destructorFunction, true);
getFunctionSignatureString(destructorFunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
stringBuffer.append("\r\n"); stringBuffer.append("\r\n");
} }
@ -1400,8 +1447,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions(); List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions();
for (Function vfunction : virtualFunctions) { for (Function vfunction : virtualFunctions) {
monitor.checkCanceled(); monitor.checkCanceled();
String functionSignatureString = String functionSignatureString = getFunctionSignatureString(vfunction, true);
getFunctionSignatureString(vfunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
stringBuffer.append("\r\n"); stringBuffer.append("\r\n");
} }

View file

@ -287,7 +287,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
List<Symbol> vftableSymbols = createMissingVftableSymbols(completeObjectLocatorSymbols); List<Symbol> vftableSymbols = createMissingVftableSymbols(completeObjectLocatorSymbols);
return vftableSymbols; return vftableSymbols;
} }
/** /**
@ -447,8 +446,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
Data baseClassDescriptor = extendedFlatAPI.getDataAt(baseClassDescriptorAddress); Data baseClassDescriptor = extendedFlatAPI.getDataAt(baseClassDescriptorAddress);
if (baseClassDescriptor == null || !baseClassDescriptor.getDataType() if (baseClassDescriptor == null || !baseClassDescriptor.getDataType()
.getName() .getName()
.equals( .equals(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) {
RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) {
int num1 = extendedFlatAPI.getInt(baseClassDescriptorAddress.add(8)); int num1 = extendedFlatAPI.getInt(baseClassDescriptorAddress.add(8));
int num2 = extendedFlatAPI.getInt(baseClassDescriptorAddress.add(12)); int num2 = extendedFlatAPI.getInt(baseClassDescriptorAddress.add(12));
@ -531,11 +529,9 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
Data classHierarchyStructure = extendedFlatAPI.getDataAt(classHierarchyDescriptorAddress); Data classHierarchyStructure = extendedFlatAPI.getDataAt(classHierarchyDescriptorAddress);
if (classHierarchyStructure != null && if (classHierarchyStructure != null && classHierarchyStructure.getDataType()
classHierarchyStructure.getDataType()
.getName() .getName()
.equals( .equals(RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME)) {
RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME)) {
return classHierarchyDescriptorAddress; return classHierarchyDescriptorAddress;
} }
@ -842,8 +838,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
private void createMissingFunctions(List<Symbol> vftableSymbols) private void createMissingFunctions(List<Symbol> vftableSymbols)
throws CancelledException, Exception { throws CancelledException, Exception {
List<Address> unusedVftableReferences = List<Address> unusedVftableReferences = findVftableReferencesNotInFunction(vftableSymbols);
findVftableReferencesNotInFunction(vftableSymbols);
if (unusedVftableReferences.size() > 0) { if (unusedVftableReferences.size() > 0) {
extendedFlatAPI.createUndefinedFunctions(unusedVftableReferences); extendedFlatAPI.createUndefinedFunctions(unusedVftableReferences);
@ -1146,8 +1141,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
List<RecoveredClass> classHierarchy = new ArrayList<RecoveredClass>(); List<RecoveredClass> classHierarchy = new ArrayList<RecoveredClass>();
List<Symbol> symbols = extendedFlatAPI.getListOfSymbolsByNameInNamespace( List<Symbol> symbols = extendedFlatAPI.getListOfSymbolsByNameInNamespace(
RTTI_BASE_CLASS_ARRAY_LABEL, RTTI_BASE_CLASS_ARRAY_LABEL, recoveredClass.getClassNamespace(), false);
recoveredClass.getClassNamespace(), false);
if (symbols.size() == 1) { if (symbols.size() == 1) {
Symbol rttiBaseClassSymbol = symbols.get(0); Symbol rttiBaseClassSymbol = symbols.get(0);
@ -1269,7 +1263,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
Msg.debug(this, recoveredClass.getName() + " has ambiguous inh type"); Msg.debug(this, recoveredClass.getName() + " has ambiguous inh type");
} }
} }
/** /**
@ -1337,8 +1330,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
} }
/** /**
* Method to recover parent information, including class offsets, vbase structure and its offset and address if applicable, and whether * Method to recover parent information, including class offsets, vbase structure and its offset and address if applicable, and whether
* the parent is regularly or virtually inherited * the parent is regularly or virtually inherited
@ -1495,8 +1486,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
continue; continue;
} }
FillOutStructureCmd fillCmd = FillOutStructureCmd fillCmd = new FillOutStructureCmd(program, location, tool);
new FillOutStructureCmd(program, location, tool);
Address vbtableAddress = getVbtableAddressFromDecompiledFunction(fillCmd, highFunction, Address vbtableAddress = getVbtableAddressFromDecompiledFunction(fillCmd, highFunction,
recoveredClass, constructor, vbtableOffset); recoveredClass, constructor, vbtableOffset);
@ -1526,8 +1516,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
continue; continue;
} }
FillOutStructureCmd fillCmd = FillOutStructureCmd fillCmd = new FillOutStructureCmd(program, location, tool);
new FillOutStructureCmd(program, location, tool);
Address vbtableAddress = getVbtableAddressFromDecompiledFunction(fillCmd, highFunction, Address vbtableAddress = getVbtableAddressFromDecompiledFunction(fillCmd, highFunction,
recoveredClass, constructor, vbtableOffset); recoveredClass, constructor, vbtableOffset);
@ -1665,8 +1654,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
List<RecoveredClass> parents = List<RecoveredClass> parents =
new ArrayList<RecoveredClass>(recoveredClass.getClassHierarchyMap().keySet()); new ArrayList<RecoveredClass>(recoveredClass.getClassHierarchyMap().keySet());
RecoveredClass singleParent = parents.get(0); RecoveredClass singleParent = parents.get(0);
List<RecoveredClass> grandParents = List<RecoveredClass> grandParents = getParentsWithVirtualFunctions(singleParent);
getParentsWithVirtualFunctions(singleParent);
// check that they both have vftables // check that they both have vftables
// get their order from the class hierarchy list // get their order from the class hierarchy list
// first see if it has a parent order map and just make it the same one // first see if it has a parent order map and just make it the same one
@ -2200,18 +2188,15 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// create pointers to empty vftable structs so they can be added to the class data type // create pointers to empty vftable structs so they can be added to the class data type
// then filled in later // then filled in later
Map<Address, DataType> vfPointerDataTypes = Map<Address, DataType> vfPointerDataTypes = createEmptyVfTableStructs(recoveredClass);
createEmptyVfTableStructs(recoveredClass);
// create current class structure and add pointer to vftable, all parent member data strutures, and class member data structure // create current class structure and add pointer to vftable, all parent member data strutures, and class member data structure
Structure classStruct = null; Structure classStruct = null;
classStruct = createClassStructureUsingRTTI(recoveredClass, classStruct = createClassStructureUsingRTTI(recoveredClass, vfPointerDataTypes);
vfPointerDataTypes);
applyVbtableStructure(recoveredClass); applyVbtableStructure(recoveredClass);
// Now that we have a class data type // Now that we have a class data type
// name constructor and destructor functions and put into the class namespace // name constructor and destructor functions and put into the class namespace
// checks are internal for hasDebugSymbols since there // checks are internal for hasDebugSymbols since there
@ -2392,8 +2377,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// if it fits at offset or is at the end and class structure can be grown, // if it fits at offset or is at the end and class structure can be grown,
// copy the whole baseClass structure to the class Structure at the given offset // copy the whole baseClass structure to the class Structure at the given offset
EditStructureUtils.addDataTypeToStructure(classStructureDataType, EditStructureUtils.addDataTypeToStructure(classStructureDataType, baseClassOffset,
baseClassOffset, baseClassStructure, baseClassStructure.getName(), monitor); baseClassStructure, baseClassStructure.getName(), monitor);
} }
@ -2421,8 +2406,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// if it fits at offset or is at the end and class structure can be grown, // if it fits at offset or is at the end and class structure can be grown,
// copy the whole baseClass structure to the class Structure at the given offset // copy the whole baseClass structure to the class Structure at the given offset
EditStructureUtils.addDataTypeToStructure(classStructureDataType, EditStructureUtils.addDataTypeToStructure(classStructureDataType, offset.intValue(),
offset.intValue(), classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor);
} }
// add the vbtable structure for single inheritance/virt parent case // add the vbtable structure for single inheritance/virt parent case
@ -2775,15 +2760,13 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
} }
AddressRange firstRange = body.getFirstRange(); AddressRange firstRange = body.getFirstRange();
Address maxAddressofFirstRange = firstRange.getMaxAddress(); Address maxAddressofFirstRange = firstRange.getMaxAddress();
Instruction instructionContaining = Instruction instructionContaining = api.getInstructionContaining(maxAddressofFirstRange);
api.getInstructionContaining(maxAddressofFirstRange);
if (!instructionContaining.getFlowType().isJump()) { if (!instructionContaining.getFlowType().isJump()) {
return null; return null;
} }
AddressRange lastRange = body.getLastRange(); AddressRange lastRange = body.getLastRange();
Address minAddressOfLastRange = lastRange.getMinAddress(); Address minAddressOfLastRange = lastRange.getMinAddress();
Reference reference = Reference reference = api.getReference(instructionContaining, minAddressOfLastRange);
api.getReference(instructionContaining, minAddressOfLastRange);
if (reference == null) { if (reference == null) {
return null; return null;
} }
@ -2836,4 +2819,3 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
} }
} }

View file

@ -23,6 +23,7 @@ import ghidra.app.cmd.data.rtti.*;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.datatype.microsoft.*; import ghidra.app.util.datatype.microsoft.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.InvalidDataTypeException; import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -41,6 +42,7 @@ public class RttiAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Windows x86 PE RTTI Analyzer"; private static final String NAME = "Windows x86 PE RTTI Analyzer";
private static final String DESCRIPTION = private static final String DESCRIPTION =
"Finds and creates RTTI metadata structures and associated vf tables."; "Finds and creates RTTI metadata structures and associated vf tables.";
public static final String RTTI_FOUND_OPTION = "RTTI Found";
// TODO If we want the RTTI analyzer to find all type descriptors regardless of whether // TODO If we want the RTTI analyzer to find all type descriptors regardless of whether
// they are used for RTTI, then change the CLASS_PREFIX_CHARS to ".". Need to be // they are used for RTTI, then change the CLASS_PREFIX_CHARS to ".". Need to be
@ -73,9 +75,15 @@ public class RttiAnalyzer extends AbstractAnalyzer {
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException { throws CancelledException {
// "rttiFound" option added in 10.3 so if analyzed with previous version analyzer will rerun
if(hasRun(program)) {
return true;
}
Address commonVfTableAddress = RttiUtil.findTypeInfoVftableAddress(program, monitor); Address commonVfTableAddress = RttiUtil.findTypeInfoVftableAddress(program, monitor);
if (commonVfTableAddress == null) { if (commonVfTableAddress == null) {
setRttiFound(program, false);
return true; return true;
} }
@ -83,22 +91,50 @@ public class RttiAnalyzer extends AbstractAnalyzer {
Set<Address> possibleTypeAddresses = locatePotentialRTTI0Entries(program, set, monitor); Set<Address> possibleTypeAddresses = locatePotentialRTTI0Entries(program, set, monitor);
if (possibleTypeAddresses == null) { if (possibleTypeAddresses == null) {
setRttiFound(program, false);
return true; return true;
} }
// We now have a list of potential rtti0 addresses. // We now have a list of potential rtti0 addresses.
processRtti0(possibleTypeAddresses, program, monitor); processRtti0(possibleTypeAddresses, program, monitor);
setRttiFound(program, true);
return true; return true;
} }
/**
* Has this analyzer been run on the given program. NOTE: option new as of 10.3 so this will
* not be accurate for older programs.
* @param program the given program
* @return true if analyzer has run, false if not or unknown (before version 10.3)
*/
private boolean hasRun(Program program) {
Options programOptions = program.getOptions(Program.PROGRAM_INFO);
Boolean hasRun = (Boolean) programOptions.getObject(RTTI_FOUND_OPTION, null);
if(hasRun == null) {
return false;
}
return true;
}
/**
* Method to set the RTTI Found option for the given program
* @param program the given program
* @param rttiFound true if RTTI found and processed, false otherwise
*/
private void setRttiFound(Program program, boolean rttiFound) {
Options programOptions = program.getOptions(Program.PROGRAM_INFO);
programOptions.setBoolean(RTTI_FOUND_OPTION, rttiFound);
}
/** /**
* locate any potential RTTI0 based on pointers to the type_info vftable * locate any potential RTTI0 based on pointers to the type_info vftable
* @param program proram to locate within * @param program proram to locate within
* @param set restricted set to locate within * @param set restricted set to locate within
* @param monitor monitor for canceling * @param monitor monitor for canceling
* @return set of potential RTTI0 entries * @return set of potential RTTI0 entries
* @throws CancelledException * @throws CancelledException if cancelled
*/ */
private Set<Address> locatePotentialRTTI0Entries(Program program, AddressSetView set, private Set<Address> locatePotentialRTTI0Entries(Program program, AddressSetView set,
TaskMonitor monitor) throws CancelledException { TaskMonitor monitor) throws CancelledException {