From 22d4c2069b07f85283e8ed4feec6ab5e2bc09839 Mon Sep 17 00:00:00 2001 From: ghidra007 Date: Thu, 14 Apr 2022 19:28:49 +0000 Subject: [PATCH] GP-1581 Recover classes script deleting destructor and clone discovery improvements. --- .../classrecovery/ExtendedFlatProgramAPI.java | 87 +- .../classrecovery/RTTIClassRecoverer.java | 1 - .../classrecovery/RTTIGccClassRecoverer.java | 14 - .../RTTIWindowsClassRecoverer.java | 43 +- .../classrecovery/RecoveredClassHelper.java | 1552 ++++++++++------- 5 files changed, 969 insertions(+), 728 deletions(-) diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java index 06bd7c50b5..c4adf5df18 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java @@ -36,7 +36,7 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { final int defaultPointerSize; - ExtendedFlatProgramAPI(Program program, TaskMonitor taskMonitor) { + public ExtendedFlatProgramAPI(Program program, TaskMonitor taskMonitor) { super(program, taskMonitor); defaultPointerSize = program.getDefaultPointerSize(); @@ -668,30 +668,31 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { * Get the nth called function from calling function * @param callingFunction The calling function * @param callIndex the called function index (ie 1 = first called function) + * @param getThunkedFunction if true get the thunked function, if false get the thunk itself, if + * there is a thunk * @return the nth called function in calling function * @throws CancelledException if cancelled */ - public Function getCalledFunctionByCallOrder(Function callingFunction, int callIndex) - throws CancelledException { + public Function getCalledFunctionByCallOrder(Function callingFunction, int callIndex, + boolean getThunkedFunction) throws CancelledException { - List orderedReferenceAddressPairsFromCallingFunction = - getOrderedReferenceAddressPairsFromCallingFunction(callingFunction); - if (callIndex > orderedReferenceAddressPairsFromCallingFunction.size()) { - return null; + int callNumber = 0; + InstructionIterator instructions = callingFunction.getProgram() + .getListing() + .getInstructions(callingFunction.getBody(), true); + while (instructions.hasNext() && callNumber < callIndex) { + monitor.checkCanceled(); + Instruction instruction = instructions.next(); + if (instruction.getFlowType().isCall()) { + callNumber++; + if (callNumber == callIndex) { + Function referencedFunction = + getReferencedFunction(instruction.getMinAddress(), getThunkedFunction); + return referencedFunction; + } + } } - - ReferenceAddressPair referenceAddressPair = - orderedReferenceAddressPairsFromCallingFunction.get(callIndex - 1); - Address calledFunctionAddress = referenceAddressPair.getDestination(); - Function calledFunction = getFunctionAt(calledFunctionAddress); - if (calledFunction == null) { - return null; - } - if (calledFunction.isThunk()) { - calledFunction = calledFunction.getThunkedFunction(true); - } - return calledFunction; - + return null; } /** @@ -885,11 +886,8 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { while (referenceIterator.hasNext()) { monitor.checkCanceled(); Address referenceAddress = referenceIterator.next(); - Address referencedAddress = getSingleReferencedAddress(referenceAddress); - if (referencedAddress == null) { - continue; - } - Function function = getFunctionAt(referencedAddress); + + Function function = getReferencedFunction(referenceAddress, true); // skip the ones that reference a vftable if (function != null) { @@ -905,30 +903,43 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { * @param address the given address * @param getThunkedFunction if true and referenced function is a thunk, get the thunked function * @return the referenced function or null if no function is referenced + * @throws CancelledException if cancelled */ - public Function getReferencedFunction(Address address, boolean getThunkedFunction) { + public Function getReferencedFunction(Address address, boolean getThunkedFunction) + throws CancelledException { - Address referencedAddress = getSingleReferencedAddress(address); + Reference[] referencesFrom = getReferencesFrom(address); - if (referencedAddress == null) { + if (referencesFrom.length == 0) { return null; } - Function function = getFunctionAt(referencedAddress); + for (Reference referenceFrom : referencesFrom) { - if (function == null) { - return null; - } + monitor.checkCanceled(); - if (!getThunkedFunction) { + Address referencedAddress = referenceFrom.getToAddress(); + if (referencedAddress == null) { + continue; + } + + Function function = getFunctionAt(referencedAddress); + + if (function == null) { + continue; + } + + if (!getThunkedFunction) { + return function; + } + + if (function.isThunk()) { + function = function.getThunkedFunction(true); + } return function; } - if (function.isThunk()) { - function = function.getThunkedFunction(true); - } - - return function; + return null; } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java index 62c29064c1..5e5f5860f8 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java @@ -258,7 +258,6 @@ public class RTTIClassRecoverer extends RecoveredClassHelper { memberFunctionsToProcess.addAll(recoveredClass.getConstructorList()); memberFunctionsToProcess.addAll(recoveredClass.getDestructorList()); memberFunctionsToProcess.addAll(recoveredClass.getIndeterminateList()); - memberFunctionsToProcess.addAll(recoveredClass.getInlinedConstructorList()); Iterator memberFunctionIterator = memberFunctionsToProcess.iterator(); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index 24e73ab9fb..bf591f09b6 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -1077,20 +1077,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { return true; } - private Data createVftableArray(Address vftableAddress, int numFunctionPointers) - throws Exception { - - api.clearListing(vftableAddress, - vftableAddress.add((numFunctionPointers * defaultPointerSize - 1))); - - DataType pointerDataType = dataTypeManager.getPointer(null); - ArrayDataType vftableArrayDataType = - new ArrayDataType(pointerDataType, numFunctionPointers, defaultPointerSize); - - Data vftableArrayData = api.createData(vftableAddress, vftableArrayDataType); - return vftableArrayData; - } - /** * Method to check for a valid vtable at the given address * @param vtableAddress the given address diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index 37621d740f..7c61590146 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -132,6 +132,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { return recoveredClasses; } + //TODO: decide whether to update to only include possible cds not all functions that + // have ref to vftable + // if decide to be more restrictive then need to move the method that weeds out inlines and + // from the class cd lists createCalledFunctionMap(recoveredClasses); // figure out class hierarchies using either RTTI or vftable refs @@ -154,9 +158,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // otherwise figure everything out from scratch else { monitor.setMessage("Figuring out class method types"); - // println( -// "Figuring out class method types (constructor, destructor, inline constructor, " + -// "inline destructor, deleting destructor, clone) ..."); processConstructorAndDestructors(recoveredClasses); } @@ -171,7 +172,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // 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 // to "this" param - //println("Creating class data types and applying class structures..."); monitor.setMessage("Creating class data types and applying class structures"); figureOutClassDataMembers(recoveredClasses); @@ -184,7 +184,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { if (!isPDBLoaded) { // create better vftable labels for multi vftable classes updateMultiVftableLabels(recoveredClasses); - //println("Removing erroneous FID namespaces and corresponding class data types"); removeEmptyClassesAndStructures(); } @@ -463,10 +462,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { num1 + "," + num2 + "," + num3 + "," + num4 + ")", classNamespace, SourceType.ANALYSIS); } -// else { -// println( -// "Failed to create a baseClassDescArray structure at " + address.toString()); -// } } } } @@ -553,8 +548,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { classHierarchyStructure = createClassHierarchyStructure(classHierarchyDescriptorAddress); if (classHierarchyStructure == null) { -// println("Failed to create a classHierarchyDescriptor structure at " + -// classHierarchyDescriptorAddress.toString()); symbolTable.removeSymbolSpecial(classHierarchySymbol); return null; } @@ -729,12 +722,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { for (Reference refTo : referencesTo) { Address vftableMetaPointer = refTo.getFromAddress(); if (vftableMetaPointer == null) { - //println("can't retrieve meta address"); continue; } Address vftableAddress = vftableMetaPointer.add(defaultPointerSize); if (vftableAddress == null) { - //println("can't retrieve vftable address"); continue; } @@ -905,8 +896,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { } } - List vftableSymbolsInNamespace = - getVftablesInNamespace(vftableSymbols, classNamespace); + List vftableSymbolsInNamespace = getClassVftableSymbols(classNamespace); //if there are no vftables in this class then create a new class object and make it // non-vftable class @@ -1204,7 +1194,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { } else if (symbols.size() > 1) { //TODO: throw exception? - //println(recoveredClass.getName() + " has more than one base class array"); } return classHierarchy; } @@ -1292,7 +1281,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { private void processConstructorAndDestructors(List recoveredClasses) throws CancelledException, InvalidInputException, DuplicateNameException, Exception { - // find deleting destructors using various mechanisms + // update the global lists and class lists to narrow the cd lists + trimConstructorDestructorLists(recoveredClasses); + + // find deleting destructors findDeletingDestructors(recoveredClasses); // use atexit param list to find more destructors @@ -1316,9 +1308,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { // ones that could not be determined in earlier stages processRemainingIndeterminateConstructorsAndDestructors(recoveredClasses); - // use the known constructors and known vfunctions to figure out - // clone functions - findCloneFunctions(recoveredClasses); + // use the known constructors and known vfunctions to figure out basic clone functions + findBasicCloneFunctions(recoveredClasses); // 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 @@ -1338,6 +1329,8 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { } + + /** * Method to recover parent information, including class offsets, vbase structure and its offset and address if applicable, and whether * the parent is regularly or virtually inherited @@ -1845,11 +1838,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { getParentOrderMap(recoveredClass, ancestorsAllowedToMap); if (sortedOrder.size() != parentOrderMap.size()) { -// if (DEBUG) { -// println(recoveredClass.getName() + -// " has mismatch between vftable and parent order map sizes " + -// sortedOrder.size() + " vs " + parentOrderMap.size()); -// } + Msg.debug(this, + recoveredClass.getName() + + " has mismatch between vftable and parent order map sizes " + + sortedOrder.size() + " vs " + parentOrderMap.size()); return; } @@ -1955,6 +1947,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { Function referencedFunction = extendedFlatAPI.getReferencedFunction(classReferenceAddress, true); + if (referencedFunction == null) { continue; } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index 5d4aade630..db5a99f47b 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -73,8 +73,6 @@ public class RecoveredClassHelper { private static final int NONE = -1; - private static int MIN_OPERATOR_NEW_REFS = 10; - private static final boolean DEBUG = false; private Map vftableToClassMap = new HashMap(); @@ -106,7 +104,9 @@ public class RecoveredClassHelper { Map> functionToLoadPcodeOps = new HashMap>(); - List allConstructorsAndDestructors = new ArrayList(); + List allClassFunctionsContainingVftableReference = new ArrayList(); + Set allPossibleConstructorsAndDestructors = new HashSet(); + Set allVfunctions = new HashSet(); List allConstructors = new ArrayList(); List allDestructors = new ArrayList(); List allInlinedConstructors = new ArrayList(); @@ -120,15 +120,12 @@ public class RecoveredClassHelper { List resolvedFIDFunctions = new ArrayList(); List fixedFIDFunctions = new ArrayList(); - private static Function operator_delete = null; - private static Function operator_new = null; + List operatorNewFunctions = new ArrayList(); private static Function purecall = null; private static Function atexit = null; List atexitCalledFunctions = new ArrayList(); - List deletingDestructorsThatCallDestructor = new ArrayList(); - GlobalNamespace globalNamespace; DataTypeManager dataTypeManager; int defaultPointerSize; @@ -416,7 +413,7 @@ public class RecoveredClassHelper { // if thunk, need to use the thunked function to see if it is on list of cds // but always need to use the actual called function to get reference address // need to used the thunked function on the hashmap - if (allConstructorsAndDestructors.contains(calledFunction)) { + if (allClassFunctionsContainingVftableReference.contains(calledFunction)) { // get list of refs to this function from the calling function List
referencesToFunctionBFromFunctionA = extendedFlatAPI .getReferencesToFunctionBFromFunctionA(function, referencedFunction); @@ -435,6 +432,43 @@ public class RecoveredClassHelper { } + /** + * Method to return a map of address to function calls for the given function + * @param function given function + * @return map of address to function calls for the given function or null if no called functions + * @throws CancelledException when script is canceled + */ + public Map getAddressToFunctionCallMap(Function function) + throws CancelledException { + + Map addressToFunctionCallMap = new HashMap(); + + Set calledFunctions = function.getCalledFunctions(monitor); + + if (calledFunctions.isEmpty()) { + return null; + } + + for (Function calledFunction : calledFunctions) { + monitor.checkCanceled(); + + Function referencedFunction = calledFunction; + + // get list of refs to this function from the calling function + List
referencesToFunctionBFromFunctionA = + extendedFlatAPI.getReferencesToFunctionBFromFunctionA(function, referencedFunction); + + // add them to list of ref address pairs + for (Address sourceRefAddr : referencesToFunctionBFromFunctionA) { + monitor.checkCanceled(); + addressToFunctionCallMap.put(sourceRefAddr, calledFunction); + } + } + return addressToFunctionCallMap; + + } + + public List getCalledConstDestRefAddrPairs(Function function) { return functionToCalledConsDestRefAddrPairMap.get(function); } @@ -469,12 +503,28 @@ public class RecoveredClassHelper { return functionToLoadPcodeOps.get(function); } - public void updateAllConstructorsAndDestructorsList(List functions) { - allConstructorsAndDestructors.addAll(functions); + public void updateAllVfunctionsSet(List vfunctions) { + allVfunctions.addAll(vfunctions); } - public List getAllConstructorsAndDestructors() { - return allConstructorsAndDestructors; + public Set getAllVfunctions() { + return allVfunctions; + } + + public void updateAllClassFunctionsWithVftableRefList(List functions) { + allClassFunctionsContainingVftableReference.addAll(functions); + } + + public List getAllClassFunctionsWithVtableRef() { + return allClassFunctionsContainingVftableReference; + } + + public void updateAllPossibleConstructorDestructorList(Function function) { + allPossibleConstructorsAndDestructors.add(function); + } + + public Set getAllPossibleConstructorsAndDestructors() { + return allPossibleConstructorsAndDestructors; } public void addToAllConstructors(Function function) { @@ -919,68 +969,6 @@ public class RecoveredClassHelper { return referencesToVftable; } - /** - * Method to retrieve a list of vftable symbols, from the given list of vftables, in the given - * namespace - * @param vftableSymbols the list of all vftable symbols - * @param namespace the given namespace - * @return a list of vftable symbols in the given namespace - * @throws CancelledException if cancelled - */ - //TODO: update to use the one below this - public List getVftablesInNamespace(List vftableSymbols, Namespace namespace) - throws CancelledException { - - List vftableSymbolsInNamespace = new ArrayList(); - - Iterator symbolsInNamespace = program.getSymbolTable().getSymbols(namespace); - while (symbolsInNamespace.hasNext()) { - monitor.checkCanceled(); - Symbol symbol = symbolsInNamespace.next(); - if (vftableSymbols.contains(symbol) && - !isSymbolAddressOnList(vftableSymbolsInNamespace, symbol.getAddress())) { - vftableSymbolsInNamespace.add(symbol); - } - } - return vftableSymbolsInNamespace; - } - - /** - * Method to retrieve a list of vftable symbols in the given namespace - * @param namespace the given namespace - * @return a list of vftable symbols in the given namespace - * @throws CancelledException if cancelled - */ - public List getVftablesInNamespace(Namespace namespace) throws CancelledException { - - List vftableSymbolsInNamespace = new ArrayList(); - - Iterator symbolsInNamespace = program.getSymbolTable().getSymbols(namespace); - while (symbolsInNamespace.hasNext()) { - monitor.checkCanceled(); - Symbol symbol = symbolsInNamespace.next(); - if (symbol.getName().contains("vftable")) { - vftableSymbolsInNamespace.add(symbol); - } - } - return vftableSymbolsInNamespace; - } - - private boolean isSymbolAddressOnList(List symbols, Address address) - throws CancelledException { - - Iterator symbolIterator = symbols.iterator(); - while (symbolIterator.hasNext()) { - monitor.checkCanceled(); - Symbol symbol = symbolIterator.next(); - if (symbol.getAddress().equals(address)) { - return true; - } - - } - return false; - } - /** * Method to determine if the given function is an inlined destructor or indeterminate in any class * @param function the given function @@ -1199,7 +1187,6 @@ public class RecoveredClassHelper { * @return the list of variables in the given function that store the vftable address * @throws CancelledException if cancelled */ - //TODO: update to make sure it is getting any global memory variables //TODO: Possibly refactor to use the same methodology as getAssignedAddressFrompcode or getStoredVftableAddress private List getVariableThatStoresVftablePointer(HighFunction highFunction, Address vftableReference) throws CancelledException { @@ -1366,6 +1353,11 @@ public class RecoveredClassHelper { public Function getCalledFunctionFromCallPcodeOp(PcodeOp calledPcodeOp) { Function calledFunction = api.getFunctionAt(calledPcodeOp.getInput(0).getAddress()); + + if (calledFunction == null) { + return null; + } + return calledFunction; } @@ -2522,7 +2514,8 @@ public class RecoveredClassHelper { Address destructorReference = destructorIterator.next(); RecoveredClass recoveredClass = referenceToClassMap.get(destructorReference); - Function destructor = extendedFlatAPI.getReferencedFunction(destructorReference, true); + Function destructor = + extendedFlatAPI.getReferencedFunction(destructorReference, true); if (recoveredClass.getIndeterminateList().contains(destructor)) { addDestructorToClass(recoveredClass, destructor); @@ -2756,8 +2749,11 @@ public class RecoveredClassHelper { recoveredClass.addIndeterminateConstructorOrDestructorList( possibleConstructorDestructorsForThisClass); - // Add them to the list of all constructors and destructors in program - updateAllConstructorsAndDestructorsList(possibleConstructorDestructorsForThisClass); + // Add them to the list of all possible constructors and destructors in program + updateAllClassFunctionsWithVftableRefList(possibleConstructorDestructorsForThisClass); + + // add virtualFunctions to the global list of them + updateAllVfunctionsSet(virtualFunctions); } // end of looping over vfTables return recoveredClasses; @@ -2953,7 +2949,7 @@ public class RecoveredClassHelper { } - private Data createVftableArray(Address vftableAddress, int numFunctionPointers) + public Data createVftableArray(Address vftableAddress, int numFunctionPointers) throws CancelledException, AddressOutOfBoundsException { api.clearListing(vftableAddress, @@ -2980,7 +2976,7 @@ public class RecoveredClassHelper { * @return the created array of pointers Data or null * @throws CancelledException if cancelled */ - private int createVftable(Address vftableAddress, boolean allowNullFunctionPtrs, + public int createVftable(Address vftableAddress, boolean allowNullFunctionPtrs, boolean allowDefaultRefsInMiddle) throws CancelledException { int numFunctionPointers = 0; @@ -4147,73 +4143,62 @@ public class RecoveredClassHelper { } /** - * Method to determine if the given constructor function calls any non-parent constructors + * Method to determine if the given constructor function calls any non-constructors * before the vftable refererence * @param recoveredClass the given class * @param constructor the given constructor function - * @param vftableReference the address of the reference to the class vftable - * @return true if the given constructor function calls any non-parent constructors before the + * @param vftableReference the address of the reference to the class vftable + * @return true if the given constructor function calls any non-constructors before the * vftable refererence, false otherwise * @throws CancelledException if cancelled */ - public boolean doesFunctionCallAnyNonParentConstructorsBeforeVtableReference( + public boolean doesFunctionCallAnyNonConstructorsBeforeVtableReference( RecoveredClass recoveredClass, Function constructor, Address vftableReference) throws CancelledException { - List orderedReferenceAddressPairsFromCallingFunction = - extendedFlatAPI.getOrderedReferenceAddressPairsFromCallingFunction(constructor); + InstructionIterator instructions = + constructor.getProgram().getListing().getInstructions(constructor.getBody(), true); - // if there are no calls from the function then return false - if (orderedReferenceAddressPairsFromCallingFunction.size() == 0) { - return false; - } - - Iterator iterator = - orderedReferenceAddressPairsFromCallingFunction.iterator(); - while (iterator.hasNext()) { + while (instructions.hasNext()) { monitor.checkCanceled(); - ReferenceAddressPair refPair = iterator.next(); - int callRefCompareToVftableRef = refPair.getSource().compareTo(vftableReference); - // if call is after the vtable reference then return false - if (callRefCompareToVftableRef > 0) { + Instruction instruction = instructions.next(); + Address instructionAddress = instruction.getAddress(); + if (instructionAddress.compareTo(vftableReference) >= 0) { return false; } + if (instruction.getFlowType().isCall()) { - // if call is before vtable and is not an inherited constructor and not the operator_new - // then return true - Address calledAddress = refPair.getDestination(); - Function calledFunction = api.getFunctionAt(calledAddress); - if (calledFunction.isThunk()) { - calledFunction = calledFunction.getThunkedFunction(true); - } + Function calledFunction = + extendedFlatAPI.getReferencedFunction(instruction.getMinAddress(), true); - if (calledFunction.equals(operator_new)) { - continue; - } + if (calledFunction == null) { + return true; + } - if (calledFunction.getName().contains("prolog")) { - continue; - } + if (calledFunction.getName().contains("prolog")) { + continue; + } - if (!getAllConstructors().contains(calledFunction)) { - return true; + if (!getAllConstructors().contains(calledFunction)) { + return true; + } } } + return false; } /** - * Method to find class clone functions - * @param recoveredClasses List of RecoveredClass objects - * @throws CancelledException when script is cancelled - * @throws Exception if issues making label + * Method to find basic clone functions from given classes virtual functions + * @param recoveredClasses list of classes to process + * @throws CancelledException if cancelled */ - public void findCloneFunctions(List recoveredClasses) - throws CancelledException, Exception { + public void findBasicCloneFunctions(List recoveredClasses) + throws CancelledException { - Map cloneToClassMap = new HashMap(); + operatorNewFunctions = getOperatorNewFunctions(recoveredClasses); Iterator recoveredClassIterator = recoveredClasses.iterator(); while (recoveredClassIterator.hasNext()) { @@ -4221,186 +4206,71 @@ public class RecoveredClassHelper { RecoveredClass recoveredClass = recoveredClassIterator.next(); - List allOtherConstructors = new ArrayList(getAllConstructors()); - allOtherConstructors.removeAll(recoveredClass.getConstructorList()); + List constructorList = recoveredClass.getConstructorList(); + List allVirtualFunctions = recoveredClass.getAllVirtualFunctions(); - // iterate through the vtable functions - List virtualFunctions = recoveredClass.getAllVirtualFunctions(); - if (virtualFunctions == null) { + if (allVirtualFunctions.isEmpty()) { continue; } - Iterator vfunctionIterator = virtualFunctions.iterator(); - while (vfunctionIterator.hasNext()) { + for (Function vfunction : allVirtualFunctions) { + monitor.checkCanceled(); - Function vfunction = vfunctionIterator.next(); - if (extendedFlatAPI.doesFunctionACallAnyListedFunction(vfunction, - recoveredClass.getConstructorList()) && - !extendedFlatAPI.doesFunctionACallAnyListedFunction(vfunction, - allOtherConstructors)) { - cloneToClassMap.put(vfunction, recoveredClass); + if (!hasNCalls(vfunction, 2)) { + continue; } - } + Function firstCalledFunction = + extendedFlatAPI.getCalledFunctionByCallOrder(vfunction, 1, false); + + // skip computed calls (ie call eax) + if (firstCalledFunction == null) { + continue; + } + + if (!operatorNewFunctions.contains(firstCalledFunction)) { + continue; + } + + // not a clone if the second called function isn't a class constructor + Function secondFunction = + extendedFlatAPI.getCalledFunctionByCallOrder(vfunction, 2, true); + + if (secondFunction == null) { + continue; + } + + if (!constructorList.contains(secondFunction)) { + continue; + } + + recoveredClass.addCloneFunction(vfunction); + + } } - // use the clone functions with only two calls (one to constructor and one to operator new) - Function operatorNew = identifyOperatorNewFunction(cloneToClassMap); + } - // use the operator new to accept only the good clones - // if more than one operator new remove - if (operatorNew != null) { - Set cloneFunctions = cloneToClassMap.keySet(); - Iterator cloneIterator = cloneFunctions.iterator(); - while (cloneIterator.hasNext()) { - monitor.checkCanceled(); - Function cloneFunction = cloneIterator.next(); - if (isBasicCloneFunction(cloneFunction, operator_new, cloneToClassMap)) { - RecoveredClass recoveredClass = cloneToClassMap.get(cloneFunction); - recoveredClass.addCloneFunction(cloneFunction); + private boolean hasNCalls(Function function, int n) throws CancelledException { + + InstructionIterator instructions = + function.getProgram().getListing().getInstructions(function.getBody(), true); + + int numCalls = 0; + while (instructions.hasNext()) { + monitor.checkCanceled(); + Instruction instruction = instructions.next(); + if (instruction.getFlowType().isCall()) { + numCalls++; + if (numCalls > n) { + return false; } } } - - } - - /** - * Method to identify basic clone functions - * @param caller possible clone function - * @param firstCalled first function called by caller - * @param cloneFunctionToClassMap map of possible clone functions to their parent class - * @return true if caller function is a basic clone, else false - * @throws CancelledException if cancelled - */ - private boolean isBasicCloneFunction(Function caller, Function firstCalled, - Map cloneFunctionToClassMap) throws CancelledException { - - Set calledFunctions = caller.getCalledFunctions(monitor); - if (calledFunctions.size() != 2 && calledFunctions.size() != 3) { - return false; + if (numCalls == n) { + return true; } - if (!extendedFlatAPI.getCalledFunctionByCallOrder(caller, 1).equals(firstCalled)) { - return false; - } - RecoveredClass recoveredClass = cloneFunctionToClassMap.get(caller); - List constructorList = recoveredClass.getConstructorList(); - - Function secondFunction = extendedFlatAPI.getCalledFunctionByCallOrder(caller, 2); - if (secondFunction.isThunk()) { - secondFunction = secondFunction.getThunkedFunction(true); - } - - if (!constructorList.contains(secondFunction)) { - return false; - } - - return true; - } - - /** - * Method to identify the operator_new function using found clone functions - * @param cloneToClassMap Map of clone functions and their classes - * @return the operator_new function or null if not identified - * @throws CancelledException if cancelled - * @throws Exception if issue making label - */ - private Function identifyOperatorNewFunction(Map cloneToClassMap) - throws CancelledException, Exception { - - Map functionOccuranceMap = new HashMap(); - - Set cloneFunctions = cloneToClassMap.keySet(); - Iterator cloneIterator = cloneFunctions.iterator(); - while (cloneIterator.hasNext()) { - monitor.checkCanceled(); - Function cloneFunction = cloneIterator.next(); - - // Easiest to find using those with only two calls so skip the bigger ones - Set calledFunctions = cloneFunction.getCalledFunctions(monitor); - if (calledFunctions.size() != 2) { - continue; - } - // get first called function which should be the operator_new function - // The second call is a class constructor and we know it is called - // from the cloneFunction or it wouldn't be a cloneFunction - Function firstCalledFunction = - extendedFlatAPI.getCalledFunctionByCallOrder(cloneFunction, 1); - if (firstCalledFunction == null) { - continue; - } - // skip any constructor or destructors that are called first - if (getAllConstructorsAndDestructors().contains(firstCalledFunction)) { - continue; - } - - if (!functionOccuranceMap.keySet().contains(firstCalledFunction)) { - functionOccuranceMap.put(firstCalledFunction, 1); - } - else { - Integer numOccurances = functionOccuranceMap.get(firstCalledFunction); - functionOccuranceMap.replace(firstCalledFunction, numOccurances + 1); - } - - } - - Function probableOperatorNewFunction = getMostFrequentFunction(functionOccuranceMap); - - if (probableOperatorNewFunction == null) { - return null; - } - - Integer numOccurances = functionOccuranceMap.get(probableOperatorNewFunction); - if (functionOccuranceMap.get(probableOperatorNewFunction) < MIN_OPERATOR_NEW_REFS) { - Msg.debug(this, probableOperatorNewFunction.toString() + - " is a possible operator_new function but has less than the defined minimum number " + - "of matching calls " + numOccurances); - - return null; - } - - // If we get this far then we are sure the operator_new function - // is correct so assign the global variable to it - operator_new = probableOperatorNewFunction; - //If its symbol is not already named then name it - if (probableOperatorNewFunction.getSymbol().getSource() == SourceType.DEFAULT) { - Msg.debug(this, - "Found unlabeled operator new that matched in all found clone functions: " + - probableOperatorNewFunction.getEntryPoint().toString() + - ". Creating label there."); - api.createLabel(probableOperatorNewFunction.getEntryPoint(), "operator_new", true); - } - - return operator_new; - - } - - /** - * Method to get the function in the map with the highest mapped Integer value - * @param map the map containing function, count mappings - * @return the function with the highest count mapped to it - * @throws CancelledException if cancelled - */ - private Function getMostFrequentFunction(Map map) throws CancelledException { - - Integer highest = null; - Function mostFrequentFunction = null; - Set keySet = map.keySet(); - Iterator iterator = keySet.iterator(); - while (iterator.hasNext()) { - monitor.checkCanceled(); - Function function = iterator.next(); - if (mostFrequentFunction == null) { - mostFrequentFunction = function; - highest = map.get(function); - continue; - } - Integer frequency = map.get(function); - if (frequency > highest) { - highest = frequency; - mostFrequentFunction = function; - } - } - return mostFrequentFunction; + return false; } /** @@ -4466,9 +4336,6 @@ public class RecoveredClassHelper { Structure badStructure = badStructureIterator.next(); ListAccumulator accumulator = new ListAccumulator<>(); - // TODO Discovering types can be very slow for large binaries. Discovering types - // should not be needed. Passing false here will still find all applied uses of the - // given structure. boolean discoverTypes = true; ReferenceUtils.findDataTypeReferences(accumulator, badStructure, program, discoverTypes, monitor); @@ -5255,6 +5122,22 @@ public class RecoveredClassHelper { bookmarkMgr.setBookmark(address, BookmarkType.ANALYSIS, BOOKMARK_CATEGORY, bookmarkComment); } + public void bookmarkAddress(Address address, String comment, String category) { + + BookmarkManager bookmarkMgr = program.getBookmarkManager(); + + Bookmark bookmark = bookmarkMgr.getBookmark(address, BookmarkType.ANALYSIS, category); + String bookmarkComment; + if (bookmark != null && !bookmark.getComment().equals(comment) && + !containsString(bookmark.getComment(), comment)) { + bookmarkComment = bookmark.getComment() + " + " + comment; + } + else { + bookmarkComment = comment; + } + bookmarkMgr.setBookmark(address, BookmarkType.ANALYSIS, category, bookmarkComment); + } + /** * Method to determine if the given comment string that has pieces separated by +'s has any * piece exactly equal to the given string. @@ -5290,198 +5173,209 @@ public class RecoveredClassHelper { } /** - * Method to find the operator_delete function using the known deleting destructor functions - * @param recoveredClasses List of RecoveredClass objects - * @return operator_delete function or null if one cannot be determined - * @throws CancelledException when cancelled - * @throws Exception when issues creating labels + * Method to find all class functions that are on both the vfunction list and the possible + * constructor/destructor list for the same class + * @param recoveredClasses the list of all known classes + * @return all functions on both their class constructor/destructor list and virtual function + * list + * @throws CancelledException if cancelled */ - private Function findOperatorDeleteUsingKnownDeletingDestructors( - List recoveredClasses) throws CancelledException, Exception { + private Set getTwoCallCommonFunctions(List recoveredClasses) + throws CancelledException { - Function possibleOperatorDelete = null; - Iterator recoveredClassIterator = recoveredClasses.iterator(); - while (recoveredClassIterator.hasNext()) { + Set twoCallCommonFunctions = new HashSet(); + + for (RecoveredClass recoveredClass : recoveredClasses) { monitor.checkCanceled(); - RecoveredClass recoveredClass = recoveredClassIterator.next(); - List deletingDestructors = recoveredClass.getDeletingDestructors(); - Iterator deletingDestructorIterator = deletingDestructors.iterator(); - while (deletingDestructorIterator.hasNext()) { + List virtualFunctions = recoveredClass.getAllVirtualFunctions(); + List cdFunctions = recoveredClass.getConstructorOrDestructorFunctions(); + for (Function cdFunction : cdFunctions) { monitor.checkCanceled(); - Function deletingDestructor = deletingDestructorIterator.next(); - if (deletingDestructorsThatCallDestructor.contains(deletingDestructor)) { - Set calledFunctions = deletingDestructor.getCalledFunctions(monitor); - - // just use the ones that call two functions to find operator_delete - if (calledFunctions.size() != 2) { - return null; - } - // get first called function and verify it is on cd list - Function firstCalledFunction = - extendedFlatAPI.getCalledFunctionByCallOrder(deletingDestructor, 1); - if (firstCalledFunction == null || - !recoveredClass.getConstructorOrDestructorFunctions() - .contains(firstCalledFunction)) { - return null; - } - - // get second one and if operator_delete has not been assigned yet, assign it - Function secondCalledFunction = - extendedFlatAPI.getCalledFunctionByCallOrder(deletingDestructor, 2); - if (secondCalledFunction == null) { - return null; - } - - // if we didn't already have one, set it here - if (possibleOperatorDelete == null) { - possibleOperatorDelete = secondCalledFunction; - } - // if we find another possibility and they don't match return null - else if (!possibleOperatorDelete.equals(secondCalledFunction)) { - return null; - } + // if it isn't on both lists continue + if (!virtualFunctions.contains(cdFunction)) { + continue; } + + // if it doesn't have exactly two calls continue + if (!hasNCalls(cdFunction, 2)) { + continue; + } + + // else add it to the set to return + twoCallCommonFunctions.add(cdFunction); + } } - // If we get this far then we are sure the operator_delete function - // is correct so assign the global variable. If its symbol is not already named then name it - if (possibleOperatorDelete != null) { - operator_delete = possibleOperatorDelete; - if (possibleOperatorDelete.getSymbol().getSource() == SourceType.DEFAULT) { - Msg.debug(this, - "Found unlabeled operator delete that matched in all found deleting destructors: " + - possibleOperatorDelete.getEntryPoint().toString() + - ". Creating label there."); - api.createLabel(operator_delete.getEntryPoint(), "operator_delete", true); - } - } - - return possibleOperatorDelete; - + return twoCallCommonFunctions; } /** - * - * @param recoveredClass the given class - * @param virtualFunction the given virtual function - * @param operatorDeleteFunction the operator delete function + * Method to find operator delete function(s) using common called functions from class virtual + * functions + * @param recoveredClasses the list of classes + * @return a Set of operator delete functions if found or an empty Set if not * @throws CancelledException if cancelled - * @throws InvalidInputException if issues setting return type - * @throws DuplicateNameException if try to create same symbol name already in namespace */ - private void processClassDeletingDestructorByOperatorDelete(RecoveredClass recoveredClass, - Function virtualFunction, Function operatorDeleteFunction) - throws CancelledException, InvalidInputException, DuplicateNameException { + private Set findOperatorDeletesUsingCalledCommonFunction( + List recoveredClasses) throws CancelledException { - // don't continue checking if it doesn't call operator_delete - if (!extendedFlatAPI.doesFunctionACallFunctionB(virtualFunction, operatorDeleteFunction)) { - return; + Set operatorDeletes = new HashSet(); + HashMap operatorDeleteCountMap = new HashMap(); + + // get all the functions that are on both the virtual function list and cd list for + // their respective class + Set twoCallCommonFunctions = getTwoCallCommonFunctions(recoveredClasses); + if (twoCallCommonFunctions.isEmpty()) { + return operatorDeletes; } - List ownConstructorOrDestructorFunctions = - new ArrayList(recoveredClass.getConstructorOrDestructorFunctions()); + // go through all the functions and update the maps if the function calls a constructor + // or destructor in either position - ownConstructorOrDestructorFunctions.removeAll(recoveredClass.getConstructorList()); - ownConstructorOrDestructorFunctions.removeAll(recoveredClass.getInlinedConstructorList()); + int numPossibleOpDeleteFunctions = 0; + int highestOpDeleteCount = 0; + Function mostCommonOpDelete = null; - Iterator functionIterator = ownConstructorOrDestructorFunctions.iterator(); - while (functionIterator.hasNext()) { + for (Function function : twoCallCommonFunctions) { monitor.checkCanceled(); - Function function = functionIterator.next(); - //Type 4 - class c/d called from other than first vfunction - if (extendedFlatAPI.doesFunctionACallFunctionB(virtualFunction, function)) { - recoveredClass.addDeletingDestructor(virtualFunction); - addDestructorToClass(recoveredClass, function); - recoveredClass.removeIndeterminateConstructorOrDestructor(function); - return; + // get first called function - get the thunked one if it is a thunk + Function firstCalledFunction = + extendedFlatAPI.getCalledFunctionByCallOrder(function, 1, true); + + // skip if computed call (ie call eax) + if (firstCalledFunction == null) { + continue; } + + // get second called function - not the thunked one + // need the actual called function for the operator test + Function secondCalledFunction = + extendedFlatAPI.getCalledFunctionByCallOrder(function, 2, false); + + if (secondCalledFunction == null) { + continue; + } + + // if the first called function is on the global list of c/ds then update the + // operator delete count map with the second called function + if (allPossibleConstructorsAndDestructors.contains(firstCalledFunction)) { + + numPossibleOpDeleteFunctions++; + + int count = operatorDeleteCountMap.compute(firstCalledFunction, + (k, v) -> v == null ? 1 : v + 1); + + if (highestOpDeleteCount < count) { + highestOpDeleteCount = count; + mostCommonOpDelete = secondCalledFunction; + } + continue; + } + } - // Type 4 inlined - inlined class c/d called from other than first function - // either just vftable ref before operator delete or vftableref followed by parent call - // before operator delete - Address vftableReference = getFirstClassVftableReference(recoveredClass, virtualFunction); + // TODO: set some percentage threshold given the number of good possibles + if (highestOpDeleteCount > numPossibleOpDeleteFunctions / 2) { - if (vftableReference == null) { - return; + Set thunkFamily = new HashSet(); + thunkFamily.add(mostCommonOpDelete); + thunkFamily.addAll(getAllThunkFunctions(mostCommonOpDelete)); + + operatorDeletes.addAll(thunkFamily); } - List possibleParentDestructors = getPossibleParentDestructors(virtualFunction); + return operatorDeletes; + } - boolean foundVftableRef = false; - Function parentDestructor = null; + private Set findOperatorNewsUsingCalledCommonFunction( + List recoveredClasses) throws CancelledException { - AddressSetView virtualFunctionBody = virtualFunction.getBody(); - CodeUnitIterator virtualFunctionCodeUnits = - program.getListing().getCodeUnits(virtualFunctionBody, true); - while (virtualFunctionCodeUnits.hasNext()) { + Set operatorNews = new HashSet(); + + HashMap operatorNewCountMap = new HashMap(); + + // get all the functions that are on both the virtual function list and cd list for + // their respective class + Set twoCallCommonFunctions = getTwoCallCommonFunctions(recoveredClasses); + if (twoCallCommonFunctions.isEmpty()) { + return operatorNews; + } + + // get count map of all first called functions + int numPossibleOpNewFunctions = 0; + int highestOpNewCount = 0; + Function mostCommonOpNew = null; + + for (Function function : twoCallCommonFunctions) { monitor.checkCanceled(); - CodeUnit codeUnit = virtualFunctionCodeUnits.next(); - Address codeUnitAddress = codeUnit.getAddress(); - if (codeUnitAddress.equals(vftableReference)) { - foundVftableRef = true; + // get first called function - not the thunked + // need the actual called function for the operator new test + Function firstCalledFunction = + extendedFlatAPI.getCalledFunctionByCallOrder(function, 1, false); + + if (firstCalledFunction == null) { continue; } - Function referencedFunction = - extendedFlatAPI.getReferencedFunction(codeUnitAddress, true); - if (referencedFunction == null) { + // get second called function - thunked function if a thunk + Function secondCalledFunction = + extendedFlatAPI.getCalledFunctionByCallOrder(function, 2, true); + + if (secondCalledFunction == null) { continue; } - if (referencedFunction.equals(operatorDeleteFunction)) { - // if find operator delete call before vftable ref then not valid deleting destructor - if (!foundVftableRef) { - return; + // if the second called function is on the global list of c/ds then update the + // operator new count map with the first called function + if (allPossibleConstructorsAndDestructors.contains(secondCalledFunction)) { + + numPossibleOpNewFunctions++; + int count = operatorNewCountMap.compute(firstCalledFunction, + (k, v) -> v == null ? 1 : v + 1); + + if (highestOpNewCount < count) { + highestOpNewCount = count; + mostCommonOpNew = firstCalledFunction; } - recoveredClass.addDeletingDestructor(virtualFunction); - if (recoveredClass.getDestructorList().contains(virtualFunction)) { - if (DEBUG) { - Msg.debug(this, "Already created vfunction as a destructor"); - } - } - recoveredClass.removeFromConstructorDestructorList(virtualFunction); - recoveredClass.removeIndeterminateConstructorOrDestructor(virtualFunction); - recoveredClass.addInlinedDestructor(virtualFunction); - - if (parentDestructor == null) { - return; - } - List parentDestructorClasses = getClasses(parentDestructor); - if (parentDestructorClasses == null) { - return; - } - if (parentDestructorClasses.size() == 1) { - if (!parentDestructorClasses.get(0) - .getDestructorList() - .contains(parentDestructor)) { - addDestructorToClass(parentDestructorClasses.get(0), parentDestructor); - parentDestructorClasses.get(0) - .removeIndeterminateConstructorOrDestructor(parentDestructor); - } - } - // if more than one parent class for this function then let either inline or multi-class - // processing handle it later - - return; } - - if (possibleParentDestructors.contains(referencedFunction)) { - // if find parent call before vftable ref then not valid deleting destructor - if (!foundVftableRef) { - return; - } - parentDestructor = referencedFunction; - continue; - } - } + // TODO: set some percentage threshold given the number of good possibles + if (highestOpNewCount > numPossibleOpNewFunctions / 2) { + + Set thunkFamily = new HashSet(); + thunkFamily.add(mostCommonOpNew); + thunkFamily.addAll(getAllThunkFunctions(mostCommonOpNew)); + operatorNews.addAll(thunkFamily); + } + + return operatorNews; + } + + private Set getAllThunkFunctions(Function function) throws CancelledException { + + FunctionManager functionManager = program.getFunctionManager(); + + Set thunkFunctions = new HashSet(); + + Address[] functionThunkAddresses = function.getFunctionThunkAddresses(true); + if (functionThunkAddresses == null) { + return thunkFunctions; + } + + for (Address address : functionThunkAddresses) { + monitor.checkCanceled(); + + Function thunkFunction = functionManager.getFunctionAt(address); + thunkFunctions.add(thunkFunction); + } + + return thunkFunctions; } /** @@ -5785,54 +5679,6 @@ public class RecoveredClassHelper { return false; } - /** - * Method to find deleting destructors that call a destructor but have no reference to a vftable - * @param recoveredClasses the list of classes - * @throws CancelledException if cancelled - */ - public void findDeletingDestructorsWithCallToDestructorWithNoVftableReference( - List recoveredClasses) throws CancelledException { - - Iterator recoveredClassIterator = recoveredClasses.iterator(); - - while (recoveredClassIterator.hasNext()) { - monitor.checkCanceled(); - RecoveredClass recoveredClass = recoveredClassIterator.next(); - - List virtualFunctions = recoveredClass.getAllVirtualFunctions(); - - if (virtualFunctions == null) { - continue; - } - - Iterator vfIterator = virtualFunctions.iterator(); - while (vfIterator.hasNext()) { - monitor.checkCanceled(); - Function vFunction = vfIterator.next(); - - Set calledFunctions = vFunction.getCalledFunctions(monitor); - if (calledFunctions.size() != 2) { - continue; - } - - // get first called function and verify is not a c/d function in current class or - // any class get second called function and verify it is operator delete - Function firstCalledFunction = - extendedFlatAPI.getCalledFunctionByCallOrder(vFunction, 1); - Function secondCalledFunction = - extendedFlatAPI.getCalledFunctionByCallOrder(vFunction, 2); - if (firstCalledFunction != null && secondCalledFunction != null && - !recoveredClass.getConstructorOrDestructorFunctions() - .contains(firstCalledFunction) && - secondCalledFunction.equals(operator_delete) && - !getAllConstructorsAndDestructors().contains(vFunction)) { - recoveredClass.addDeletingDestructor(vFunction); - recoveredClass.setVBaseDestructor(firstCalledFunction); - } - } - } - } - /** * Method to find destructors that have no parameters or return type * @param recoveredClasses list of classes to process @@ -5860,7 +5706,7 @@ public class RecoveredClassHelper { } String returnDataName = returnDataType.getDisplayName(); - //ParameterDefinition[] params = getParametersFromDecompiler(indeterminateFunction); + ParameterDefinition[] params = decompilerUtils.getParametersFromDecompiler(indeterminateFunction); int numberParams = 0; @@ -5946,13 +5792,12 @@ public class RecoveredClassHelper { continue; } - // if the first vftable reference is not in the first code block and the - // constructor calls any non-inherited constructors before the vtable reference, - // the constructor function is really another function with the constructor + // if the first vftable reference is not in the first code block and the + // constructor calls any non-constructors before the vtable reference, + // the constructor function is really another function with the constructor // function inlined in it - if (firstVftableReferenceAddress.compareTo(firstEndOfBlock) > 0) { - if (doesFunctionCallAnyNonParentConstructorsBeforeVtableReference( + if (doesFunctionCallAnyNonConstructorsBeforeVtableReference( recoveredClass, constructor, firstVftableReferenceAddress)) { // remove from the allConstructors too @@ -6118,6 +5963,7 @@ public class RecoveredClassHelper { Address functionReference = functionReferenceIterator.next(); Function function = extendedFlatAPI.getReferencedFunction(functionReference, true); + if (function == null) { continue; } @@ -6472,104 +6318,6 @@ public class RecoveredClassHelper { } - /** - * Find deleting destructors using first vfunction on vtable - * @param recoveredClasses List of RecoveredClass objects - * @throws CancelledException if cancelled - * @throws InvalidInputException if issues setting return type - * @throws DuplicateNameException if try to create same symbol name already in namespace - */ - private void findFirstDeletingDestructors(List recoveredClasses) - throws CancelledException, InvalidInputException, DuplicateNameException { - - Iterator recoveredClassIterator = recoveredClasses.iterator(); - while (recoveredClassIterator.hasNext()) { - monitor.checkCanceled(); - - RecoveredClass recoveredClass = recoveredClassIterator.next(); - - if (!recoveredClass.hasVftable()) { - continue; - } - - List
vftableAddresses = recoveredClass.getVftableAddresses(); - Iterator
vftableIterator = vftableAddresses.iterator(); - while (vftableIterator.hasNext()) { - monitor.checkCanceled(); - Address vftableAddress = vftableIterator.next(); - - // this gets the first function pointer in the vftable - Function firstVirtualFunction = - extendedFlatAPI.getReferencedFunction(vftableAddress); - processDeletingDestructor(recoveredClass, firstVirtualFunction); - } - } - } - - /** - * Find more deleting destructors by looking at other vfunctions to see if they call - * their own cd and also call operator_delete - * @param recoveredClasses List of RecoveredClass objects - * @throws CancelledException when script cancelled - * @throws InvalidInputException if issues setting return type - * @throws DuplicateNameException if try to create same symbol name already in namespace - * @throws Exception if issues making label - */ - private void findMoreDeletingDestructors(List recoveredClasses) - throws CancelledException, InvalidInputException, DuplicateNameException, Exception { - - // first identify operator delete function - Function operatorDeleteFunction = - findOperatorDeleteUsingKnownDeletingDestructors(recoveredClasses); - if (operatorDeleteFunction == null) { - if (DEBUG) { - Msg.debug(this, - "Could not find operator delete function. Cannot process more deleting destructors."); - } - return; - } - - // then use it to find more deleting destructors of type 3 (the ones that call their own - // destructor) - Iterator recoveredClassIterator = recoveredClasses.iterator(); - while (recoveredClassIterator.hasNext()) { - monitor.checkCanceled(); - - RecoveredClass recoveredClass = recoveredClassIterator.next(); - if (!recoveredClass.hasVftable()) { - continue; - } - - List
vftableAddresses = recoveredClass.getVftableAddresses(); - Iterator
vftableAddressIterator = vftableAddresses.iterator(); - while (vftableAddressIterator.hasNext()) { - monitor.checkCanceled(); - Address vftableAddress = vftableAddressIterator.next(); - - Function firstVirtualFunction = - extendedFlatAPI.getReferencedFunction(vftableAddress); - List virtualFunctions = - recoveredClass.getVirtualFunctions(vftableAddress); - - if (virtualFunctions == null) { - continue; - } - - Iterator virtualFunctionsIterator = virtualFunctions.iterator(); - while (virtualFunctionsIterator.hasNext()) { - monitor.checkCanceled(); - Function virtualFunction = virtualFunctionsIterator.next(); - if (virtualFunction.equals(firstVirtualFunction)) { - continue; - } - processClassDeletingDestructorByOperatorDelete(recoveredClass, virtualFunction, - operatorDeleteFunction); - } - } - } - - } - /** * Method to find deleting destructors that do one of the following: * 1. reference their own vftable (ie on own c/d list) which means function @@ -6579,27 +6327,26 @@ public class RecoveredClassHelper { * 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 * @param recoveredClass the given class - * @param firstVftableFunction the first vftableFunction a class vftable * @throws CancelledException if cancelled * @throws DuplicateNameException if try to create same symbol name already in namespace * @throws InvalidInputException if issues setting return type */ - private void processDeletingDestructor(RecoveredClass recoveredClass, - Function firstVftableFunction) + private boolean processDeletingDestructor(RecoveredClass recoveredClass, Function vfunction) throws CancelledException, DuplicateNameException, InvalidInputException { - // if the first function on the vftable IS ALSO on the class constructor/destructor list - // then it is a deleting destructor with and inline destructor and we need to + // if the virtual function IS ALSO on the class constructor/destructor list + // then it is a deleting destructor with and inlined destructor and we need to // determine if the inline is the class or parent/grandparent class destructor - if (getAllConstructorsAndDestructors().contains(firstVftableFunction)) { + boolean isDeletingDestructor = false; + if (getAllClassFunctionsWithVtableRef().contains(vfunction)) { - recoveredClass.addDeletingDestructor(firstVftableFunction); - recoveredClass.removeFromConstructorDestructorList(firstVftableFunction); - recoveredClass.removeIndeterminateConstructorOrDestructor(firstVftableFunction); + recoveredClass.addDeletingDestructor(vfunction); + recoveredClass.removeFromConstructorDestructorList(vfunction); + recoveredClass.removeIndeterminateConstructorOrDestructor(vfunction); - List
vftableReferences = getVftableReferences(firstVftableFunction); + List
vftableReferences = getVftableReferences(vfunction); if (vftableReferences == null) { - return; + return false; } Iterator
vftableReferencesIterator = vftableReferences.iterator(); while (vftableReferencesIterator.hasNext()) { @@ -6611,59 +6358,46 @@ public class RecoveredClassHelper { } // Type 1 if (recoveredClass.getVftableAddresses().contains(vftableAddress)) { - recoveredClass.addInlinedDestructor(firstVftableFunction); + recoveredClass.addInlinedDestructor(vfunction); + + isDeletingDestructor = true; } // Type 2 else { - //RecoveredClass parentClass = vftableToClassMap.get(vftableAddress); RecoveredClass parentClass = getVftableClass(vftableAddress); - parentClass.addInlinedDestructor(firstVftableFunction); - parentClass.removeFromConstructorDestructorList(firstVftableFunction); - parentClass.removeIndeterminateConstructorOrDestructor(firstVftableFunction); + parentClass.addInlinedDestructor(vfunction); + + parentClass.removeFromConstructorDestructorList(vfunction); + parentClass.removeIndeterminateConstructorOrDestructor(vfunction); + isDeletingDestructor = true; } } } - // else, if first function pointed to by the vftable CALLS a function on the constructor/destructor list + // else, if the virtual function CALLS a function on the current class constructor/destructor list // then it is a deleting destructor and we have identified a destructor function for the class else { - processClassDeletingDestructor(recoveredClass, firstVftableFunction); - } + List classConstructorOrDestructorFunctions = + recoveredClass.getConstructorOrDestructorFunctions(); - } + Iterator functionIterator = classConstructorOrDestructorFunctions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); - /** - * Method to find deleting destructors that are the first function in the class vftable - * and call the class destructor - * @param recoveredClass the current class - * @param firstVirtualFunction the first function referenced by the class virtual function table - * @throws CancelledException when cancelled - * @throws InvalidInputException if issue setting return type - * @throws DuplicateNameException if try to create same symbol name already in namespace - */ - private void processClassDeletingDestructor(RecoveredClass recoveredClass, - Function firstVirtualFunction) - throws CancelledException, InvalidInputException, DuplicateNameException { + Function function = functionIterator.next(); - List classConstructorOrDestructorFunctions = - recoveredClass.getConstructorOrDestructorFunctions(); + if (extendedFlatAPI.doesFunctionACallFunctionB(vfunction, function)) { + recoveredClass.addDeletingDestructor(vfunction); - Iterator functionIterator = classConstructorOrDestructorFunctions.iterator(); - while (functionIterator.hasNext()) { - monitor.checkCanceled(); + addDestructorToClass(recoveredClass, function); - Function function = functionIterator.next(); - - if (extendedFlatAPI.doesFunctionACallFunctionB(firstVirtualFunction, function)) { - recoveredClass.addDeletingDestructor(firstVirtualFunction); - addDestructorToClass(recoveredClass, function); - recoveredClass.removeIndeterminateConstructorOrDestructor(function); - - if (!deletingDestructorsThatCallDestructor.contains(firstVirtualFunction)) { - deletingDestructorsThatCallDestructor.add(firstVirtualFunction); + recoveredClass.removeIndeterminateConstructorOrDestructor(function); + isDeletingDestructor = true; } } } + return isDeletingDestructor; + } /** @@ -6915,21 +6649,489 @@ public class RecoveredClassHelper { } /** - * Method that calls various methods to find deleting destructor functions which help - * identify class destructors - * @param recoveredClasses List of all class objects + * Method to find deleting destructors in the virtual functions of the given list of classes. + * Method finds the following use cases (Note that in all cases operator_delete is called after + * the called destructor or inlined destructor): + * 1. deleting destructors that reference their own vftable (ie on own c/d list) which means + * function is both a deleting destructor and has an inlined class destructor. + * 2. deleting destructors that references a parent vftable (ie on parent c/d list) which + * means function is a deleting destructor in the class and contains an inlined destructor + * for the parent class. + * 3. deleting destructors that do not reference a vftable but call their own destructor + * 4. deleting destructors that do not reference a vftable with only two calls and that calls + * a destructor from another class + * 5. deleting destructors that do not reference a vftable with only two calls tand that + * calls a vbase destructor + * @param recoveredClasses the given classes to process for deleting destructors * @throws CancelledException if cancelled - * @throws InvalidInputException if issues setting return value - * @throws DuplicateNameException if try to create same symbol name already in namespace - * @throws Exception if issues making label + * @throws DuplicateNameException if duplicate name + * @throws InvalidInputException if invalid input */ public void findDeletingDestructors(List recoveredClasses) - throws CancelledException, InvalidInputException, DuplicateNameException, Exception { + throws CancelledException, InvalidInputException, DuplicateNameException { - findFirstDeletingDestructors(recoveredClasses); - findMoreDeletingDestructors(recoveredClasses); - findDeletingDestructorsWithCallToDestructorWithNoVftableReference(recoveredClasses); + if (recoveredClasses.isEmpty()) { + return; + } + // try finding operator delete functions by name or if can't, by common function calls + List operatorDeletesList = getOperatorDeleteFunctions(recoveredClasses); + if (operatorDeletesList == null) { + return; + } + + // iterate over all class virtual functions to find the ones that are deleting destructors + for (RecoveredClass recoveredClass : recoveredClasses) { + monitor.checkCanceled(); + + List vfunctions = recoveredClass.getAllVirtualFunctions(); + + if (vfunctions.isEmpty()) { + continue; + } + + for (Function vfunction : vfunctions) { + monitor.checkCanceled(); + + // get the map of addresses in vfunction to the corresponding functions called + // from those addresses + Map addressToFunctionCallMap = + getAddressToFunctionCallMap(vfunction); + + if (addressToFunctionCallMap == null) { + continue; + } + + List
operatorDeleteCallingAddresses = getAddressesOfListedFunctionsInMap( + operatorDeletesList, addressToFunctionCallMap); + + // only process vfunctions with at least one call to operator delete + if (operatorDeleteCallingAddresses.size() == 0) { + continue; + } + + // get the first call to operator delete in the function + Collections.sort(operatorDeleteCallingAddresses); + + // get first vftable reference in vfuntion + List
vftableReferences = getVftableReferences(vfunction); + Address firstVftableReference = null; + if (vftableReferences != null) { + Collections.sort(vftableReferences); + firstVftableReference = vftableReferences.get(0); + } + + List possibleCalledDestructors = + getPossibleCalledDestructors(addressToFunctionCallMap, + operatorDeleteCallingAddresses, firstVftableReference); + + // process deleting destructors if type 1, 2 or 3 + boolean isDeletingDestructor = processDeletingDestructor(recoveredClass, vfunction); + if (isDeletingDestructor) { + processPossibleDestructors(possibleCalledDestructors, vfunction); + continue; + } + + // process deleting destructors type 4 and 5 + // if function has only two calls and one is a vetted possible destructor (ie on + // list called after first vftable reference and before operator delete) and the + // other is a call to operator delete, then it is one of these two types + if (!allClassFunctionsContainingVftableReference.contains(vfunction) && + operatorDeleteCallingAddresses.size() == 1 && + addressToFunctionCallMap.keySet().size() == 2 && + possibleCalledDestructors.size() == 1) { + + recoveredClass.addDeletingDestructor(vfunction); + Function destructor = possibleCalledDestructors.get(0); + + // if the called destructor isn't on the possible constructor/destructor + // list then it is a vbase destructor + if (firstVftableReference == null && + !allPossibleConstructorsAndDestructors.contains(destructor)) { + + recoveredClass.setVBaseDestructor(destructor); + continue; + } + + // else, if the vfunction CALLS a function on the constructor/destructor list + // it is a regular destructor for some class + if (allPossibleConstructorsAndDestructors.contains(destructor)) { + processFoundDestructor(destructor, vfunction); + } + } + + } + } + + } + + + /** + * Method to find the operator delete functions in the current program either by name or + * by common virtual function calls + * @return a list of operator_delete functions or null if none could be determined + * @throws CancelledException if cancelled + */ + private List getOperatorDeleteFunctions(List recoveredClasses) + throws CancelledException { + + // try finding operator delete functions by name or if can't, by common function calls + Set operatorDeletes = findOperatorDeletes(); + if (operatorDeletes.isEmpty()) { + operatorDeletes = findOperatorDeletesUsingCalledCommonFunction(recoveredClasses); + if (operatorDeletes.isEmpty()) { + return null; + } + } + + List operatorDeletesList = new ArrayList(operatorDeletes); + return operatorDeletesList; + } + + /** + * Method to find the operator delete functions in the current program either by name or + * by common virtual function calls + * @return a list of operator_delete functions or null if none could be determined + * @throws CancelledException if cancelled + */ + private List getOperatorNewFunctions(List recoveredClasses) + throws CancelledException { + + // try finding operator delete functions by name or if can't, by common function calls + Set operatorNews = findOperatorNews(); + if (operatorNews.isEmpty()) { + operatorNews = findOperatorNewsUsingCalledCommonFunction(recoveredClasses); + if (operatorNews.isEmpty()) { + return null; + } + } + + List operatorNewsList = new ArrayList(operatorNews); + return operatorNewsList; + } + + private List getPossibleCalledDestructors( + Map addressToFunctionCallMap, + List
operatorDeleteCallingAddresses, Address firstVftableReference) + throws CancelledException { + + Set possibleCalledDestructorSet = new HashSet(); + + List
callingAddresses = new ArrayList
(addressToFunctionCallMap.keySet()); + + callingAddresses.removeAll(operatorDeleteCallingAddresses); + + Collections.sort(operatorDeleteCallingAddresses); + Address operatorDeleteCallingAddress = operatorDeleteCallingAddresses.get(0); + + Collections.sort(callingAddresses); + for (Address callingAddress : callingAddresses) { + monitor.checkCanceled(); + + // skip if the function call is after the operator delete + if (callingAddress.getOffset() > operatorDeleteCallingAddress.getOffset()) { + continue; + } + + // skip if there is a vftableref and it is after the function call + if (firstVftableReference != null && + firstVftableReference.getOffset() > callingAddress.getOffset()) { + continue; + } + + // add the called function to the list if it is between the vftable ref and + // the operator delete or if there is no vftable ref, the call is before + // the operator delete call + Function calledFunction = addressToFunctionCallMap.get(callingAddress); + if (calledFunction.isThunk()) { + calledFunction.getThunkedFunction(true); + } + possibleCalledDestructorSet.add(calledFunction); + } + List possibleCalledDestructors = + new ArrayList(possibleCalledDestructorSet); + return possibleCalledDestructors; + } + + + private void processPossibleDestructors(List possibleDestructors, + Function calledFromFunction) + throws InvalidInputException, DuplicateNameException, CancelledException { + + if (possibleDestructors.isEmpty()) { + return; + } + + for (Function possibleDestructor : possibleDestructors) { + monitor.checkCanceled(); + if (allPossibleConstructorsAndDestructors.contains(possibleDestructor)) { + processFoundDestructor(possibleDestructor, calledFromFunction); + } + } + } + + private void processFoundDestructor(Function possibleDestructor, Function calledFromFunction) + throws InvalidInputException, DuplicateNameException { + + List classes = getClasses(possibleDestructor); + if (classes.size() == 1) { + RecoveredClass recoveredClass = classes.get(0); + addDestructorToClass(recoveredClass, possibleDestructor); + bookmarkAddress(possibleDestructor.getEntryPoint(), + recoveredClass.getName() + + " destructor found using last new called method - called from dd at " + + calledFromFunction.getEntryPoint(), + "TEST RC"); + recoveredClass.removeIndeterminateConstructorOrDestructor(possibleDestructor); + } + } + + + /** + * Method to return list of reference addresses for any of the given functions that are + * contained in the given map + * @param functions the given functions + * @param addressToCalledFunctions the given map of addresses to called functions + * @return a list of function addresses for functions that are on both the given function list + * and in the given map entry set + * @throws CancelledException if cancelled + */ + private List
getAddressesOfListedFunctionsInMap(List functions, + Map addressToCalledFunctions) throws CancelledException { + + List
calledFunctionsOnList = new ArrayList
(); + Set
addresses = addressToCalledFunctions.keySet(); + for (Address address : addresses) { + monitor.checkCanceled(); + + Function function = addressToCalledFunctions.get(address); + if (functions.contains(function)) { + calledFunctionsOnList.add(address); + } + } + return calledFunctionsOnList; + } + + /** + * Find operator_delete functions (including thunks) by name or by using calls to various + * named "free" functions + * @return a Set of operator_delete functions + * @throws CancelledException if cancelled + */ + public Set findOperatorDeletes() throws CancelledException { + + Set operatorDeletesBySymbol = findFunctionsUsingSymbolName("operator_delete"); + + if (!operatorDeletesBySymbol.isEmpty()) { + return operatorDeletesBySymbol; + } + + Set operatorDeletesByThunkToFree = new HashSet(); + + Set freesBySymbol = findFunctionsUsingSymbolName("_free"); + Set freebasesBySymbol = findFunctionsUsingSymbolName("_free_base"); + + freesBySymbol.addAll(freebasesBySymbol); + + if (!freesBySymbol.isEmpty()) { + for (Function function : freesBySymbol) { + monitor.checkCanceled(); + + Set thunksTo = getThunksTo(function); + operatorDeletesByThunkToFree.addAll(thunksTo); + } + } + + if (!operatorDeletesByThunkToFree.isEmpty()) { + return operatorDeletesByThunkToFree; + } + + // look for external free call + Set operatorDeletesByThunkToExternalFree = new HashSet(); + Set externalFrees = findFunctionsUsingSymbolName("free"); + if (!externalFrees.isEmpty()) { + for (Function externalFree : externalFrees) { + if (!externalFree.isExternal()) { + continue; + } + + // get all thunks to external free + Address[] thunksToExternalFree = externalFree.getFunctionThunkAddresses(true); + if (thunksToExternalFree == null) { + continue; + } + for (Address address : thunksToExternalFree) { + monitor.checkCanceled(); + + Function function = api.getFunctionAt(address); + operatorDeletesByThunkToExternalFree.add(function); + Set thunksToThunk = getThunksTo(function); + if (thunksToThunk.isEmpty()) { + continue; + } + for (Function thunkToExternalFree : thunksToThunk) { + monitor.checkCanceled(); + // in this case just want the true thunks not the single call functions - get unwinds that way + if (thunkToExternalFree.isThunk()) { + operatorDeletesByThunkToExternalFree.add(thunkToExternalFree); + } + } + } + + } + } + return operatorDeletesByThunkToExternalFree; + } + + /** + * Find operator_new functions (including thunks) by name or by using calls to various + * named "new" or "malloc" functions + * @return a Set of operator_new functions + * @throws CancelledException if cancelled + */ + public Set findOperatorNews() throws CancelledException { + + Set operatorDeletesBySymbol = findFunctionsUsingSymbolName("operator_new"); + + if (!operatorDeletesBySymbol.isEmpty()) { + return operatorDeletesBySymbol; + } + + // if can't find it by name, look for function with call to malloc then __callnewh + Set operatorNewsByCallToMallocAndCallnewh = new HashSet(); + + Set callnewhBySymbol = findFunctionsUsingSymbolName("__callnewh"); + callnewhBySymbol.addAll(findFunctionsUsingSymbolName("_callnewh")); + + Set mallocBySymbol = findFunctionsUsingSymbolName("malloc"); + + // return emtpy list if no methods called __callnewh + if (callnewhBySymbol.isEmpty()) { + return operatorNewsByCallToMallocAndCallnewh; + } + + if (mallocBySymbol.isEmpty()) { + return operatorNewsByCallToMallocAndCallnewh; + } + + Set functionsThatCallCallnewh = new HashSet(); + for (Function callnewh : callnewhBySymbol) { + monitor.checkCanceled(); + + Set callingFunctions = + new HashSet(callnewh.getCallingFunctions(monitor)); + functionsThatCallCallnewh.addAll(callingFunctions); + } + + for (Function malloc : mallocBySymbol) { + monitor.checkCanceled(); + + List callingFunctions = + new ArrayList(malloc.getCallingFunctions(monitor)); + + if (callingFunctions.isEmpty()) { + continue; + } + for (Function callingFunction : callingFunctions) { + monitor.checkCanceled(); + if (functionsThatCallCallnewh.contains(callingFunction) && + !operatorNewsByCallToMallocAndCallnewh.contains(callingFunction)) { + operatorNewsByCallToMallocAndCallnewh.add(callingFunction); + } + } + + } + + return operatorNewsByCallToMallocAndCallnewh; + } + + private Set findFunctionsUsingSymbolName(String name) throws CancelledException { + + Set functions = new HashSet(); + + FunctionManager functionManager = program.getFunctionManager(); + + SymbolIterator symbolIterator = symbolTable.getSymbolIterator(); + while (symbolIterator.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbolIterator.next(); + if (!symbol.getName().equals(name)) { + continue; + } + Address address = symbol.getAddress(); + + Function function = functionManager.getFunctionAt(address); + if (function != null) { + functions.add(function); + } + } + return functions; + } + + private Set getThunksTo(Function function) throws CancelledException { + + Set thunksToFunction = new HashSet(); + if (function == null) { + return thunksToFunction; + } + + Reference[] referencesTo = api.getReferencesTo(function.getEntryPoint()); + + if (referencesTo == null || referencesTo.length == 0) { + return thunksToFunction; + } + + for (Reference ref : referencesTo) { + monitor.checkCanceled(); + Address address = ref.getFromAddress(); + + Instruction instruction = program.getListing().getInstructionAt(address); + if (instruction == null) { + continue; + } + + Function thunkFunction = api.getFunctionContaining(address); + if (thunkFunction == null) { + continue; + } + + boolean isThunk = false; + if (instruction.getFlowType() == RefType.UNCONDITIONAL_JUMP && + thunkFunction.isThunk()) { + isThunk = true; + } + if (!isThunk) { + FlowOverride flowOverride = instruction.getFlowOverride(); + if (flowOverride.equals(FlowOverride.CALL) || + flowOverride.equals(FlowOverride.CALL_RETURN)) { + + // skip any that call more than one function to limit to only possible + // thunks + if (thunkFunction.getCalledFunctions(monitor).size() > 1) { + continue; + } + isThunk = true; + } + } + + if (isThunk) { + thunksToFunction.add(thunkFunction); + + // add thunks to this function too + Set thunksToThunk = getThunksTo(thunkFunction); + if (thunksToThunk.isEmpty()) { + continue; + } + + for (Function thunk : thunksToThunk) { + monitor.checkCanceled(); + + thunksToFunction.add(thunk); + } + + } + } + return thunksToFunction; } /** @@ -7188,6 +7390,51 @@ public class RecoveredClassHelper { return listOfUniqueAddresses; } + /** + * Remove functions that are on both vfunction and cd list from the cd lists and add the rest to + * a more restrictive list + * @param recoveredClasses List of RecoveredClass objects + * @throws CancelledException if cancelled + */ + protected void trimConstructorDestructorLists(List recoveredClasses) + throws CancelledException { + + if (recoveredClasses.isEmpty()) { + return; + } + + for (RecoveredClass recoveredClass : recoveredClasses) { + + monitor.checkCanceled(); + + List constructorOrDestructorFunctions = + recoveredClass.getConstructorOrDestructorFunctions(); + + if (constructorOrDestructorFunctions.isEmpty()) { + continue; + } + + Iterator cdFunctionIterator = constructorOrDestructorFunctions.iterator(); + while (cdFunctionIterator.hasNext()) { + + monitor.checkCanceled(); + + Function cdFunction = cdFunctionIterator.next(); + + if (!getAllVfunctions().contains(cdFunction)) { + updateAllPossibleConstructorDestructorList(cdFunction); + continue; + } + + // straight cd function will not also be a vfunction so remove it from the given + // class cd list + cdFunctionIterator.remove(); + recoveredClass.removeIndeterminateConstructorOrDestructor(cdFunction); + } + } + + } + /** * Method to apply the function signature of the given function, if different, to the corresponding * function definition and call the method to update related structure fields and functions @@ -7305,8 +7552,7 @@ public class RecoveredClassHelper { ReferenceManager refMan = program.getReferenceManager(); List
vftableAddresses = new ArrayList
(); - // TODO: need to look back recursively -- see ticket GP-1692 once finished - Address[] functionThunkAddresses = function.getFunctionThunkAddresses(); + Address[] functionThunkAddresses = function.getFunctionThunkAddresses(true); // add any thunk addresses to the list List
functionAddresses = @@ -7644,7 +7890,7 @@ public class RecoveredClassHelper { } // get the vftable structures that contain the pointer to the function definition - Set dataTypesContaining = dataTypeManager.getDataTypesContaining(pointer); + Collection dataTypesContaining = pointer.getParents(); for (DataType dataTypeContaining : dataTypesContaining) { monitor.checkCanceled(); @@ -8030,6 +8276,12 @@ public class RecoveredClassHelper { return changedItems; } + /** + * Method to retrieve the vftable symbols in the given namespace + * @param classNamespace the given namespace + * @return a list of vftable symbols in the given namespace + * @throws CancelledException if cancelled + */ public List getClassVftableSymbols(Namespace classNamespace) throws CancelledException { List vftableSymbols = new ArrayList();