Merge remote-tracking branch 'origin/GP-1962_ghidra007_better_check_for_existing_classes_SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-05-18 13:37:32 -04:00
commit 864321a060
5 changed files with 126 additions and 124 deletions

View File

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

View File

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

View File

@ -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<Address> typeinfoAddressesToProcess = new ArrayList<Address>();
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)

View File

@ -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<RecoveredClass> recoveredClasses)
throws CancelledException, MemoryAccessException, AddressOutOfBoundsException {
throws Exception {
// Use RTTI information to determine inheritance type and
// class hierarchy

View File

@ -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<FunctionDefinition> 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<FunctionDefinition> functionDefs = new ArrayList<FunctionDefinition>();
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<GhidraClass> classNamespaces = program.getSymbolTable().getClassNamespaces();
List<Namespace> 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;
}