From 0766c300480561c1ab287572f5c43764a0a5dfbc Mon Sep 17 00:00:00 2001 From: ghidra007 Date: Wed, 10 Nov 2021 19:59:21 +0000 Subject: [PATCH] GP-1464 RecoverClassesFromRTTIScript now consistently applies its class structures in programs that have PDB information applied. Also, an option was added so users can decide whether to replace existing class data in thiscall functions regardless of whether they originated as PDB or not. --- ...yClassFunctionDefinitionUpdatesScript.java | 2 +- ...lyClassFunctionSignatureUpdatesScript.java | 2 +- .../RecoverClassesFromRTTIScript.java | 8 + .../classrecovery/RTTIClassRecoverer.java | 4 +- .../classrecovery/RTTIGccClassRecoverer.java | 15 +- .../RTTIWindowsClassRecoverer.java | 31 +-- .../classrecovery/RecoveredClassUtils.java | 181 ++++++++++++++++-- 7 files changed, 207 insertions(+), 36 deletions(-) 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); + } } }