mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 22:05:50 +08:00
GP-1044 Fixed promotion of namespaces to only verified classes and added creation of typeinfo structs in stripped gcc binaries
This commit is contained in:
@@ -163,6 +163,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||||||
}
|
}
|
||||||
else if (isGcc()) {
|
else if (isGcc()) {
|
||||||
|
|
||||||
|
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.");
|
||||||
|
if (!runGcc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
nameVfunctions = true;
|
nameVfunctions = true;
|
||||||
recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation,
|
recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation,
|
||||||
state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
|
state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
|
||||||
@@ -545,25 +550,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: call this before create data in debug mode from script
|
|
||||||
private void findClassesWithErrors(List<RecoveredClass> recoveredClasses)
|
|
||||||
throws CancelledException {
|
|
||||||
|
|
||||||
Iterator<RecoveredClass> iterator = recoveredClasses.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
monitor.checkCanceled();
|
|
||||||
RecoveredClass recoveredClass = iterator.next();
|
|
||||||
if (hasConstructorDestructorDiscrepancy(recoveredClass)) {
|
|
||||||
println(recoveredClass.getName() + " has function on both c and d lists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to analyze the program changes with the decompiler parameter ID analyzer
|
* Method to analyze the program changes with the decompiler parameter ID analyzer
|
||||||
* @param set the set of addresses to analyze
|
* @param set the set of addresses to analyze
|
||||||
|
|||||||
@@ -131,25 +131,25 @@ public class RTTIClassRecoverer extends RecoveredClassUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to promote the namespace is a class namespace.
|
* Method to promote the namespace is a class namespace.
|
||||||
* @param vftableNamespace the namespace for the vftable
|
* @param namespace the namespace for the vftable
|
||||||
* @return true if namespace is (now) a class namespace or false if it could not be promoted.
|
* @return true if namespace is (now) a class namespace or false if it could not be promoted.
|
||||||
*/
|
*/
|
||||||
public Namespace promoteToClassNamespace(Namespace vftableNamespace) {
|
public Namespace promoteToClassNamespace(Namespace namespace) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Namespace newClass = NamespaceUtils.convertNamespaceToClass(vftableNamespace);
|
Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace);
|
||||||
|
|
||||||
SymbolType symbolType = newClass.getSymbol().getSymbolType();
|
SymbolType symbolType = newClass.getSymbol().getSymbolType();
|
||||||
if (symbolType == SymbolType.CLASS) {
|
if (symbolType == SymbolType.CLASS) {
|
||||||
return newClass;
|
return newClass;
|
||||||
}
|
}
|
||||||
Msg.debug(this,
|
Msg.debug(this,
|
||||||
"Could not promote " + vftableNamespace.getName() + " to a class namespace");
|
"Could not promote " + namespace.getName() + " to a class namespace");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (InvalidInputException e) {
|
catch (InvalidInputException e) {
|
||||||
|
|
||||||
Msg.debug(this, "Could not promote " + vftableNamespace.getName() +
|
Msg.debug(this, "Could not promote " + namespace.getName() +
|
||||||
" to a class namespace because " + e.getMessage());
|
" to a class namespace because " + e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+423
-108
File diff suppressed because it is too large
Load Diff
+12
-8
@@ -47,6 +47,7 @@ import ghidra.program.model.pcode.HighFunction;
|
|||||||
import ghidra.program.model.pcode.HighVariable;
|
import ghidra.program.model.pcode.HighVariable;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -937,10 +938,13 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||||||
Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace();
|
Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace();
|
||||||
|
|
||||||
if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) {
|
if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) {
|
||||||
// println("RTTI_Class_Hierarchy_Descriptor at " +
|
classNamespace = promoteToClassNamespace(classNamespace);
|
||||||
// classHierarchyDescriptorAddress.toString() +
|
if(classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) {
|
||||||
// " is not in a class namespace. Cannot process.");
|
Msg.debug(this,
|
||||||
continue;
|
classHierarchyDescriptorAddress.toString() + " Could not promote " +
|
||||||
|
classNamespace.getName(true) + " to a class namespace.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Symbol> vftableSymbolsInNamespace =
|
List<Symbol> vftableSymbolsInNamespace =
|
||||||
@@ -975,12 +979,12 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||||||
List<RecoveredClass> classesWithVftablesInNamespace =
|
List<RecoveredClass> classesWithVftablesInNamespace =
|
||||||
recoverClassesFromVftables(vftableSymbolsInNamespace, false, false);
|
recoverClassesFromVftables(vftableSymbolsInNamespace, false, false);
|
||||||
if (classesWithVftablesInNamespace.size() == 0) {
|
if (classesWithVftablesInNamespace.size() == 0) {
|
||||||
//println("No class recovered for namespace " + classNamespace.getName());
|
Msg.debug(this,"No class recovered for namespace " + classNamespace.getName());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (classesWithVftablesInNamespace.size() > 1) {
|
if (classesWithVftablesInNamespace.size() > 1) {
|
||||||
// println("Unexpected multiple classes recovered for namespace " +
|
Msg.debug(this,"Unexpected multiple classes recovered for namespace " +
|
||||||
// classNamespace.getName());
|
classNamespace.getName());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1232,7 +1236,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||||||
// if the namespace isn't in the map then it is a class
|
// if the namespace isn't in the map then it is a class
|
||||||
// without a vftable and a new RecoveredClass object needs to be created
|
// without a vftable and a new RecoveredClass object needs to be created
|
||||||
if (getClass(pointedToNamespace) == null) {
|
if (getClass(pointedToNamespace) == null) {
|
||||||
addNoVftableClass(pointedToNamespace);
|
createNewClass(pointedToNamespace, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoveredClass pointedToClass = getClass(pointedToNamespace);
|
RecoveredClass pointedToClass = getClass(pointedToNamespace);
|
||||||
|
|||||||
@@ -2672,26 +2672,29 @@ public class RecoveredClassUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to add class with no vftable to the namespace map
|
* Method to create a new recovered class object and add it to the namespaceToClassMap
|
||||||
* @param namespace the namespace to put the new class in
|
* @param namespace the namespace to put the new class in
|
||||||
* @return the recovered class\
|
* @param hasVftable true if class has at least one vftable, false otherwise
|
||||||
|
* @return the RecoveredClass object
|
||||||
* @throws CancelledException if cancelled
|
* @throws CancelledException if cancelled
|
||||||
*/
|
*/
|
||||||
public RecoveredClass addNoVftableClass(Namespace namespace) throws CancelledException {
|
public RecoveredClass createNewClass(Namespace namespace, boolean hasVftable)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
String className = namespace.getName();
|
String className = namespace.getName();
|
||||||
String classNameWithNamespace = namespace.getName(true);
|
String classNameWithNamespace = namespace.getName(true);
|
||||||
CategoryPath classPath =
|
|
||||||
extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath,
|
|
||||||
classNameWithNamespace);
|
|
||||||
|
|
||||||
RecoveredClass nonVftableClass =
|
CategoryPath classPath = extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath,
|
||||||
|
classNameWithNamespace);
|
||||||
|
|
||||||
|
RecoveredClass newClass =
|
||||||
new RecoveredClass(className, classPath, namespace, dataTypeManager);
|
new RecoveredClass(className, classPath, namespace, dataTypeManager);
|
||||||
nonVftableClass.setHasVftable(false);
|
newClass.setHasVftable(hasVftable);
|
||||||
|
|
||||||
updateNamespaceToClassMap(namespace, nonVftableClass);
|
updateNamespaceToClassMap(namespace, newClass);
|
||||||
return nonVftableClass;
|
return newClass;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2735,24 +2738,7 @@ public class RecoveredClassUtils {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// promote any non-class namespaces in the vftableNamespace path to class namespaces
|
|
||||||
boolean success = promoteNamespaces(vftableNamespace.getSymbol());
|
|
||||||
if (!success) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Msg.debug(this, "Unable to promote all non-class namespaces for " +
|
|
||||||
vftableNamespace.getName(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String className = vftableNamespace.getName();
|
|
||||||
String classNameWithNamespace = vftableNamespace.getName(true);
|
|
||||||
|
|
||||||
// Create Data Type Manager Category for given class
|
|
||||||
CategoryPath classPath =
|
|
||||||
extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath,
|
|
||||||
classNameWithNamespace);
|
|
||||||
|
|
||||||
// get only the functions from the ones that are not already processed structures
|
// get only the functions from the ones that are not already processed structures
|
||||||
// return null if not an unprocessed table
|
// return null if not an unprocessed table
|
||||||
List<Function> virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol,
|
List<Function> virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol,
|
||||||
@@ -2764,24 +2750,24 @@ public class RecoveredClassUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if already have an existing RecoveredClass object for the
|
// Check to see if already have an existing RecoveredClass object for the
|
||||||
// class associated with the current vftable. If so, it indicates multi-inheritance
|
// class associated with the current vftable.
|
||||||
RecoveredClass recoveredClass = getClass(vftableNamespace);
|
RecoveredClass recoveredClass = getClass(vftableNamespace);
|
||||||
|
|
||||||
if (recoveredClass == null) {
|
if (recoveredClass == null) {
|
||||||
// Create a RecoveredClass object for the current class
|
// Create a RecoveredClass object for the current class
|
||||||
recoveredClass =
|
recoveredClass = createNewClass(vftableNamespace, true);
|
||||||
new RecoveredClass(className, classPath, vftableNamespace, dataTypeManager);
|
|
||||||
recoveredClass.addVftableAddress(vftableAddress);
|
recoveredClass.addVftableAddress(vftableAddress);
|
||||||
recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions);
|
recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions);
|
||||||
|
|
||||||
// add recovered class to map
|
|
||||||
updateNamespaceToClassMap(vftableNamespace, recoveredClass);
|
|
||||||
// add it to the running list of RecoveredClass objects
|
// add it to the running list of RecoveredClass objects
|
||||||
recoveredClasses.add(recoveredClass);
|
recoveredClasses.add(recoveredClass);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
recoveredClass.addVftableAddress(vftableAddress);
|
recoveredClass.addVftableAddress(vftableAddress);
|
||||||
recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions);
|
recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions);
|
||||||
|
if (!recoveredClasses.contains(recoveredClass)) {
|
||||||
|
recoveredClasses.add(recoveredClass);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2795,8 +2781,6 @@ public class RecoveredClassUtils {
|
|||||||
Map<Address, Function> vftableReferenceToFunctionMapping =
|
Map<Address, Function> vftableReferenceToFunctionMapping =
|
||||||
createVftableReferenceToFunctionMapping(referencesToVftable);
|
createVftableReferenceToFunctionMapping(referencesToVftable);
|
||||||
|
|
||||||
// add this smaller mapping set to the global map
|
|
||||||
//vftableRefToFunctionMap.putAll(vftableReferenceToFunctionMapping);
|
|
||||||
|
|
||||||
//vftableReferenceToFunctionMapping
|
//vftableReferenceToFunctionMapping
|
||||||
List<Function> possibleConstructorDestructorsForThisClass =
|
List<Function> possibleConstructorDestructorsForThisClass =
|
||||||
@@ -2819,25 +2803,38 @@ public class RecoveredClassUtils {
|
|||||||
return recoveredClasses;
|
return recoveredClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean promoteNamespaces(Symbol symbol) throws CancelledException {
|
public void promoteClassNamespaces(List<RecoveredClass> recoveredClasses)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
Iterator<RecoveredClass> classIterator = recoveredClasses.iterator();
|
||||||
|
while (classIterator.hasNext()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
RecoveredClass recoveredClass = classIterator.next();
|
||||||
|
Namespace classNamespace = recoveredClass.getClassNamespace();
|
||||||
|
promoteNamespaces(classNamespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean promoteNamespaces(Namespace namespace) throws CancelledException {
|
||||||
|
|
||||||
Namespace namespace = symbol.getParentNamespace();
|
|
||||||
while (!namespace.isGlobal()) {
|
while (!namespace.isGlobal()) {
|
||||||
|
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
SymbolType namespaceType = namespace.getSymbol().getSymbolType();
|
SymbolType namespaceType = namespace.getSymbol().getSymbolType();
|
||||||
if (namespaceType != SymbolType.CLASS) {
|
// if it is a namespace but not a class and it is in our namespace map (which makes
|
||||||
// if it is a namespace but not a class we need to promote it to a class namespace
|
// it a valid class) we need to promote it to a class namespace
|
||||||
if (namespaceType == SymbolType.NAMESPACE) {
|
if (namespaceType != SymbolType.CLASS && namespaceType == SymbolType.NAMESPACE &&
|
||||||
namespace = promoteToClassNamespace(namespace);
|
namespaceToClassMap.get(namespace) != null) {
|
||||||
if (namespace == null) {
|
|
||||||
return false;
|
namespace = promoteToClassNamespace(namespace);
|
||||||
}
|
if (namespace == null) {
|
||||||
if (DEBUG) {
|
return false;
|
||||||
Msg.debug(this,
|
|
||||||
"Promoted namespace " + namespace.getName() + " to a class namespace");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//if (DEBUG) {
|
||||||
|
Msg.debug(this,
|
||||||
|
"Promoted namespace " + namespace.getName(true) + " to a class namespace");
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
namespace = namespace.getParentNamespace();
|
namespace = namespace.getParentNamespace();
|
||||||
@@ -2852,11 +2849,20 @@ public class RecoveredClassUtils {
|
|||||||
*/
|
*/
|
||||||
private Namespace promoteToClassNamespace(Namespace namespace) {
|
private Namespace promoteToClassNamespace(Namespace namespace) {
|
||||||
|
|
||||||
|
SymbolType symbolType = namespace.getSymbol().getSymbolType();
|
||||||
|
if (symbolType == SymbolType.CLASS) {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbolType != SymbolType.NAMESPACE) {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace);
|
Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace);
|
||||||
|
|
||||||
SymbolType symbolType = newClass.getSymbol().getSymbolType();
|
SymbolType newSymbolType = newClass.getSymbol().getSymbolType();
|
||||||
if (symbolType == SymbolType.CLASS) {
|
if (newSymbolType == SymbolType.CLASS) {
|
||||||
return newClass;
|
return newClass;
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -3246,7 +3252,8 @@ public class RecoveredClassUtils {
|
|||||||
|
|
||||||
if (parentClasses.isEmpty()) {
|
if (parentClasses.isEmpty()) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
recoveredClass.getName() + " should not have an empty class hierarchy");
|
recoveredClass.getClassNamespace().getName(true) +
|
||||||
|
" should not have an empty class hierarchy");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if size one it only includes self
|
// if size one it only includes self
|
||||||
|
|||||||
Reference in New Issue
Block a user