mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
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.
This commit is contained in:
parent
2c561c875b
commit
0766c30048
7 changed files with 207 additions and 36 deletions
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -69,15 +69,19 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
|
|||
new HashMap<RecoveredClass, Map<RecoveredClass, Long>>();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Address, DataType> vftableToStructureMap) throws CancelledException, Exception {
|
||||
Map<Address, DataType> 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<Function> vFunctions = recoveredClass.getVirtualFunctions(vftableAddress);
|
||||
int vfunctionNumber = 1;
|
||||
Iterator<Function> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue