diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionDefinitionUpdatesScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionDefinitionUpdatesScript.java index d949dd98eb..25b6922a49 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionDefinitionUpdatesScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionDefinitionUpdatesScript.java @@ -45,7 +45,7 @@ public class ApplyClassFunctionDefinitionUpdatesScript extends GhidraScript { } RecoveredClassUtils classUtils = new RecoveredClassUtils(currentProgram, currentLocation, - state.getTool(), this, false, false, false, monitor); + state.getTool(), this, false, false, false, false, monitor); Namespace classNamespace = classUtils.getClassNamespace(currentAddress); if (classNamespace == null) { diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionSignatureUpdatesScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionSignatureUpdatesScript.java index 3db3b3c858..6ce4321839 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionSignatureUpdatesScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/ApplyClassFunctionSignatureUpdatesScript.java @@ -45,7 +45,7 @@ public class ApplyClassFunctionSignatureUpdatesScript extends GhidraScript { } RecoveredClassUtils classUtils = new RecoveredClassUtils(currentProgram, currentLocation, - state.getTool(), this, false, false, false, monitor); + state.getTool(), this, false, false, false, false, monitor); Namespace classNamespace = classUtils.getClassNamespace(currentAddress); if (classNamespace == null) { diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java index 107a73a843..7a39e51fa2 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -121,6 +121,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { // show shortened class template names in class structure field names private static final boolean USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS = true; + // replace defined existing class structures (ie pdb, fid, demangler, or other)with ones created by + // this script and rename the existing ones with a _REPLACED suffix + // NOTE: currently does not replace DWARF + // NEW OPTION: + private static final boolean REPLACE_EXISTING_CLASS_STRUCTURES = true; + private static final String CLASS_DATA_STRUCT_NAME = "_data"; private static final String CONSTRUCTOR_BOOKMARK = "CONSTRUCTOR"; @@ -162,6 +168,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram, currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, hasDebugSymbols, + REPLACE_EXISTING_CLASS_STRUCTURES, monitor); } else if (isGcc()) { @@ -182,6 +189,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, hasDebugSymbols, + REPLACE_EXISTING_CLASS_STRUCTURES, monitor); } else { diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java index daba6d4698..408765ac17 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java @@ -40,12 +40,14 @@ public class RTTIClassRecoverer extends RecoveredClassUtils { TaskMonitor monitor; boolean hasDebugSymbols; + RTTIClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVfunctions, boolean hasDebugSymbols, + boolean nameVfunctions, boolean hasDebugSymbols, boolean replaceClassStructures, TaskMonitor monitor) { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, + replaceClassStructures, monitor); this.program = program; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index 74f872de7b..a678e60417 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -69,15 +69,19 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { new HashMap>(); boolean isDwarfLoaded; + boolean replaceClassStructs; public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVfunctions, boolean isDwarfLoaded, TaskMonitor monitor) { + boolean nameVfunctions, boolean isDwarfLoaded, boolean replaceExistingClassStructures, + TaskMonitor monitor) { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, + replaceExistingClassStructures, isDwarfLoaded, monitor); this.isDwarfLoaded = isDwarfLoaded; + this.replaceClassStructs = replaceExistingClassStructures; } @Override @@ -2898,12 +2902,14 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { Structure classStruct = createSimpleClassStructure(recoveredClass, vfPointerDataTypes); // check for DWARF -- if none add c/d/etc to class + //TODO: if decide to replace dwarf data types then remove this check so the replaces + // in the following methods can replace the dwarf data types if (!isDwarfLoaded) { // Now that we have a class data type // name constructor and destructor functions and put into the class namespace addConstructorsToClassNamespace(recoveredClass, classStruct); - addDestructorsToClassNamespace(recoveredClass); + addDestructorsToClassNamespace(recoveredClass, classStruct); // addNonThisDestructorsToClassNamespace(recoveredClass); // addVbaseDestructorsToClassNamespace(recoveredClass); // addVbtableToClassNamespace(recoveredClass); @@ -2914,7 +2920,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // createIndeterminateInlineComments(recoveredClass); // add label on constructor destructor functions that could not be determined which were which - createIndeterminateLabels(recoveredClass); + createIndeterminateLabels(recoveredClass, classStruct); } // This is done after the class structure is created and added to the dtmanager @@ -2922,7 +2928,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // then empty classes will get auto-created in the wrong place // when the vfunctions are put in the class - fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes); + fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes, + classStruct); } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index 356fbfde37..ddadec4088 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -69,11 +69,11 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { public RTTIWindowsClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVFunctions, boolean isPDBLoaded, + boolean nameVFunctions, boolean isPDBLoaded, boolean replaceClassStructures, TaskMonitor monitor) throws CancelledException { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVFunctions, - isPDBLoaded, monitor); + isPDBLoaded, replaceClassStructures, monitor); this.isPDBLoaded = isPDBLoaded; @@ -2245,32 +2245,35 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { applyVbtableStructure(recoveredClass); - // pdb already has good names so only name if no pdb - if (!isPDBLoaded) { - // Now that we have a class data type - // name constructor and destructor functions and put into the class namespace - addConstructorsToClassNamespace(recoveredClass, classStruct); - addDestructorsToClassNamespace(recoveredClass); + // Now that we have a class data type + // name constructor and destructor functions and put into the class namespace + // checks are internal for hasDebugSymbols since there + // are also replace methods that need to be called either way + addConstructorsToClassNamespace(recoveredClass, classStruct); + addDestructorsToClassNamespace(recoveredClass, classStruct); + addVbaseDestructorsToClassNamespace(recoveredClass, classStruct); + + if (!hasDebugSymbols) { addNonThisDestructorsToClassNamespace(recoveredClass); - addVbaseDestructorsToClassNamespace(recoveredClass); + addVbtableToClassNamespace(recoveredClass); // add secondary label on functions with inlined constructors or destructors createInlinedConstructorComments(recoveredClass); createInlinedDestructorComments(recoveredClass); createIndeterminateInlineComments(recoveredClass); - - // add label on constructor destructor functions that could not be determined which were which - createIndeterminateLabels(recoveredClass); } + // add label on constructor destructor functions that could not be determined which were which + createIndeterminateLabels(recoveredClass, classStruct); + // This is done after the class structure is created and added to the dtmanager // because if done before the class structures are created // then empty classes will get auto-created in the wrong place // when the vfunctions are put in the class - - fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes); + fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes, + classStruct); } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java index 7b6e1fadc6..da19b6b4b0 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java @@ -144,10 +144,11 @@ public class RecoveredClassUtils { boolean createBookmarks; boolean useShortTemplates; boolean nameVfunctions; + boolean replaceClassStructures; public RecoveredClassUtils(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVfunctions, TaskMonitor monitor) { + boolean nameVunctions, boolean replaceClassStructures, TaskMonitor monitor) { this.monitor = monitor; this.program = program; @@ -162,7 +163,8 @@ public class RecoveredClassUtils { this.createBookmarks = createBookmarks; this.useShortTemplates = useShortTemplates; - this.nameVfunctions = nameVfunctions; + this.nameVfunctions = nameVunctions; + this.replaceClassStructures = replaceClassStructures; globalNamespace = (GlobalNamespace) program.getGlobalNamespace(); @@ -3294,12 +3296,17 @@ public class RecoveredClassUtils { monitor.checkCanceled(); Function constructorFunction = constructorsIterator.next(); - createNewSymbolAtFunction(constructorFunction, className, classNamespace, true, true); + if (nameVfunctions) { + createNewSymbolAtFunction(constructorFunction, className, classNamespace, true, + true); + } // check to see if the "this" data type is an empty placeholder for the class // structure and replace it with the one that was just created by the script - //deleteEmptyClassStructure(constructorFunction, className); - replaceEmptyClassStructure(constructorFunction, className, classStruct); + //NEW + if (replaceClassStructures) { + replaceClassStructure(constructorFunction, className, classStruct); + } // if current decompiler function return type is a pointer then set the return type // to a pointer to the class structure, otherwise if it is a void, make it a void so the @@ -3337,9 +3344,11 @@ public class RecoveredClassUtils { /** * Method to name class destructors and add them to class namespace * @param recoveredClass current class + * @param classStruct the class structure for the given class * @throws Exception when cancelled */ - public void addDestructorsToClassNamespace(RecoveredClass recoveredClass) throws Exception { + public void addDestructorsToClassNamespace(RecoveredClass recoveredClass, Structure classStruct) + throws Exception { Namespace classNamespace = recoveredClass.getClassNamespace(); String className = recoveredClass.getName(); @@ -3351,8 +3360,17 @@ public class RecoveredClassUtils { Function destructorFunction = destructorIterator.next(); String destructorName = "~" + className; - createNewSymbolAtFunction(destructorFunction, destructorName, classNamespace, true, - true); + if (nameVfunctions) { + createNewSymbolAtFunction(destructorFunction, destructorName, classNamespace, true, + true); + } + + // check to see if the "this" data type is an empty placeholder for the class + // structure and replace it with the one that was just created by the script + //NEW + if (replaceClassStructures) { + replaceClassStructure(destructorFunction, className, classStruct); + } destructorFunction.setReturnType(DataType.VOID, SourceType.ANALYSIS); } @@ -3384,9 +3402,11 @@ public class RecoveredClassUtils { /** * Method to name class vbase destructors and add them to class namespace * @param recoveredClass current class + * @param classStruct the class structure for the given class * @throws Exception when cancelled */ - public void addVbaseDestructorsToClassNamespace(RecoveredClass recoveredClass) + public void addVbaseDestructorsToClassNamespace(RecoveredClass recoveredClass, + Structure classStruct) throws Exception { Namespace classNamespace = recoveredClass.getClassNamespace(); @@ -3395,8 +3415,18 @@ public class RecoveredClassUtils { if (vbaseDestructorFunction != null) { String destructorName = VBASE_DESTRUCTOR_LABEL; - createNewSymbolAtFunction(vbaseDestructorFunction, destructorName, classNamespace, true, - true); + if (nameVfunctions) { + createNewSymbolAtFunction(vbaseDestructorFunction, destructorName, classNamespace, + true, true); + } + + // check to see if the "this" data type is an empty placeholder for the class + // structure and replace it with the one that was just created by the script + //NEW + if (replaceClassStructures) { + replaceClassStructure(vbaseDestructorFunction, recoveredClass.getName(), + classStruct); + } vbaseDestructorFunction.setReturnType(DataType.VOID, SourceType.ANALYSIS); } @@ -3505,6 +3535,106 @@ public class RecoveredClassUtils { } } + /** + * Method to replace the program's current class structure, only if an empty placeholder structure, + * with the one generated by this script + * @param function a class method with current class structure applied + * @param className the given class name + * @param newClassStructure the new structure to replace the old with + * @throws DataTypeDependencyException if there is a data dependency exception when replacing + * @throws CancelledException if cancelled + */ + public void replaceClassStructure(Function function, String className, + Structure newClassStructure) throws DataTypeDependencyException, CancelledException { + + Parameter thisParam = function.getParameter(0); + if (thisParam == null) { + return; + } + + DataType dataType = thisParam.getDataType(); + if (dataType instanceof Pointer) { + Pointer ptr = (Pointer) dataType; + DataType baseDataType = ptr.getDataType(); + if (!baseDataType.equals(newClassStructure) && + baseDataType.getName().equals(className)) { + + // check if fid demangler or pdb - don't replace user ones + if (!isReplaceableType(function.getEntryPoint(), baseDataType)) { + return; + } + // create copy of existing one + DataType baseDataTypeCopy = baseDataType.copy(dataTypeManager); + + renameDataType(baseDataTypeCopy, baseDataType.getName() + "_REPLACED"); + + // replace the other with the new one + dataTypeManager.replaceDataType(baseDataType, newClassStructure, false); + +// // remove original folder if it is empty after the replace + // in future if decide to just remove the other ones, then do the following +// CategoryPath originalPath = baseDataType.getCategoryPath(); +// Category category = dataTypeManager.getCategory(originalPath); +// Category parentCategory = category.getParent(); +// if (parentCategory != null) { +// parentCategory.removeEmptyCategory(category.getName(), monitor); +// } + + } + } + } + + private void renameDataType(DataType dataType, String name) throws CancelledException { + + boolean renamed = false; + int oneup = 2; + while (!renamed) { + monitor.checkCanceled(); + try { + dataType.setName(name); + dataTypeManager.resolve(dataType, DataTypeConflictHandler.DEFAULT_HANDLER); + renamed = true; + } + catch (InvalidNameException | DuplicateNameException e) { + name = name + oneup++; + renamed = false; + } + } + } + + private boolean isReplaceableType(Address address, DataType dataType) { + + // return false if it isn't even a structure + if (!(dataType instanceof Structure)) { + return false; + } + String categoryPath = dataType.getPathName(); + if (categoryPath.startsWith("/Demangler")) { + return true; + } + + if (categoryPath.contains(".pdb")) { + return true; + } + + //TODO: decide whether to replace dwarf or not + + // test to see if the data type is an empty structure with "PlaceHolder Class Structure" in + // the description + Structure structure = (Structure) dataType; + if (structure.isNotYetDefined() && + structure.getDescription().equals("PlaceHolder Class Structure")) { + return true; + } + + if (program.getBookmarkManager().getBookmark(address, BookmarkType.ANALYSIS, + "Function ID Analyzer") != null) { + return true; + } + return false; + + } + /** * Method to create a new symbol at the given function * @param function the given function @@ -4435,11 +4565,12 @@ public class RecoveredClassUtils { * Method to fill in the vftable structure with pointers to virtual function signature data types * @param recoveredClass the current class to be processed * @param vftableToStructureMap the map from the class's vftables to the correct vftable structure data type + * @param classStruct the class structure for the given class * @throws CancelledException when cancelled * @throws Exception if other exception */ public void fillInAndApplyVftableStructAndNameVfunctions(RecoveredClass recoveredClass, - Map vftableToStructureMap) throws CancelledException, Exception { + Map vftableToStructureMap, Structure classStruct) throws CancelledException, Exception { //create function definition for each virtual function and put in vftable structure and // data subfolder @@ -4468,6 +4599,7 @@ public class RecoveredClassUtils { nameVfunctions(recoveredClass, vftableAddress, vftableStructureName); } + List vFunctions = recoveredClass.getVirtualFunctions(vftableAddress); int vfunctionNumber = 1; Iterator vfIterator = vFunctions.iterator(); @@ -4483,6 +4615,13 @@ public class RecoveredClassUtils { continue; } + // check to see if the "this" data type is an empty placeholder for the class + // structure and replace it with the one that was just created by the script + //NEW + if (replaceClassStructures) { + replaceClassStructure(vfunction, recoveredClass.getName(), classStruct); + } + // get the classPath of highest level parent with vfAddress in their vftable classPath = getCategoryPathForFunctionSignature(vfunction, recoveredClass, vftableAddress); @@ -5008,9 +5147,11 @@ public class RecoveredClassUtils { /** * Method to add label on constructor or destructors but couldn't tell which * @param recoveredClass current class + * @param classStruct the class structure for the given class * @throws Exception when cancelled */ - public void createIndeterminateLabels(RecoveredClass recoveredClass) throws Exception { + public void createIndeterminateLabels(RecoveredClass recoveredClass, Structure classStruct) + throws Exception { Namespace classNamespace = recoveredClass.getClassNamespace(); String className = recoveredClass.getName(); @@ -5020,8 +5161,18 @@ public class RecoveredClassUtils { while (unknownsIterator.hasNext()) { monitor.checkCanceled(); Function indeterminateFunction = unknownsIterator.next(); - createNewSymbolAtFunction(indeterminateFunction, - className + "_Constructor_or_Destructor", classNamespace, false, false); + + if (nameVfunctions) { + createNewSymbolAtFunction(indeterminateFunction, + className + "_Constructor_or_Destructor", classNamespace, false, false); + } + + // check to see if the "this" data type is an empty placeholder for the class + // structure and replace it with the one that was just created by the script + //NEW + if (replaceClassStructures) { + replaceClassStructure(indeterminateFunction, className, classStruct); + } } }