GP-5831 Added a few speed improvements to the RecoverClassesFromRTTIScript.

This commit is contained in:
ghidra007
2025-12-03 05:05:17 -05:00
committed by Ryan Kurtz
parent f901a1bb4a
commit c5beedac5a
5 changed files with 339 additions and 113 deletions

View File

@@ -116,7 +116,8 @@ public class DecompilerScriptUtils {
if (decompRes == null || decompRes.getHighFunction() == null || if (decompRes == null || decompRes.getHighFunction() == null ||
decompRes.getHighFunction().getFunctionPrototype() == null) { decompRes.getHighFunction().getFunctionPrototype() == null) {
Msg.debug(this, "Couldn't commit params - null high function"); Msg.debug(this, "Couldn't commit params - null high function " +
function.getEntryPoint().toString());
return; return;
} }
@@ -125,11 +126,13 @@ public class DecompilerScriptUtils {
ReturnCommitOption.COMMIT, SourceType.ANALYSIS); ReturnCommitOption.COMMIT, SourceType.ANALYSIS);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
Msg.debug(this, "Couldn't commit params " + e); Msg.debug(this,
"Couldn't commit params for " + function.getEntryPoint().toString() + " " + e);
return; return;
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
Msg.debug(this, "Couldn't commit params " + e); Msg.debug(this,
"Couldn't commit params for " + function.getEntryPoint().toString() + " " + e);
return; return;
} }
} }

View File

@@ -324,6 +324,12 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
functionAddress = functionAddress.getNewAddress(longValue); functionAddress = functionAddress.getNewAddress(longValue);
} }
// skip anything that is data - this could get passed in if it is an external ptr to a function
Data data = getDataAt(functionAddress);
if (data != null) {
return null;
}
Function function = getFunctionAt(functionAddress); Function function = getFunctionAt(functionAddress);
if (function == null) { if (function == null) {
// try to create function // try to create function
@@ -1002,6 +1008,9 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
* @return the referenced function or null if no function is referenced * @return the referenced function or null if no function is referenced
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
//TODO: this is same as getReferencedFunction at line 313 except for the thunked function
// argument but is missing the lowBit code stuff - combine both and replace uses to use the
// one merged version - this one has loop and the other just works if one ref
public Function getReferencedFunction(Address address, boolean getThunkedFunction) public Function getReferencedFunction(Address address, boolean getThunkedFunction)
throws CancelledException { throws CancelledException {
@@ -1020,6 +1029,19 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
continue; continue;
} }
Register lowBitCodeMode = currentProgram.getRegister("LowBitCodeMode");
if (lowBitCodeMode != null) {
long longValue = referencedAddress.getOffset();
longValue = longValue & ~0x1;
referencedAddress = referencedAddress.getNewAddress(longValue);
}
// skip anything that is data - this could get passed in if it is an external ptr to a function
Data data = getDataAt(referencedAddress);
if (data != null) {
continue;
}
Function function = getFunctionAt(referencedAddress); Function function = getFunctionAt(referencedAddress);
if (function == null) { if (function == null) {
@@ -1197,7 +1219,7 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
} }
/** /**
* Method to generate unique shorted names for classes with templates * Method to generate unique shortened names for classes with templates
* @param recoveredClasses the list of classes in the program * @param recoveredClasses the list of classes in the program
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */

View File

@@ -167,15 +167,16 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// using all the information found above, create the class structures, add the constructor, // using all the information found above, create the class structures, add the constructor,
// destructor, vfunctions to class which finds the appropriate class structure and assigns // destructor, vfunctions to class which finds the appropriate class structure and assigns
// to "this" param // to "this" param
monitor.setMessage("Creating class data types and applying class structures"); monitor.setMessage("Figuring out class data members...");
figureOutClassDataMembers(recoveredClasses); figureOutClassDataMembers(recoveredClasses);
if (USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS) { if (USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS) {
extendedFlatAPI.createShortenedTemplateNamesForClasses(recoveredClasses); extendedFlatAPI.createShortenedTemplateNamesForClasses(recoveredClasses);
} }
monitor.setMessage("Creating class data types and applying class structures...");
createAndApplyClassStructures(recoveredClasses); createAndApplyClassStructures(recoveredClasses);
monitor.setMessage("Finishing up...");
// fix purecall vfunction definitions // fix purecall vfunction definitions
fixupPurecallFunctionDefs(); fixupPurecallFunctionDefs();
@@ -1299,12 +1300,15 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// lists to remove functions that are also on vfunction lists // lists to remove functions that are also on vfunction lists
trimConstructorDestructorLists(recoveredClasses, allVftables); trimConstructorDestructorLists(recoveredClasses, allVftables);
monitor.setMessage("... determining operator_delete and new functions");
determineOperatorDeleteAndNewFunctions(allVftables); determineOperatorDeleteAndNewFunctions(allVftables);
// find deleting destructors // find deleting destructors
monitor.setMessage("... finding deleting destructors");
findDeletingDestructors(recoveredClasses, allVftables); findDeletingDestructors(recoveredClasses, allVftables);
// use atexit param list to find more destructors // use atexit param list to find more destructors
monitor.setMessage("... finding destructors using atexit calls");
findDestructorsUsingAtexitCalledFunctions(recoveredClasses); findDestructorsUsingAtexitCalledFunctions(recoveredClasses);
// figure out which are inlined and put on separate list to be processed later // figure out which are inlined and put on separate list to be processed later
@@ -1312,33 +1316,43 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// figure out which member functions are constructors and which are destructors // figure out which member functions are constructors and which are destructors
// using the order their parents are called // using the order their parents are called
monitor.setMessage("... processing constructors and destructors using call order");
processRegularConstructorsAndDestructorsUsingCallOrder(recoveredClasses); processRegularConstructorsAndDestructorsUsingCallOrder(recoveredClasses);
// determine which of the inlines are constructors and which are destructors // determine which of the inlines are constructors and which are destructors
monitor.setMessage("... processing inlineds");
processInlinedConstructorsAndDestructors(recoveredClasses); processInlinedConstructorsAndDestructors(recoveredClasses);
monitor.setMessage("... finding more constructors and destructors");
findConstructorsAndDestructorsUsingAncestorClassFunctions(recoveredClasses); findConstructorsAndDestructorsUsingAncestorClassFunctions(recoveredClasses);
monitor.setMessage("... finding more inlines");
findInlineConstructorsAndDestructorsUsingRelatedClassFunctions(recoveredClasses); findInlineConstructorsAndDestructorsUsingRelatedClassFunctions(recoveredClasses);
// use the load/store information from decompiler to figure out as many of the // use the load/store information from decompiler to figure out as many of the
// ones that could not be determined in earlier stages // ones that could not be determined in earlier stages
monitor.setMessage("... processing remaining indeterminate constructors and destructors");
processRemainingIndeterminateConstructorsAndDestructors(recoveredClasses); processRemainingIndeterminateConstructorsAndDestructors(recoveredClasses);
// use the known constructors and known vfunctions to figure out basic clone functions // use the known constructors and known vfunctions to figure out basic clone functions
monitor.setMessage("... finding basic clones");
findBasicCloneFunctions(recoveredClasses); findBasicCloneFunctions(recoveredClasses);
// This has to be here. It needs all the info from the previously run methods to do this. // This has to be here. It needs all the info from the previously run methods to do this.
// Finds the constructors that have multiple basic blocks, reference the vftable not in the // Finds the constructors that have multiple basic blocks, reference the vftable not in the
// first block, and call non-parent constructors and non operator new before the vftable ref // first block, and call non-parent constructors and non operator new before the vftable ref
monitor.setMessage("... finding more inlined constructors");
findMoreInlinedConstructors(recoveredClasses); findMoreInlinedConstructors(recoveredClasses);
monitor.setMessage("... finding destructors with no params");
findDestructorsWithNoParamsOrReturn(recoveredClasses); findDestructorsWithNoParamsOrReturn(recoveredClasses);
// use vftables with references to all the same function (except possibly one deleting // use vftables with references to all the same function (except possibly one deleting
// destructor)to find the purecall function // destructor)to find the purecall function
monitor.setMessage("... identifying pure virtual function");
identifyPureVirtualFunction(recoveredClasses); identifyPureVirtualFunction(recoveredClasses);
monitor.setMessage("... finding real vbase functions");
findRealVBaseFunctions(recoveredClasses); findRealVBaseFunctions(recoveredClasses);
// make constructors and destructors _thiscalls // make constructors and destructors _thiscalls
@@ -1905,8 +1919,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
parentOrderMap = new HashMap<Integer, RecoveredClass>(); parentOrderMap = new HashMap<Integer, RecoveredClass>();
Map<Address, RecoveredClass> referenceToParentMap = Map<Address, ReferencedClassObject> referenceToParentMap =
getReferenceToClassMap(recoveredClass, function); getReferenceToReferencedObjectsMap(recoveredClass, function);
Map<Address, RecoveredClass> allowedReferncesToParentMap = Map<Address, RecoveredClass> allowedReferncesToParentMap =
new HashMap<Address, RecoveredClass>(); new HashMap<Address, RecoveredClass>();
@@ -1933,8 +1947,9 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
recoveredClass.getVftableAddresses().contains(possibleVftable)) { recoveredClass.getVftableAddresses().contains(possibleVftable)) {
continue; continue;
} }
ReferencedClassObject referencedClassObject =
RecoveredClass ancestorClass = referenceToParentMap.get(classReferenceAddress); referenceToParentMap.get(classReferenceAddress);
RecoveredClass ancestorClass = referencedClassObject.getContainingClass();
if (allowedAncestors.contains(ancestorClass)) { if (allowedAncestors.contains(ancestorClass)) {
allowedReferncesToParentMap.put(classReferenceAddress, ancestorClass); allowedReferncesToParentMap.put(classReferenceAddress, ancestorClass);
} }
@@ -1955,7 +1970,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// iterate over the ordered parents and add the order to the parent map // iterate over the ordered parents and add the order to the parent map
for (Address refAddress : parentReferences) { for (Address refAddress : parentReferences) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass parentClass = referenceToParentMap.get(refAddress); ReferencedClassObject referencedClassObject = referenceToParentMap.get(refAddress);
RecoveredClass parentClass = referencedClassObject.getContainingClass();
parentOrderMap.put(order, parentClass); parentOrderMap.put(order, parentClass);
order++; order++;
} }
@@ -2077,16 +2093,12 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
private void createAndApplyClassStructures(List<RecoveredClass> recoveredClasses) private void createAndApplyClassStructures(List<RecoveredClass> recoveredClasses)
throws CancelledException, Exception { throws CancelledException, Exception {
List<RecoveredClass> listOfClasses = new ArrayList<RecoveredClass>(recoveredClasses); List<RecoveredClass> processedClasses = new ArrayList<>();
Iterator<RecoveredClass> recoveredClassIterator = recoveredClasses.iterator();
// first process all the classes with no parents // first process all the classes with no parents
while (recoveredClassIterator.hasNext()) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
if (recoveredClass.hasMultipleInheritance()) { if (recoveredClass.hasMultipleInheritance()) {
continue; continue;
} }
@@ -2097,20 +2109,19 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
if (!recoveredClass.hasVftable()) { if (!recoveredClass.hasVftable()) {
createClassStructureWhenNoParentOrVftable(recoveredClass); createClassStructureWhenNoParentOrVftable(recoveredClass);
listOfClasses.remove(recoveredClass); processedClasses.add(recoveredClass);
continue; continue;
} }
processDataTypes(recoveredClass); processDataTypes(recoveredClass);
listOfClasses.remove(recoveredClass); processedClasses.add(recoveredClass);
} }
// now process the classes that have all parents processed // now process the classes that have all parents processed
// continue looping until all classes are processed // continue looping until all classes are processed
int numLoops = 0; int numLoops = 0;
while (!listOfClasses.isEmpty()) { while (processedClasses.size() < recoveredClasses.size()) {
monitor.checkCancelled(); monitor.checkCancelled();
// put in stop gap measure in case some classes never get all // put in stop gap measure in case some classes never get all
@@ -2120,13 +2131,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
} }
numLoops++; numLoops++;
recoveredClassIterator = recoveredClasses.iterator(); for (RecoveredClass recoveredClass : recoveredClasses) {
while (recoveredClassIterator.hasNext()) {
RecoveredClass recoveredClass = recoveredClassIterator.next();
monitor.checkCancelled(); monitor.checkCancelled();
if (!listOfClasses.contains(recoveredClass)) {
if (processedClasses.contains(recoveredClass)) {
continue; continue;
} }
@@ -2135,8 +2143,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
} }
processDataTypes(recoveredClass); processDataTypes(recoveredClass);
listOfClasses.remove(recoveredClass); processedClasses.add(recoveredClass);
} }
} }
} }
@@ -2673,11 +2680,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
continue; continue;
} }
if (numAddressRanges == 2) { if (numAddressRanges == 2) {
// else fixup split dd function // else possible split dd function - try to split and created second function
// if it is one
Function scalarDeletingDestructor = createSplitDeletingDestructorFunction(body); Function scalarDeletingDestructor = createSplitDeletingDestructorFunction(body);
if (scalarDeletingDestructor == null) { if (scalarDeletingDestructor == null) {
Msg.debug(this, "Could not fixup split deleting destructor function: " +
function.getEntryPoint());
continue; continue;
} }
fixupSplitDeletingDestructorSymbols(function, scalarDeletingDestructor); fixupSplitDeletingDestructorSymbols(function, scalarDeletingDestructor);

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -96,7 +96,6 @@ public class RecoveredClass {
TaskMonitor monitor = TaskMonitor.DUMMY; TaskMonitor monitor = TaskMonitor.DUMMY;
RecoveredClass(String name, CategoryPath classPath, Namespace classNamespace, RecoveredClass(String name, CategoryPath classPath, Namespace classNamespace,
DataTypeManager dataTypeManager) { DataTypeManager dataTypeManager) {
this.name = name; this.name = name;
@@ -519,19 +518,19 @@ public class RecoveredClass {
// if the new component is a non-empty structure, check to see if the current // if the new component is a non-empty structure, check to see if the current
// structure has undefined or equivalent components and replace with new struct if so // structure has undefined or equivalent components and replace with new struct if so
if (newComponentDataType instanceof Structure) { if (newComponentDataType instanceof Structure) {
// if new component is any empty placeholder structure AND if the existing component // if new component is any empty placeholder structure AND if the existing component
// is undefined then replace with undefined1 dt // is undefined then replace with undefined1 dt
if (newComponentDataType.isNotYetDefined()) { if (newComponentDataType.isNotYetDefined()) {
if (Undefined.isUndefined(currentComponentDataType)) { if (Undefined.isUndefined(currentComponentDataType)) {
computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1, computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1,
fieldName, comment); fieldName, comment);
} }
continue; continue;
} }
if (EditStructureUtils.hasReplaceableComponentsAtOffset(computedClassStructure, if (EditStructureUtils.hasReplaceableComponentsAtOffset(computedClassStructure,
offset, (Structure)newComponentDataType, monitor)) { offset, (Structure) newComponentDataType, monitor)) {
boolean successfulClear = boolean successfulClear =
EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset, EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset,
length, monitor); length, monitor);
@@ -675,4 +674,3 @@ public class RecoveredClass {
return shortenedTemplateName; return shortenedTemplateName;
} }
} }

View File

@@ -141,6 +141,15 @@ public class RecoveredClassHelper {
public HashMap<Address, Set<Function>> allVfunctions = new HashMap<>(); public HashMap<Address, Set<Function>> allVfunctions = new HashMap<>();
public HashMap<Function, Map<Address, Function>> functionCallMapFollowThunks =
new HashMap<>();
public HashMap<Function, Map<Address, Function>> functionCallMapDontFollowThunks =
new HashMap<>();
public HashMap<Address, ReferencedClassObject> referencedObjectMap = new HashMap<>();
public HashMap<Function, HashMap<Address, ReferencedClassObject>> functionReferenceMap =
new HashMap<>();
public RecoveredClassHelper(Program program, ServiceProvider serviceProvider, public RecoveredClassHelper(Program program, ServiceProvider serviceProvider,
FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates,
boolean nameVunctions, boolean makeVfunctionsThisCalls, TaskMonitor monitor) boolean nameVunctions, boolean makeVfunctionsThisCalls, TaskMonitor monitor)
@@ -416,16 +425,30 @@ public class RecoveredClassHelper {
* @param function the given function * @param function the given function
* @param getThunkedFunction if true, use the thunked function in the map, if false use the * @param getThunkedFunction if true, use the thunked function in the map, if false use the
* directly called function from the calling function even if it is a thunk * directly called function from the calling function even if it is a thunk
* @param visited the set of function entry point addresses already processed
* @return a map of the given functions calling addresses to the called functions * @return a map of the given functions calling addresses to the called functions
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
public Map<Address, Function> getFunctionCallMap(Function function, boolean getThunkedFunction, public Map<Address, Function> getFunctionCallMap(Function function, boolean getThunkedFunction)
Set<Address> visited)
throws CancelledException { throws CancelledException {
visited.add(function.getEntryPoint()); Map<Address, Function> functionCallMap;
Map<Address, Function> functionCallMap = new HashMap<Address, Function>();
if (getThunkedFunction) {
functionCallMap = functionCallMapFollowThunks.get(function);
}
else {
functionCallMap = functionCallMapDontFollowThunks.get(function);
}
if (functionCallMap != null) {
return functionCallMap;
}
// initializing here not above because checking for empty map with the null
// wouldn't be correct because the below could generate an empty map if there are no
// calls at all. The above map check/return needs to distinguish between the not in map
// case and the already processed empty map case
functionCallMap = new HashMap<Address, Function>();
InstructionIterator instructions = InstructionIterator instructions =
function.getProgram().getListing().getInstructions(function.getBody(), true); function.getProgram().getListing().getInstructions(function.getBody(), true);
@@ -442,8 +465,14 @@ public class RecoveredClassHelper {
continue; continue;
} }
if (calledFunction.isExternal()) {
continue;
}
// include the null functions in map so things using map can get accurate count // include the null functions in map so things using map can get accurate count
// of number of CALL instructions even if the call reg type // of number of CALL instructions even if the call reg type
//TODO: the above continue is preventing the nulls and exts here - do we want this?
// or do we want another map/option?
functionCallMap.put(instruction.getMinAddress(), calledFunction); functionCallMap.put(instruction.getMinAddress(), calledFunction);
} }
if (instruction.getFlowOverride().equals(FlowOverride.CALL_RETURN)) { if (instruction.getFlowOverride().equals(FlowOverride.CALL_RETURN)) {
@@ -454,10 +483,9 @@ public class RecoveredClassHelper {
Address functionAddress = reference.getFromAddress(); Address functionAddress = reference.getFromAddress();
Function secondHalfOfFunction = Function secondHalfOfFunction =
extendedFlatAPI.getReferencedFunction(functionAddress); extendedFlatAPI.getReferencedFunction(functionAddress);
if (secondHalfOfFunction != null && if (secondHalfOfFunction != null) {
!visited.contains(secondHalfOfFunction.getEntryPoint())) {
Map<Address, Function> functionCallMap2 = Map<Address, Function> functionCallMap2 =
getFunctionCallMap(secondHalfOfFunction, false, visited); getFunctionCallMap(secondHalfOfFunction, false);
for (Address addr : functionCallMap2.keySet()) { for (Address addr : functionCallMap2.keySet()) {
monitor.checkCancelled(); monitor.checkCancelled();
functionCallMap.put(addr, functionCallMap2.get(addr)); functionCallMap.put(addr, functionCallMap2.get(addr));
@@ -466,12 +494,14 @@ public class RecoveredClassHelper {
} }
} }
return functionCallMap;
}
public Map<Address, Function> getFunctionCallMap(Function function, boolean getThunkedFunction) if (getThunkedFunction) {
throws CancelledException { functionCallMapFollowThunks.put(function, functionCallMap);
return getFunctionCallMap(function, getThunkedFunction, new HashSet<>()); }
else {
functionCallMapDontFollowThunks.put(function, functionCallMap);
}
return functionCallMap;
} }
public void updateNamespaceToClassMap(Namespace namespace, RecoveredClass recoveredClass) { public void updateNamespaceToClassMap(Namespace namespace, RecoveredClass recoveredClass) {
@@ -714,19 +744,26 @@ public class RecoveredClassHelper {
public List<Address> getSortedListOfAncestorRefsInFunction(Function function, public List<Address> getSortedListOfAncestorRefsInFunction(Function function,
RecoveredClass recoveredClass) throws CancelledException { RecoveredClass recoveredClass) throws CancelledException {
// get the map of all referenced vftables or constructor/desstructor calls in this function // get the addresses in the function that refer to classes either by
Map<Address, RecoveredClass> referenceToClassMapForFunction = // referencing a vftable in a class or by calling a function in a class
getReferenceToClassMap(recoveredClass, function); Map<Address, ReferencedClassObject> referenceToClassMap =
functionReferenceMap.get(function);
if (referenceToClassMap == null) {
referenceToClassMap =
getReferenceToReferencedObjectsMap(recoveredClass, function);
}
// get a list of all ancestor classes referenced in the map // get a list of all ancestor classes referenced in the map
List<RecoveredClass> classHierarchy = recoveredClass.getClassHierarchy(); List<RecoveredClass> classHierarchy = recoveredClass.getClassHierarchy();
// make a list of all related class references // make a list of all related class references
List<Address> listOfAncestorRefs = new ArrayList<Address>(); List<Address> listOfAncestorRefs = new ArrayList<Address>();
Set<Address> ancestorRefs = referenceToClassMapForFunction.keySet(); Set<Address> ancestorRefs = referenceToClassMap.keySet();
for (Address ancestorRef : ancestorRefs) { for (Address ancestorRef : ancestorRefs) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass mappedClass = referenceToClassMapForFunction.get(ancestorRef);
ReferencedClassObject referencedClassObject = referenceToClassMap.get(ancestorRef);
RecoveredClass mappedClass = referencedClassObject.getContainingClass();
if (classHierarchy.contains(mappedClass)) { if (classHierarchy.contains(mappedClass)) {
listOfAncestorRefs.add(ancestorRef); listOfAncestorRefs.add(ancestorRef);
} }
@@ -745,10 +782,11 @@ public class RecoveredClassHelper {
* @return Map of Address references to Class object for the given function * @return Map of Address references to Class object for the given function
* @throws CancelledException when cancelled * @throws CancelledException when cancelled
*/ */
public Map<Address, RecoveredClass> getReferenceToClassMap(RecoveredClass recoveredClass, public Map<Address, ReferencedClassObject> getReferenceToReferencedObjectsMap(
RecoveredClass recoveredClass,
Function function) throws CancelledException { Function function) throws CancelledException {
Map<Address, RecoveredClass> referenceToParentMap = new HashMap<Address, RecoveredClass>(); Map<Address, ReferencedClassObject> referenceToParentMap = new HashMap<>();
List<Address> vftableRefs = functionToVftableRefsMap.get(function); List<Address> vftableRefs = functionToVftableRefsMap.get(function);
@@ -768,7 +806,9 @@ public class RecoveredClassHelper {
} }
RecoveredClass parentClass = vftableToClassMap.get(vftableAddress); RecoveredClass parentClass = vftableToClassMap.get(vftableAddress);
referenceToParentMap.put(vftableRef, parentClass); ReferencedClassObject referencedObject =
new ReferencedClassObject(vftableRef, vftableAddress, parentClass);
referenceToParentMap.put(vftableRef, referencedObject);
} }
// remove duplicate vftable refs (occasionally there are LEA then MOV of same vftable address // remove duplicate vftable refs (occasionally there are LEA then MOV of same vftable address
@@ -808,7 +848,9 @@ public class RecoveredClassHelper {
continue; continue;
} }
referenceToParentMap.put(address, ancestorClass); ReferencedClassObject referencedObject =
new ReferencedClassObject(address, calledFunction, ancestorClass);
referenceToParentMap.put(address, referencedObject);
} }
return referenceToParentMap; return referenceToParentMap;
@@ -840,7 +882,7 @@ public class RecoveredClassHelper {
return badFIDFunctions; return badFIDFunctions;
} }
private Map<Address, RecoveredClass> dedupeMap(Map<Address, RecoveredClass> map) private Map<Address, ReferencedClassObject> dedupeMap(Map<Address, ReferencedClassObject> map)
throws CancelledException { throws CancelledException {
// Sort the vftable refs in the order they appear in the function // Sort the vftable refs in the order they appear in the function
@@ -852,7 +894,9 @@ public class RecoveredClassHelper {
Address lastVftableRef = null; Address lastVftableRef = null;
for (Address vftableRef : vftableRefList) { for (Address vftableRef : vftableRefList) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass currentClass = map.get(vftableRef);
ReferencedClassObject referencedObject = map.get(vftableRef);
RecoveredClass currentClass = referencedObject.getContainingClass();
if (lastClass != null && lastClass.equals(currentClass)) { if (lastClass != null && lastClass.equals(currentClass)) {
// if vftable refs are within a few instructions, dedupe map // if vftable refs are within a few instructions, dedupe map
@@ -2108,17 +2152,21 @@ public class RecoveredClassHelper {
* @throws DuplicateNameException if try to create same symbol name already in namespace * @throws DuplicateNameException if try to create same symbol name already in namespace
* @throws CircularDependencyException if parent namespace is descendant of given namespace * @throws CircularDependencyException if parent namespace is descendant of given namespace
*/ */
public void createListedConstructorFunctions(Map<Address, RecoveredClass> referenceToClassMap, public void createListedConstructorFunctions(
Map<Address, ReferencedClassObject> referenceToClassMap,
List<Address> referencesToConstructors) throws CancelledException, List<Address> referencesToConstructors) throws CancelledException,
InvalidInputException, DuplicateNameException, CircularDependencyException { InvalidInputException, DuplicateNameException, CircularDependencyException {
for (Address constructorReference : referencesToConstructors) { for (Address constructorReference : referencesToConstructors) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass recoveredClass = referenceToClassMap.get(constructorReference); ReferencedClassObject referencedClassObject =
referenceToClassMap.get(constructorReference);
RecoveredClass recoveredClass = referencedClassObject.getContainingClass();
Function constructor = Function constructor =
extendedFlatAPI.getReferencedFunction(constructorReference, true); referencedClassObject.getReferencedFunction();
if (constructor == null) { if (constructor == null) {
continue; continue;
@@ -2146,9 +2194,9 @@ public class RecoveredClassHelper {
* @throws DuplicateNameException if try to create same symbol name already in namespace * @throws DuplicateNameException if try to create same symbol name already in namespace
* @throws CircularDependencyException if parent namespace is descendant of given namespace * @throws CircularDependencyException if parent namespace is descendant of given namespace
*/ */
public void processInlineConstructor(RecoveredClass recoveredClass, public void processInlineConstructor(RecoveredClass recoveredClass,
Function inlinedConstructorFunction, Map<Address, RecoveredClass> referenceToClassMap) Function inlinedConstructorFunction,
Map<Address, ReferencedClassObject> referenceToClassMap)
throws CancelledException, InvalidInputException, DuplicateNameException, throws CancelledException, InvalidInputException, DuplicateNameException,
CircularDependencyException { CircularDependencyException {
@@ -2161,6 +2209,7 @@ public class RecoveredClassHelper {
List<Address> referenceAddresses = new ArrayList<Address>(referenceToClassMap.keySet()); List<Address> referenceAddresses = new ArrayList<Address>(referenceToClassMap.keySet());
for (Address reference : referenceAddresses) { for (Address reference : referenceAddresses) {
monitor.checkCancelled(); monitor.checkCancelled();
Address vftableAddress = getVftableAddress(reference); Address vftableAddress = getVftableAddress(reference);
if (vftableAddress != null) { if (vftableAddress != null) {
referencesToVftables.add(reference); referencesToVftables.add(reference);
@@ -2178,7 +2227,10 @@ public class RecoveredClassHelper {
for (Address refToVftable : referencesToVftables) { for (Address refToVftable : referencesToVftables) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass referencedClass = referenceToClassMap.get(refToVftable);
ReferencedClassObject referencedClassObject = referenceToClassMap.get(refToVftable);
RecoveredClass referencedClass = referencedClassObject.getContainingClass();
// last reference is the constructor // last reference is the constructor
if (refToVftable.equals(lastRef)) { if (refToVftable.equals(lastRef)) {
@@ -2206,7 +2258,8 @@ public class RecoveredClassHelper {
* @throws DuplicateNameException if try to create same symbol name already in namespace * @throws DuplicateNameException if try to create same symbol name already in namespace
*/ */
public void processInlineDestructor(RecoveredClass recoveredClass, public void processInlineDestructor(RecoveredClass recoveredClass,
Function inlinedDestructorFunction, Map<Address, RecoveredClass> referenceToClassMap) Function inlinedDestructorFunction,
Map<Address, ReferencedClassObject> referenceToClassMap)
throws CancelledException, InvalidInputException, DuplicateNameException { throws CancelledException, InvalidInputException, DuplicateNameException {
if (referenceToClassMap.isEmpty()) { if (referenceToClassMap.isEmpty()) {
@@ -2236,7 +2289,9 @@ public class RecoveredClassHelper {
for (Address refToVftable : referencesToVftables) { for (Address refToVftable : referencesToVftables) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass referencedClass = referenceToClassMap.get(refToVftable); ReferencedClassObject referencedClassObject = referenceToClassMap.get(refToVftable);
RecoveredClass referencedClass = referencedClassObject.getContainingClass();
// last reference is the constructor // last reference is the constructor
if (refToVftable.equals(lastRef)) { if (refToVftable.equals(lastRef)) {
@@ -2293,7 +2348,7 @@ public class RecoveredClassHelper {
/** /**
* Method to determine if the given function calls a known constructor or inlined constructor * Method to determine if the given function calls a known constructor or inlined constructor
* @param Set of called functions * @param calledFunctions set of called functions
* @return true if calling function calls a known constructor or inlined constructor, false otherwise * @return true if calling function calls a known constructor or inlined constructor, false otherwise
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
@@ -2313,7 +2368,7 @@ public class RecoveredClassHelper {
/** /**
* Method to determine if the given function calls a known denstructor or inlined destructor * Method to determine if the given function calls a known denstructor or inlined destructor
* @param Set of called functions * @param calledFunctions Set of called functions
* @return true if function calls a known constructor or inlined constructor, false otherwise * @return true if function calls a known constructor or inlined constructor, false otherwise
* of its own or none * of its own or none
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
@@ -2536,16 +2591,19 @@ public class RecoveredClassHelper {
* @throws InvalidInputException if error setting return type * @throws InvalidInputException if error setting return type
* @throws DuplicateNameException if try to create same symbol name already in namespace * @throws DuplicateNameException if try to create same symbol name already in namespace
*/ */
public void createListedDestructorFunctions(Map<Address, RecoveredClass> referenceToClassMap, public void createListedDestructorFunctions(
Map<Address, ReferencedClassObject> referenceToClassMap,
List<Address> referencesToDestructors) List<Address> referencesToDestructors)
throws CancelledException, InvalidInputException, DuplicateNameException { throws CancelledException, InvalidInputException, DuplicateNameException {
for (Address destructorReference : referencesToDestructors) { for (Address destructorReference : referencesToDestructors) {
monitor.checkCancelled(); monitor.checkCancelled();
RecoveredClass recoveredClass = referenceToClassMap.get(destructorReference); ReferencedClassObject referencedClassObject =
referenceToClassMap.get(destructorReference);
RecoveredClass recoveredClass = referencedClassObject.getContainingClass();
Function destructor = extendedFlatAPI.getReferencedFunction(destructorReference, true); Function destructor = referencedClassObject.getReferencedFunction();
if (destructor == null) { if (destructor == null) {
continue; continue;
} }
@@ -4871,6 +4929,13 @@ public class RecoveredClassHelper {
Symbol symbol = symbols.next(); Symbol symbol = symbols.next();
// function might be in multiple classes if compiler was optimized to use same function
// for multiple classes with identical functions
// let the class that has the primary symbol do the update
if (!symbol.isPrimary()) {
continue;
}
Function function = functionManager.getFunctionAt(symbol.getAddress()); Function function = functionManager.getFunctionAt(symbol.getAddress());
if (function == null) { if (function == null) {
@@ -6145,6 +6210,8 @@ public class RecoveredClassHelper {
throws CancelledException, InvalidInputException, DuplicateNameException, throws CancelledException, InvalidInputException, DuplicateNameException,
CircularDependencyException { CircularDependencyException {
Set<Function> processedFunctions = new HashSet<>();
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCancelled(); monitor.checkCancelled();
List<Function> inlineFunctionsList = List<Function> inlineFunctionsList =
@@ -6153,12 +6220,20 @@ public class RecoveredClassHelper {
for (Function inlineFunction : inlineFunctionsList) { for (Function inlineFunction : inlineFunctionsList) {
monitor.checkCancelled(); monitor.checkCancelled();
// functions with inlines often contain multiple inlined constructor/destructors
// skip if already processed (processInline<c/d> methods update all of them)
if (processedFunctions.contains(inlineFunction)) {
continue;
}
// get the addresses in the function that refer to classes either by // get the addresses in the function that refer to classes either by
// referencing a vftable in a class or by calling a function in a class // referencing a vftable in a class or by calling a function in a class
Map<Address, RecoveredClass> referenceToClassMap = Map<Address, ReferencedClassObject> referenceToClassMap =
getReferenceToClassMap(recoveredClass, inlineFunction); functionReferenceMap.get(inlineFunction);
List<Address> referencesToFunctions = if (referenceToClassMap == null) {
extendedFlatAPI.getReferencesToFunctions(referenceToClassMap); referenceToClassMap =
getReferenceToReferencedObjectsMap(recoveredClass, inlineFunction);
}
// if some of the references are to functions figure out if they are // if some of the references are to functions figure out if they are
// constructors destructors or add them to list of indetermined // constructors destructors or add them to list of indetermined
@@ -6166,46 +6241,48 @@ public class RecoveredClassHelper {
boolean isDestructor = false; boolean isDestructor = false;
List<Address> referenceToIndeterminates = new ArrayList<Address>(); List<Address> referenceToIndeterminates = new ArrayList<Address>();
if (!referencesToFunctions.isEmpty()) { for (Address referenceAddress : referenceToClassMap.keySet()) {
for (Address functionReference : referencesToFunctions) {
monitor.checkCancelled(); monitor.checkCancelled();
Function function =
extendedFlatAPI.getReferencedFunction(functionReference, true);
if (function == null) { ReferencedClassObject referencedClassObject =
continue; referenceToClassMap.get(referenceAddress);
}
if (getAllConstructors().contains(function) ||
getAllInlinedConstructors().contains(function)) {
isConstructor = true;
continue;
}
if (getAllDestructors().contains(function) ||
getAllInlinedDestructors().contains(function)) {
isDestructor = true;
continue;
}
// TODO: refactor to make this function and refactor method that uses
// it to use function instead of refiguring it out
referenceToIndeterminates.add(functionReference);
Function function = referencedClassObject.getReferencedFunction();
if (function == null) {
continue;
} }
if (getAllConstructors().contains(function) ||
getAllInlinedConstructors().contains(function)) {
isConstructor = true;
continue;
}
if (getAllDestructors().contains(function) ||
getAllInlinedDestructors().contains(function)) {
isDestructor = true;
continue;
}
// TODO: refactor to make this function and refactor method that uses
// it to use function instead of refiguring it out
referenceToIndeterminates.add(referenceAddress);
} }
// if one or more is a constructor and none are destructors then the indeterminate // if one or more is a constructor and none are destructors then the indeterminate
// inline is an inlined constructor // inline is an inlined constructor
if (isConstructor == true && isDestructor == false) { if (isConstructor == true && isDestructor == false) {
processInlineConstructor(recoveredClass, inlineFunction, referenceToClassMap); processInlineConstructor(recoveredClass, inlineFunction,
referenceToClassMap);
processedFunctions.add(inlineFunction);
} }
// if one or more is a destructor and none are constructors then the indeterminate // if one or more is a destructor and none are constructors then the indeterminate
// inline is an inlined destructor // inline is an inlined destructor
else if (isConstructor == false && isDestructor == true) { else if (isConstructor == false && isDestructor == true) {
processInlineDestructor(recoveredClass, inlineFunction, referenceToClassMap); processInlineDestructor(recoveredClass, inlineFunction, referenceToClassMap);
processedFunctions.add(inlineFunction);
} }
else { else {
@@ -6248,6 +6325,7 @@ public class RecoveredClassHelper {
processInlineConstructor(recoveredClass, inlineFunction, processInlineConstructor(recoveredClass, inlineFunction,
referenceToClassMap); referenceToClassMap);
isConstructor = true; isConstructor = true;
processedFunctions.add(inlineFunction);
} }
// inlined destructor // inlined destructor
@@ -6255,6 +6333,7 @@ public class RecoveredClassHelper {
processInlineDestructor(recoveredClass, inlineFunction, processInlineDestructor(recoveredClass, inlineFunction,
referenceToClassMap); referenceToClassMap);
isDestructor = true; isDestructor = true;
processedFunctions.add(inlineFunction);
} }
} }
@@ -6542,12 +6621,14 @@ public class RecoveredClassHelper {
* 3. do not reference a vftable but call own destructor (call func on own c/d list) which * 3. do not reference a vftable but call own destructor (call func on own c/d list) which
* means it is just a deleting destructor for class but has no inlined destructor * means it is just a deleting destructor for class but has no inlined destructor
* @param recoveredClass the given class * @param recoveredClass the given class
* @param vfunction the given vfunction
* @param functionsWithVftableRef all class functions with vftable references
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
* @throws DuplicateNameException if try to create same symbol name already in namespace * @throws DuplicateNameException if try to create same symbol name already in namespace
* @throws InvalidInputException if issues setting return type * @throws InvalidInputException if issues setting return type
*/ */
private boolean processDeletingDestructor(RecoveredClass recoveredClass, Function vfunction, private boolean processDeletingDestructor(RecoveredClass recoveredClass, Function vfunction,
List<Address> allVftables) Set<Function> functionsWithVftableRef)
throws CancelledException, DuplicateNameException, InvalidInputException { throws CancelledException, DuplicateNameException, InvalidInputException {
// if the virtual function IS ALSO on the class constructor/destructor list // if the virtual function IS ALSO on the class constructor/destructor list
@@ -6555,7 +6636,7 @@ public class RecoveredClassHelper {
// determine if the inline is the class or parent/grandparent class destructor // determine if the inline is the class or parent/grandparent class destructor
boolean isDeletingDestructor = false; boolean isDeletingDestructor = false;
if (getAllClassFunctionsWithVtableRef(allVftables).contains(vfunction)) { if (functionsWithVftableRef.contains(vfunction)) {
recoveredClass.addDeletingDestructor(vfunction); recoveredClass.addDeletingDestructor(vfunction);
recoveredClass.removeFromConstructorDestructorList(vfunction); recoveredClass.removeFromConstructorDestructorList(vfunction);
@@ -6727,6 +6808,8 @@ public class RecoveredClassHelper {
List<RecoveredClass> recoveredClasses) throws CancelledException, InvalidInputException, List<RecoveredClass> recoveredClasses) throws CancelledException, InvalidInputException,
DuplicateNameException, CircularDependencyException { DuplicateNameException, CircularDependencyException {
Set<Function> processedFunctions = new HashSet<>();
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCancelled(); monitor.checkCancelled();
List<Function> indeterminateList = List<Function> indeterminateList =
@@ -6741,16 +6824,27 @@ public class RecoveredClassHelper {
for (Function indeterminateFunction : indeterminateList) { for (Function indeterminateFunction : indeterminateList) {
monitor.checkCancelled(); monitor.checkCancelled();
// get the addresses in the function that refer to classes either by
// referencing a vftable in a class or by calling a function in a class // functions with inlines often contain multiple inlined constructor/destructors
Map<Address, RecoveredClass> referenceToClassMap = // skip if already processed (processInline<c/d> methods update all of them)
getReferenceToClassMap(recoveredClass, indeterminateFunction); if (processedFunctions.contains(indeterminateFunction)) {
continue;
}
Map<Address, ReferencedClassObject> referenceToClassMap =
functionReferenceMap.get(indeterminateFunction);
if (referenceToClassMap == null) {
referenceToClassMap =
getReferenceToReferencedObjectsMap(recoveredClass, indeterminateFunction);
}
List<Function> allDescendantConstructors = List<Function> allDescendantConstructors =
getAllDescendantConstructors(recoveredClass); getAllDescendantConstructors(recoveredClass);
if (allDescendantConstructors.contains(indeterminateFunction)) { if (allDescendantConstructors.contains(indeterminateFunction)) {
processInlineConstructor(recoveredClass, indeterminateFunction, processInlineConstructor(recoveredClass, indeterminateFunction,
referenceToClassMap); referenceToClassMap);
processedFunctions.add(indeterminateFunction);
continue; continue;
} }
@@ -6759,6 +6853,7 @@ public class RecoveredClassHelper {
if (allDescendantDestructors.contains(indeterminateFunction)) { if (allDescendantDestructors.contains(indeterminateFunction)) {
processInlineDestructor(recoveredClass, indeterminateFunction, processInlineDestructor(recoveredClass, indeterminateFunction,
referenceToClassMap); referenceToClassMap);
processedFunctions.add(indeterminateFunction);
continue; continue;
} }
@@ -6787,12 +6882,14 @@ public class RecoveredClassHelper {
if (ancestorConstructor != null) { if (ancestorConstructor != null) {
processInlineConstructor(recoveredClass, indeterminateFunction, processInlineConstructor(recoveredClass, indeterminateFunction,
referenceToClassMap); referenceToClassMap);
processedFunctions.add(indeterminateFunction);
continue; continue;
} }
if (ancestorDestructor != null) { if (ancestorDestructor != null) {
processInlineDestructor(recoveredClass, indeterminateFunction, processInlineDestructor(recoveredClass, indeterminateFunction,
referenceToClassMap); referenceToClassMap);
processedFunctions.add(indeterminateFunction);
continue; continue;
} }
@@ -6919,7 +7016,8 @@ public class RecoveredClassHelper {
// process deleting destructors if type 1, 2 or 3 // process deleting destructors if type 1, 2 or 3
boolean isDeletingDestructor = boolean isDeletingDestructor =
processDeletingDestructor(recoveredClass, vfunction, allVftables); processDeletingDestructor(recoveredClass, vfunction,
allFunctionsThatRefVftables);
if (isDeletingDestructor) { if (isDeletingDestructor) {
processPossibleDestructors(allPossibleCDs, possibleCalledDestructors, vfunction, processPossibleDestructors(allPossibleCDs, possibleCalledDestructors, vfunction,
allVftables); allVftables);
@@ -8748,4 +8846,103 @@ public class RecoveredClassHelper {
return classStructureDataType; return classStructureDataType;
} }
public class ReferencedClassObject {
Address referenceLocation;
RecoveredClass containingClass;
Object referencedObject;
Function referencedFunction;
Address referencedAddress;
Boolean isConstructor;
Boolean isDestructor;
public ReferencedClassObject(Address referenceLocation, Object referencedObject,
RecoveredClass containingClass) {
this.referenceLocation = referenceLocation;
this.referencedObject = referencedObject;
this.containingClass = containingClass;
if (referencedObject instanceof Function function) {
referencedFunction = function;
referencedAddress = function.getEntryPoint();
}
else if (referencedObject instanceof Address address) {
referencedAddress = address;
}
}
/**
* Method to return the address of the reference location
* @return the address of the reference location
*/
public Address getReferenceLocation() {
return referenceLocation;
}
/**
* Method to return the referenced address
* @return the referenced address or null if the referenced object is not an address
*/
public Address getReferencedAddress() {
return referencedAddress;
}
/**
* Method to return the referenced function
* @return the referenced function or null if the referenced object is not a function
*/
public Function getReferencedFunction() {
return referencedFunction;
}
/**
* Method to return the RecoveredClass that corresponds to the namespace that contains the
* given referenced object
* @return the RecoveredClass object for the namespace that contains the referenced object
*/
public RecoveredClass getContainingClass() {
return containingClass;
}
/**
* Method to set whether the referenced object is a constructor or not
* @param choice should be set to true if it is a constructor and false if not
*/
public void setIsConstructor(Boolean choice) {
isConstructor = choice;
// Note that the opposite is not necessarily true
if (isConstructor) {
isDestructor = false;
}
}
/**
* Method to return whether the referenced object is a constructor
* @return true if a constructor, false if not a constructor, null if unknown
*/
public Boolean isConstructor() {
return isConstructor;
}
/**
* Method to set whether the referenced object is a destructor or not
* @param choice should be set to true if it is a destructor and false if not
*/
public void setIsDestructor(Boolean choice) {
isDestructor = choice;
// Note that the opposite is not necessarily true
if (isDestructor) {
isConstructor = false;
}
}
/**
* Method to return whether the referenced object is a destructor
* @return true if a destructor, false if not a destructor, null if unknown
*/
public Boolean isDestructor() {
return isDestructor;
}
}
} }