GP-4748 Updated RttiUtil's find end of vftable to be more accurate.

This commit is contained in:
ghidra007 2024-07-19 21:48:15 +00:00
parent 220d6d9f58
commit f657b11c1d
3 changed files with 150 additions and 47 deletions

View file

@ -2662,8 +2662,9 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
if (numAddressRanges == 1) { if (numAddressRanges == 1) {
fixupContiguousDeletingDestructorSymbols(function); fixupContiguousDeletingDestructorSymbols(function);
processedFunctions.add(function); processedFunctions.add(function);
continue;
} }
else if (numAddressRanges == 2) { if (numAddressRanges == 2) {
// else fixup split dd function // else fixup split dd function
Function scalarDeletingDestructor = createSplitDeletingDestructorFunction(body); Function scalarDeletingDestructor = createSplitDeletingDestructorFunction(body);
if (scalarDeletingDestructor == null) { if (scalarDeletingDestructor == null) {
@ -2674,7 +2675,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
fixupSplitDeletingDestructorSymbols(function, scalarDeletingDestructor); fixupSplitDeletingDestructorSymbols(function, scalarDeletingDestructor);
processedFunctions.add(function); processedFunctions.add(function);
} }
// else if > 2 do nothing - not sure how to handle or even if they exist // if > 2 do nothing - not sure how to handle or even if they exist
} }
} }
} }

View file

@ -426,6 +426,22 @@ public class RecoveredClassHelper {
// of number of CALL instructions even if the call reg type // of number of CALL instructions even if the call reg type
functionCallMap.put(instruction.getMinAddress(), calledFunction); functionCallMap.put(instruction.getMinAddress(), calledFunction);
} }
if(instruction.getFlowOverride().equals(FlowOverride.CALL_RETURN)) {
Reference reference = instruction.getPrimaryReference(0);
if (reference == null) {
continue;
}
Address functionAddress = reference.getFromAddress();
Function secondHalfOfFunction = extendedFlatAPI.getReferencedFunction(functionAddress);
if(secondHalfOfFunction != null){
Map<Address,Function> functionCallMap2 = getFunctionCallMap(secondHalfOfFunction,false);
for(Address addr : functionCallMap2.keySet()) {
monitor.checkCancelled();
functionCallMap.put(addr, functionCallMap2.get(addr));
}
}
}
} }
return functionCallMap; return functionCallMap;
} }
@ -946,9 +962,9 @@ public class RecoveredClassHelper {
} }
/** /**
* Method to get a list of addresses that reference the given vftable address * Method to get a list of addresses that reference the given vftable address (only non-offcut ones)
* @param vftableAddress the given vftable address * @param vftableAddress the given vftable address
* @return list of addresses that reference the given vftable address * @return list of non-offcut addresses that reference the given vftable address
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
public List<Address> getReferencesToVftable(Address vftableAddress) throws CancelledException { public List<Address> getReferencesToVftable(Address vftableAddress) throws CancelledException {
@ -2239,41 +2255,62 @@ 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 callingFunction the given calling function * @param 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
*/ */
public boolean callsKnownConstructor(Function callingFunction) throws CancelledException { public boolean callsKnownConstructor(Set<Function> calledFunctions) throws CancelledException {
InstructionIterator instructions = callingFunction.getProgram() for (Function calledFunction : calledFunctions) {
.getListing()
.getInstructions(callingFunction.getBody(), true);
while (instructions.hasNext()) {
monitor.checkCancelled(); monitor.checkCancelled();
Instruction instruction = instructions.next();
if (instruction.getFlowType().isCall()) {
Function calledFunction = if (getAllConstructors().contains(calledFunction) || getAllInlinedConstructors().contains(calledFunction)) {
extendedFlatAPI.getReferencedFunction(instruction.getMinAddress(), true);
if (calledFunction == null) {
continue;
}
if (getAllConstructors().contains(calledFunction) ||
getAllInlinedConstructors().contains(calledFunction)) {
return true; return true;
} }
} }
}
return false; return false;
} }
/** /**
* Method to determine if the given function calls a known constructor or inlined constructor * Method to determine if the given function calls a known denstructor or inlined destructor
* @param callingFunction the given calling function * @param 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
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
public boolean callsKnownDestructor(Function callingFunction) throws CancelledException { public boolean callsKnownDestructor(Set<Function> calledFunctions) throws CancelledException {
for (Function calledFunction : calledFunctions) {
monitor.checkCancelled();
if (getAllDestructors().contains(calledFunction) || getAllInlinedDestructors().contains(calledFunction)) {
return true;
}
}
return false;
}
private boolean callsOwnFunction(RecoveredClass recoveredClass, Set<Function> calledFunctions) throws CancelledException {
for (Function calledFunction : calledFunctions) {
monitor.checkCancelled();
if(recoveredClass.getConstructorOrDestructorFunctions().contains(calledFunction)) {
return true;
}
}
return false;
}
private Set<Function> getCalledFunctions(Function callingFunction) throws CancelledException {
Set<Function> calledFunctions = new HashSet<Function>();
InstructionIterator instructions = callingFunction.getProgram() InstructionIterator instructions = callingFunction.getProgram()
.getListing() .getListing()
@ -2288,13 +2325,12 @@ public class RecoveredClassHelper {
if (calledFunction == null) { if (calledFunction == null) {
continue; continue;
} }
if (getAllDestructors().contains(calledFunction) ||
getAllInlinedDestructors().contains(calledFunction)) { calledFunctions.add(calledFunction);
return true;
} }
} }
}
return false; return calledFunctions;
} }
/** /**
@ -2535,7 +2571,7 @@ public class RecoveredClassHelper {
* @param recoveredClass the given class object * @param recoveredClass the given class object
* @return true if class has a vbase destructor, false if not * @return true if class has a vbase destructor, false if not
*/ */
private boolean hasVbaseDestructor(RecoveredClass recoveredClass) throws CancelledException { private boolean hasValidVbaseDestructor(RecoveredClass recoveredClass) throws CancelledException {
Function vBaseDestructor = recoveredClass.getVBaseDestructor(); Function vBaseDestructor = recoveredClass.getVBaseDestructor();
StringBuffer string = new StringBuffer(); StringBuffer string = new StringBuffer();
@ -5384,16 +5420,35 @@ public class RecoveredClassHelper {
// if inline, put on separate list and remove from indeterminate list // if inline, put on separate list and remove from indeterminate list
// process later // process later
if(callsOwnConstructorOrDestructor(recoveredClass, indeterminateFunction)) {
recoveredClass.addIndeterminateInline(indeterminateFunction);
indeterminateIterator.remove();
continue;
}
if (vftableReferenceList.size() > 1) { if (vftableReferenceList.size() > 1) {
if (!areVftablesInSameClass(vftableReferenceList)) { if (!areVftablesInSameClass(vftableReferenceList)) {
recoveredClass.addIndeterminateInline(indeterminateFunction); recoveredClass.addIndeterminateInline(indeterminateFunction);
indeterminateIterator.remove(); indeterminateIterator.remove();
} }
}
}
}
}
continue; private boolean callsOwnConstructorOrDestructor(RecoveredClass recoveredClass, Function function) throws CancelledException {
}
Set<Function> calledFunctions = getCalledFunctions(function);
List<Function> constructorOrDestructorFunctions = recoveredClass.getConstructorOrDestructorFunctions();
for(Function cdFunction : constructorOrDestructorFunctions) {
monitor.checkCancelled();
if(calledFunctions.contains(cdFunction)) {
return true;
} }
} }
return false;
} }
/** /**
@ -6075,9 +6130,16 @@ public class RecoveredClassHelper {
monitor.checkCancelled(); monitor.checkCancelled();
Function indeterminateFunction = indeterminateIterator.next(); Function indeterminateFunction = indeterminateIterator.next();
// first try identifying useing known constructors and destructors // weed out any that call own possible constructors and destructors
boolean callsKnownConstructor = callsKnownConstructor(indeterminateFunction); // as they will not be const/dest (possibly may be inlined one though
boolean callsKnownDestructor = callsKnownDestructor(indeterminateFunction); Set<Function> calledFunctions = getCalledFunctions(indeterminateFunction);
if(callsOwnFunction(recoveredClass, calledFunctions)) {
continue;
}
// first try identifying using known constructors and destructors
boolean callsKnownConstructor = callsKnownConstructor(calledFunctions);
boolean callsKnownDestructor = callsKnownDestructor(calledFunctions);
boolean callsAtexit = boolean callsAtexit =
extendedFlatAPI.doesFunctionACallFunctionB(indeterminateFunction, atexit); extendedFlatAPI.doesFunctionACallFunctionB(indeterminateFunction, atexit);
@ -6102,6 +6164,7 @@ public class RecoveredClassHelper {
// Next try identifying constructors using decompiler return type // Next try identifying constructors using decompiler return type
DataType decompilerReturnType = DataType decompilerReturnType =
decompilerUtils.getDecompilerReturnType(indeterminateFunction); decompilerUtils.getDecompilerReturnType(indeterminateFunction);
if (decompilerReturnType != null) { if (decompilerReturnType != null) {
String returnDataName = decompilerReturnType.getDisplayName(); String returnDataName = decompilerReturnType.getDisplayName();
@ -6149,7 +6212,7 @@ public class RecoveredClassHelper {
addConstructorToClass(recoveredClass, indeterminateFunction); addConstructorToClass(recoveredClass, indeterminateFunction);
indeterminateIterator.remove(); indeterminateIterator.remove();
} }
else if (stores.size() == 1 && loads.size() > 0) { else if (stores.size() == 1 && loads.size() >= 0) {
addDestructorToClass(recoveredClass, indeterminateFunction); addDestructorToClass(recoveredClass, indeterminateFunction);
indeterminateIterator.remove(); indeterminateIterator.remove();
} }
@ -7093,7 +7156,9 @@ public class RecoveredClassHelper {
if (vBaseDestructor == null) { if (vBaseDestructor == null) {
continue; continue;
} }
if (!hasVbaseDestructor(recoveredClass)) { // test whether the identified vbase destructor is valid and if not
// just make it a normal destructor
if (!hasValidVbaseDestructor(recoveredClass)) {
addDestructorToClass(recoveredClass, vBaseDestructor); addDestructorToClass(recoveredClass, vBaseDestructor);
recoveredClass.setVBaseDestructor(null); recoveredClass.setVBaseDestructor(null);
} }

View file

@ -73,7 +73,6 @@ public class RttiUtil {
SymbolTable symbolTable = program.getSymbolTable(); SymbolTable symbolTable = program.getSymbolTable();
// See if the symbol already exists for the RTTI data. // See if the symbol already exists for the RTTI data.
Symbol matchingSymbol = symbolTable.getSymbol(rttiSuffix, rttiAddress, classNamespace); Symbol matchingSymbol = symbolTable.getSymbol(rttiSuffix, rttiAddress, classNamespace);
if (matchingSymbol != null) { if (matchingSymbol != null) {
@ -170,8 +169,9 @@ public class RttiUtil {
} }
} }
// any references after the first one ends the table // any non-computed source type references after the first one ends the table
if (tableSize > 0 && referenceManager.hasReferencesTo(currentVfPointerAddress)) { if (tableSize > 0 &&
referenceIndicatesEndOfTable(referenceManager, currentVfPointerAddress)) {
break; break;
} }
@ -190,6 +190,43 @@ public class RttiUtil {
return tableSize; return tableSize;
} }
/**
* Method to determine if there certain types of references to the given address that would
* indicate the end of a vftable
* @param address the address of a possible pointer in a vftable
* @return true if there are references to the given address and any of the references are
* types that would indicate the given pointer should not be in the vftable preceding it. In
* general most references would fall into this category such as ones created by user, importer,
* disassembler. Returns false if no references or if the only references are ones not
* indicative of the end of a vftable.
*/
private static boolean referenceIndicatesEndOfTable(ReferenceManager referenceManager,
Address address) {
boolean hasReferencesTo = referenceManager.hasReferencesTo(address);
if (!hasReferencesTo) {
return false;
}
ReferenceIterator referenceIter = referenceManager.getReferencesTo(address);
while (referenceIter.hasNext()) {
Reference ref = referenceIter.next();
// if source type is any besides analysis then it is the kind of reference to stop
// the vftable
if (ref.getSource() != SourceType.ANALYSIS) {
return true;
}
// if it is analysis source type but reference is data that is not read this indicates
// it is not the kind of reference that should end a vftable
// For example something could be getting this address to figure out the address pointed
// to so that that address can be referenced.
if (ref.getReferenceType().isData() && !ref.getReferenceType().isRead()) {
return true;
}
}
return false;
}
/** /**
* Gets the namespace referred to by the type descriptor model if it can determine the * Gets the namespace referred to by the type descriptor model if it can determine the
* namespace. Otherwise it returns the empty string. * namespace. Otherwise it returns the empty string.