diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java index 9cc3d3883a..6ea17c6006 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -326,6 +326,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { return ("There is no open program"); } + CategoryPath path = + new CategoryPath(CategoryPath.ROOT, RecoveredClassHelper.DTM_CLASS_DATA_FOLDER_NAME); + if (currentProgram.getDataTypeManager().containsCategory(path)) { + return ("This script has already been run on this program"); + } + 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"); } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java index 777e511f05..b263bd4ec6 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java @@ -1031,72 +1031,22 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { } /** - * Create data type manager path that will be used when data types are created to place them in the correct folder - * @param parent parent CategoryPath - * @param categoryName name of the new category in the parent path + * Create data type manager path combining the given parent category path and namespace + * @param parent the given parent CategoryPath + * @param namespace the given namespace * @return CategoryPath for new categoryName * @throws CancelledException if cancelled */ - public CategoryPath createDataTypeCategoryPath(CategoryPath parent, String categoryName) throws CancelledException { + public CategoryPath createDataTypeCategoryPath(CategoryPath parent, Namespace namespace) + throws CancelledException { - CategoryPath dataTypePath; + CategoryPath dataTypePath = parent; - // if single namespace no parsing necessary, just create using given categoryName - if (!categoryName.contains("::")) { - dataTypePath = new CategoryPath(parent, categoryName); - return dataTypePath; + for (String name : namespace.getPathList(true)) { + monitor.checkCanceled(); + + dataTypePath = new CategoryPath(dataTypePath, name); } - - // if category name contains :: but not valid template info then just - // replace ::'s with /'s to form multi level path - if (!containsTemplate(categoryName)) { - categoryName = categoryName.replace("::", "/"); - } - - // if category name contains both :: and matched template brackets then only replace the - // :: that are not contained inside template brackets - else { - boolean insideBrackets = false; - int numOpenedBrackets = 0; - int index = 0; - String newCategoryName = new String(); - while (index < categoryName.length()) { - monitor.checkCanceled(); - - if (categoryName.substring(index).startsWith("::") && !insideBrackets) { - newCategoryName = newCategoryName.concat("/"); - index += 2; - continue; - } - - String character = categoryName.substring(index, index + 1); - - newCategoryName = newCategoryName.concat(character); - index++; - - if (character.equals("<")) { - insideBrackets = true; - numOpenedBrackets++; - } - if (character.equals(">")) { - numOpenedBrackets--; - } - if (numOpenedBrackets == 0) { - insideBrackets = false; - } - } - categoryName = newCategoryName; - } - - String path; - if (parent.getName().equals("")) { - path = "/" + categoryName; - } - else { - path = "/" + parent.getName() + "/" + categoryName; - } - dataTypePath = new CategoryPath(path); - return dataTypePath; } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index d45d075506..a070b0d1d5 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -112,6 +112,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { Msg.debug(this, "Could not recover gcc rtti classes"); return null; } + + if (recoveredClasses.isEmpty()) { + return recoveredClasses; + } createCalledFunctionMap(recoveredClasses); @@ -781,6 +785,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { return false; } + // This has to be "contains" to get all types of class structures some begin and end + // with other things Structure structure = (Structure) baseDataType; if (structure.getName().contains(CLASS_TYPE_INFO_STRUCTURE)) { return true; @@ -1403,9 +1409,17 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { if (typeinfoAddresses.isEmpty()) { return typeinfoAddresses; } + + List
typeinfoAddressesToProcess = new ArrayList
(); for (Address typeinfoAddress : typeinfoAddresses) { + monitor.checkCanceled(); + + if (hasExistingTypeinfoStructure(typeinfoAddress)) { + continue; + } + Address specialTypeinfoRef = extendedFlatAPI.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { @@ -1457,6 +1471,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { throw new Exception( "ERROR: Could not apply typeinfo structure to " + typeinfoAddress); } + + typeinfoAddressesToProcess.add(typeinfoAddress); // check for existing symbol and if none, demangle the name and apply Symbol typeinfoSymbol = api.getSymbolAt(typeinfoAddress); @@ -1474,7 +1490,40 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } } - return typeinfoAddresses; + return typeinfoAddressesToProcess; + } + + /** + * Method to determine if the given address has one of the ClassTypeinfoDataType types applied + * @param address the given address + * @return true if already has a class data type applied or false if not + */ + private boolean hasExistingTypeinfoStructure(Address address) { + + + Data dataAt = api.getDataAt(address); + + if (dataAt == null) { + return false; + } + + DataType dataType = dataAt.getDataType(); + + if (!(dataType instanceof Structure)) { + return false; + } + + // This has to be "contains" to get all types of class structures some begin and end + // with other things + if (!dataType.getName().contains(CLASS_TYPE_INFO_STRUCTURE)) { + return false; + } + + if (!dataType.getPathName().startsWith(DTM_CLASS_DATA_FOLDER_PATH)) { + return false; + } + return true; + } private Data applyTypeinfoStructure(Structure typeInfoStructure, Address typeinfoAddress) diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index 205b79a720..7c76505485 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -885,6 +885,14 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // Get class name from class vftable is in Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); + // get the data type category associated with the given class namespace + Category category = getDataTypeCategory(classNamespace); + + // if it already exists, continue since this class has already been recovered + if (category != null) { + continue; + } + if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { classNamespace = promoteToClassNamespace(classNamespace); if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { @@ -901,13 +909,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // non-vftable class if (vftableSymbolsInNamespace.size() == 0) { String className = classNamespace.getName(); - String classNameWithNamespace = classNamespace.getName(true); - // Create Data Type Manager Category for given class - // TODO: make this global and check it for null - CategoryPath classPath = - extendedFlatAPI.createDataTypeCategoryPath(classDataTypesCategoryPath, - classNameWithNamespace); + // Make a CategoryPath for given class + CategoryPath classPath = extendedFlatAPI + .createDataTypeCategoryPath(classDataTypesCategoryPath, classNamespace); RecoveredClass nonVftableClass = new RecoveredClass(className, classPath, classNamespace, dataTypeManager); @@ -1009,12 +1014,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { * Method to figure out the class hierarchies either with RTTI if it is present or with vftable * references * @param recoveredClasses List of classes to process - * @throws CancelledException if cancelled - * @throws AddressOutOfBoundsException AddressOutOfBoundsException - * @throws MemoryAccessException if memory cannot be read + * @throws Exception various exceptions */ private void assignClassInheritanceAndHierarchies(List recoveredClasses) - throws CancelledException, MemoryAccessException, AddressOutOfBoundsException { + throws Exception { // Use RTTI information to determine inheritance type and // class hierarchy diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index ce832419cd..b98c239e8e 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -51,6 +51,7 @@ import ghidra.util.task.TaskMonitor; public class RecoveredClassHelper { public static final String DTM_CLASS_DATA_FOLDER_NAME = "ClassDataTypes"; + public static final String DTM_CLASS_DATA_FOLDER_PATH = "/" + DTM_CLASS_DATA_FOLDER_NAME + "/"; private static final String CLASS_DATA_STRUCT_NAME = "_data"; private static final String DEFAULT_VFUNCTION_PREFIX = "vfunction"; private static final String VFUNCTION_COMMENT = "virtual function #"; @@ -158,9 +159,9 @@ public class RecoveredClassHelper { extendedFlatAPI = new ExtendedFlatProgramAPI(program, monitor); - this.classDataTypesCategoryPath = extendedFlatAPI - .createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME); + CategoryPath path = new CategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME); + this.classDataTypesCategoryPath = path; this.createBookmarks = createBookmarks; this.useShortTemplates = useShortTemplates; this.nameVfunctions = nameVunctions; @@ -2637,10 +2638,9 @@ public class RecoveredClassHelper { throws CancelledException { String className = namespace.getName(); - String classNameWithNamespace = namespace.getName(true); CategoryPath classPath = extendedFlatAPI - .createDataTypeCategoryPath(classDataTypesCategoryPath, classNameWithNamespace); + .createDataTypeCategoryPath(classDataTypesCategoryPath, namespace); RecoveredClass newClass = new RecoveredClass(className, classPath, namespace, dataTypeManager); @@ -7287,7 +7287,7 @@ public class RecoveredClassHelper { return null; } - if (!category.getCategoryPath().getPath().contains(DTM_CLASS_DATA_FOLDER_NAME)) { + if (!category.getCategoryPath().getPath().startsWith(DTM_CLASS_DATA_FOLDER_PATH)) { return null; } @@ -7705,7 +7705,6 @@ public class RecoveredClassHelper { // get the corresponding existing pointer for this function definition from the dtManager Pointer pointer = getPointerDataType(functionDefinition); - if (pointer == null) { throw new IllegalArgumentException( "Cannot find existing pointer data type for " + functionDefinition.getName()); @@ -7730,6 +7729,11 @@ public class RecoveredClassHelper { // get class namespace using the vftable structure Namespace vfunctionStructureNamespace = getClassNamespace(vftableStructure); + //skip if not in the class data type folder so cannot get corresponding namespace + if (vfunctionStructureNamespace == null) { + continue; + } + Data vftableData = getVftableStructureFromListing(vfunctionStructureNamespace, vftableStructure); @@ -7803,32 +7807,16 @@ public class RecoveredClassHelper { * the given data type. This is getting an existing pointer not trying to create a new one. * @param dataType the given data type * @return the existing pointer data type to the given data type in the same class dt folder - * @throws CancelledException if cancelled */ - private Pointer getPointerDataType(DataType dataType) throws CancelledException { + private Pointer getPointerDataType(DataType dataType) { - CategoryPath classPath = dataType.getCategoryPath(); + Category category = dataTypeManager.getCategory(dataType.getCategoryPath()); - Category category = dataTypeManager.getCategory(classPath); + DataType pointer = new PointerDataType(dataType, dataTypeManager); - DataType[] classDataTypes = category.getDataTypes(); - for (DataType classDataType : classDataTypes) { + DataType dt = category.getDataType(pointer.getName()); - monitor.checkCanceled(); - - if (!(classDataType instanceof Pointer)) { - continue; - } - - Pointer pointer = (Pointer) classDataType; - - DataType pointedToDataType = pointer.getDataType(); - - if (pointedToDataType.equals(dataType)) { - return pointer; - } - } - return null; + return (dt instanceof Pointer) ? (Pointer) dt : null; } /** @@ -7871,14 +7859,15 @@ public class RecoveredClassHelper { public List getClassFunctionDefinitions(Namespace classNamespace) throws CancelledException { - CategoryPath classPath = getClassDataFolder(classNamespace); - - Category category = dataTypeManager.getCategory(classPath); + // get the data type category associated with the given class namespace + Category category = getDataTypeCategory(classNamespace); + // return null if there isn't one if (category == null) { return null; } + // get the function definitions in the given data type category and add them to the list List functionDefs = new ArrayList(); DataType[] classDataTypes = category.getDataTypes(); @@ -7896,22 +7885,30 @@ public class RecoveredClassHelper { return functionDefs; } - private CategoryPath getClassDataFolder(Namespace classNamespace) throws CancelledException { + /** + * Get the associated data type category for the given class namespace + * @param classNamespace the given class namespace + * @return the associated data type category or null if it doesn't exist + * @throws CancelledException if cancelled + */ + public Category getDataTypeCategory(Namespace classNamespace) throws CancelledException { - String classNameWithNamespace = classNamespace.getName(true); + // Make a CategoryPath for the given namespace + CategoryPath classPath = + extendedFlatAPI.createDataTypeCategoryPath(classDataTypesCategoryPath, classNamespace); - // Create Data Type Manager Category for given class - CategoryPath classPath = extendedFlatAPI - .createDataTypeCategoryPath(classDataTypesCategoryPath, classNameWithNamespace); - - return classPath; + // check to see if it exists in the data type manager and return it (it will return null + // if it is not in the dtman + Category category = dataTypeManager.getCategory(classPath); + return category; } /** * Method to get the class Namespace corresponding to the given data type. NOTE: The data type * must be in the DTM_CLASS_DATA_FOLDER_NAME folder in the data type manager. * @param dataType the given data type - * @return the class Namespace corresponding to the given data type + * @return the class Namespace corresponding to the given data type or null if the data type + * is not in the DTM_CLASS_DATA_FOLDER_NAME folder or if class doesn't exist * @throws CancelledException if cancelled */ public Namespace getClassNamespace(DataType dataType) throws CancelledException { @@ -7922,32 +7919,29 @@ public class RecoveredClassHelper { CategoryPath categoryPath = dataType.getCategoryPath(); - String className = categoryPath.getName(); - String path = categoryPath.getPath(); - if (!path.contains(DTM_CLASS_DATA_FOLDER_NAME)) { - throw new IllegalArgumentException("DataType must be in the " + - DTM_CLASS_DATA_FOLDER_NAME + " data type manager folder"); + if (!path.startsWith(DTM_CLASS_DATA_FOLDER_PATH)) { + return null; } - path = path.replace("/" + DTM_CLASS_DATA_FOLDER_NAME + "/", ""); + // strip off the leading class folder path and replace /'s with ::'s to get + // class namespace path + path = path.substring(DTM_CLASS_DATA_FOLDER_PATH.length()); + // TODO: update with regex to exclude very unlikely \/ case path = path.replace("/", "::"); - Iterator classNamespaces = program.getSymbolTable().getClassNamespaces(); + List namespaceByPath = + NamespaceUtils.getNamespaceByPath(program, null, path); - while (classNamespaces.hasNext()) { - monitor.checkCanceled(); - Namespace namespace = classNamespaces.next(); - if (!namespace.getName().equals(className)) { - continue; - } - String fullName = namespace.getName(true); - if (fullName.equals(path)) { + // ignore namespaces contained within libraries + for (Namespace namespace : namespaceByPath) { + if (!namespace.isExternal()) { return namespace; } } + Msg.debug(this, "Expected clas namespace not found: " + path); return null; }