From 3d052cde1145a82ac16fa744a074a3af930a4aab Mon Sep 17 00:00:00 2001 From: ghidra007 Date: Tue, 18 May 2021 18:53:05 -0400 Subject: [PATCH] GP-339 Added RecoverClassesFromRTTI script and related classes. --- .../ghidra_scripts/GccRttiAnalysisScript.java | 1279 ++++ .../ghidra_scripts/GraphClassesScript.java | 201 + .../SearchForImageBaseOffsets.java | 84 + .../UpdateClassFunctionDataScript.java | 191 + .../ghidra_scripts/DecompilerScriptUtils.java | 280 + .../ghidra_scripts/EditStructureUtils.java | 381 + .../ghidra_scripts/ExtraScriptUtils.java | 1376 ++++ .../ghidra_scripts/RTTIClassRecoverer.java | 292 + .../ghidra_scripts/RTTIGccClassRecoverer.java | 1820 +++++ .../RTTIWindowsClassRecoverer.java | 2613 +++++++ .../RecoverClassesFromRTTIScript.java | 1450 ++++ .../ghidra_scripts/RecoveredClass.java | 665 ++ .../ghidra_scripts/RecoveredClassUtils.java | 6650 +++++++++++++++++ .../FixUpRttiAnalysisScript.java | 651 ++ .../ghidra_scripts/IdPeRttiScript.java | 68 + .../ghidra_scripts/RunRttiAnalyzerScript.java | 36 + 16 files changed, 18037 insertions(+) create mode 100644 Ghidra/Features/Base/ghidra_scripts/GccRttiAnalysisScript.java create mode 100644 Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java create mode 100644 Ghidra/Features/Base/ghidra_scripts/SearchForImageBaseOffsets.java create mode 100644 Ghidra/Features/Base/ghidra_scripts/UpdateClassFunctionDataScript.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/DecompilerScriptUtils.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/EditStructureUtils.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/ExtraScriptUtils.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/RTTIClassRecoverer.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/RTTIGccClassRecoverer.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/RTTIWindowsClassRecoverer.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClass.java create mode 100644 Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClassUtils.java create mode 100644 Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/FixUpRttiAnalysisScript.java create mode 100644 Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/IdPeRttiScript.java create mode 100644 Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/RunRttiAnalyzerScript.java diff --git a/Ghidra/Features/Base/ghidra_scripts/GccRttiAnalysisScript.java b/Ghidra/Features/Base/ghidra_scripts/GccRttiAnalysisScript.java new file mode 100644 index 0000000000..24bd590062 --- /dev/null +++ b/Ghidra/Features/Base/ghidra_scripts/GccRttiAnalysisScript.java @@ -0,0 +1,1279 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Script to create gcc RTTI vtables and structures +//@category C++ + +import java.util.*; + +import ghidra.app.cmd.label.DemanglerCmd; +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Function; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.symbol.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; + +public class GccRttiAnalysisScript extends GhidraScript { + + private static final String VTABLE_LABEL = "vtable"; + private static final String VFTABLE_LABEL = "vftable"; + private static final String DTM_CLASS_DATA_FOLDER_NAME = "ClassDataTypes"; + + // specific to gcc globals + Address class_type_info_vtable = null; + Address si_class_type_info_vtable = null; + Address vmi_class_type_info_vtable = null; + Address class_type_info = null; + Address si_class_type_info = null; + Address vmi_class_type_info = null; + + boolean isGcc = false; + + DataTypeManager dataTypeManager = null; + SymbolTable symbolTable = null; + int defaultPointerSize = 0; + GlobalNamespace globalNamespace = null; + CategoryPath classDataTypesCategoryPath = null; + + @Override + public void run() throws Exception { + + if (currentProgram == null) { + println("There is no open program"); + return; + } + + setIsGcc(); + + if (!isGcc) { + println("This script only handles gcc programs"); + return; + } + + defaultPointerSize = currentProgram.getDefaultPointerSize(); + if (defaultPointerSize != 4 && defaultPointerSize != 8) { + println("This script only works on 32 or 64 bit programs"); + return; + } + + dataTypeManager = currentProgram.getDataTypeManager(); + symbolTable = currentProgram.getSymbolTable(); + + globalNamespace = (GlobalNamespace) currentProgram.getGlobalNamespace(); + + // create the path for the data type manager root/ClassDataTypes folder + classDataTypesCategoryPath = + createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME); + + createGccRttiData(); + } + + /** + * Create data type manager path that will be used when data types are created to place them in the correct folder + * @param parent parent CategoryPath + * @param categoryName name of the new category in the parent path + * @return CategoryPath for new categoryName + * @throws CancelledException if cancelled + */ + private CategoryPath createDataTypeCategoryPath(CategoryPath parent, String categoryName) + throws CancelledException { + + CategoryPath dataTypePath; + + // if single namespace no parsing necessary, just create using given categoryName + if (!categoryName.contains("::")) { + dataTypePath = new CategoryPath(parent, categoryName); + return dataTypePath; + } + + // if category name contains :: but not valid template info then just + // replace ::'s with /'s to form multi level path + if (!containsTemplate(categoryName)) { + categoryName = categoryName.replace("::", "/"); + } + + // if category name contains both :: and matched template brackets then only replace the + // :: that are not contained inside template brackets + else { + boolean insideBrackets = false; + int numOpenedBrackets = 0; + int index = 0; + String newCategoryName = new String(); + while (index < categoryName.length()) { + monitor.checkCanceled(); + + if (categoryName.substring(index).startsWith("::") && !insideBrackets) { + newCategoryName = newCategoryName.concat("/"); + index += 2; + continue; + } + + String character = categoryName.substring(index, index + 1); + + newCategoryName = newCategoryName.concat(character); + index++; + + if (character.equals("<")) { + insideBrackets = true; + numOpenedBrackets++; + } + if (character.equals(">")) { + numOpenedBrackets--; + } + if (numOpenedBrackets == 0) { + insideBrackets = false; + } + } + categoryName = newCategoryName; + } + + String path; + if (parent.getName().equals("")) { + path = "/" + categoryName; + } + else { + path = "/" + parent.getName() + "/" + categoryName; + } + dataTypePath = new CategoryPath(path); + + return dataTypePath; + + } + + /** + * Method to check the given string to see if it contains valid template(s) + * @param name the given name to check + * @return true if name contains valid template(s), false otherwise + */ + private boolean containsTemplate(String name) { + + if (!name.contains("<")) { + return false; + } + + int numOpenLips = getNumSubstrings(name, "<"); + int numClosedLips = getNumSubstrings(name, ">"); + + if (numOpenLips > 0 && numClosedLips > 0 && numOpenLips == numClosedLips) { + return true; + } + return false; + } + + /** + * Method to return the number of the given substrings contained in the given string + * @param string the given string + * @param substring the given substring + * @return the number of the given substrings in the given string + */ + private int getNumSubstrings(String string, String substring) { + + int num = 0; + + int indexOf = string.indexOf(substring); + while (indexOf >= 0) { + num++; + string = string.substring(indexOf + 1); + indexOf = string.indexOf(substring); + } + return num; + } + + private void createGccRttiData() throws CancelledException, Exception { + + // find the three special vtables and replace the incorrectly made array with + // data types found in vtable + boolean continueProcessing = createSpecialVtables(); + if(!continueProcessing) { + return; + } + // find all typeinfo symbols and get their class namespace and create RecoveredClass object + List typeinfoSymbols = getListOfSymbolsInAddressSet( + currentProgram.getAddressFactory().getAddressSet(), "typeinfo", true); + + // create the appropriate type of type info struct at the various typeinfo symbol locations + createTypeinfoStructs(typeinfoSymbols); + + // process vtables and create classes for the vtables that have no typeinfo + processVtables(); + } + + /** + * Method to find the (up to three) special gcc vtables and replace the incorrectly made array with the + * correct data types. Also creates a type info symbol at the correct offset in the table. + * @return true if all found tables have a typeinfo symbol created successfully + * @throws CancelledException if cancelled + */ + private boolean createSpecialVtables() throws CancelledException { + + class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info"); + class_type_info = null; + if (class_type_info_vtable == null) { + println("__class_type_info vtable not found --> no classes without parents"); + } + else { + class_type_info = createSpecialVtable(class_type_info_vtable); + if (class_type_info == null) { + println( + "__class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + } + + si_class_type_info = null; + si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info"); + if (si_class_type_info_vtable == null) { + println("__si_class_type_info vtable not found --> no single parent classes"); + } + else { + si_class_type_info = createSpecialVtable(si_class_type_info_vtable); + if (si_class_type_info == null) { + println( + "__si_class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + } + + vmi_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__vmi_class_type_info"); + vmi_class_type_info = null; + if (vmi_class_type_info_vtable == null) { + println("__vmi_class_type_info vtable not found --> no multi-parent classes"); + } + else { + vmi_class_type_info = createSpecialVtable(vmi_class_type_info_vtable); + if (vmi_class_type_info == null) { + println( + "__vmi_class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + } + + if(class_type_info_vtable == null && si_class_type_info_vtable == null && vmi_class_type_info_vtable == null) { + println("Since there are no class typeinfo tables this program does not appear to have RTTI."); + return false; + } + return true; + } + + private Address findSpecialVtable(String namespace, String name) throws CancelledException { + + Address vtableAddress = null; + + Symbol symbolInNamespaces = getSymbolInNamespaces(namespace, name, VTABLE_LABEL); + + if (symbolInNamespaces != null) { + if (!symbolInNamespaces.isPrimary()) { + symbolInNamespaces.setPrimary(); + } + vtableAddress = symbolInNamespaces.getAddress(); + + return vtableAddress; + } + + // if there is just one address that has symbols containing both strings then it suggests + // mangled symbol since the above didn't find it + Address addressContainingBothStrings = + getSingleAddressOfSymbolContainingBothStrings(namespace, name); + if (addressContainingBothStrings == null) { + return null; + } + + // try demangling all the symbols at this address + Symbol[] vtableSymbols = symbolTable.getSymbols(addressContainingBothStrings); + for (Symbol vtableSymbol : vtableSymbols) { + DemanglerCmd cmd = + new DemanglerCmd(addressContainingBothStrings, vtableSymbol.getName()); + cmd.applyTo(currentProgram, monitor); + + } + + // now check again to see if we can find the namespace/name + symbolInNamespaces = getSymbolInNamespaces(namespace, name, VTABLE_LABEL); + + if (symbolInNamespaces != null) { + if (!symbolInNamespaces.isPrimary()) { + symbolInNamespaces.setPrimary(); + } + vtableAddress = symbolInNamespaces.getAddress(); + + return vtableAddress; + } + + println(namespace + "::" + name + " table not found"); + return null; + + } + + /** + * Method to replace the array incorrectly placed at special vftable with longs followed by + * typeinfo label + * @param vtableAddress the given special vtable address + * @return the address of the typeinfo in the vtable if replace was successful, null otherwise + * @throws CancelledException if cancelled + */ + private Address createSpecialVtable(Address vtableAddress) throws CancelledException { + + Symbol vtableSymbol = symbolTable.getPrimarySymbol(vtableAddress); + + clearListing(vtableAddress); + try { + int vtableLongs = createVtableLongs(vtableAddress); + + if (vtableLongs > 0) { + + Address typeinfoAddress = vtableAddress.add(vtableLongs * defaultPointerSize); + symbolTable.createLabel(typeinfoAddress, "typeinfo", + vtableSymbol.getParentNamespace(), SourceType.ANALYSIS); + return typeinfoAddress; + } + return null; + } + + catch (AddressOutOfBoundsException e) { + return null; + } + catch (IllegalArgumentException e) { + return null; + } + catch (InvalidInputException e) { + return null; + } + + } + + /** + * Method to create long data type at the given vtable address and return the number created OR + * if they are already created, just return how many there are + * @param vtableAddress the address of the given vtable + * @return the number of long data types at vtableAddress + */ + private int createVtableLongs(Address vtableAddress) { + + AddressSetView programAddressSet = currentProgram.getMemory().getAllInitializedAddressSet(); + DataType pointer = dataTypeManager.getPointer(null); + LongDataType longDT = new LongDataType(); + + int offset = 0; + int numLongs = 0; + while (true) { + + Address address = vtableAddress.add(offset); + + // Except for the first one which should have a symbol, if there is a symbol at the + // address, stop making longs because it there are no references into the vtable longs + if (offset > 0 && symbolTable.getSymbols(address).length > 0) { + return numLongs; + } + + // create a pointer and check to see if it is a reference to a valid memory location + try { + createData(address, pointer); + Address referencedAddress = getSingleReferencedAddress(address); + + // if it isn't valid, clear what we just created and increment to offset so + // the next can be checked + if (referencedAddress == null || !programAddressSet.contains(referencedAddress)) { + clearListing(address); + createData(address, longDT); + offset += defaultPointerSize; + numLongs++; + } + // if it is valid, leave the pointer created and get out of the loop + else { + return numLongs; + } + } + catch (Exception e) { + return numLongs; + } + + } + } + + /** + * Method to retrieve a single referenced address from the given address + * @param address the given address to look for a single referenced address + * @return the address referred to or null if none or more than one referenced + */ + private Address getSingleReferencedAddress(Address address) { + + List
refFromAddresses = getReferenceFromAddresses(address); + + if (refFromAddresses.size() != 1) { + return null; + } + + return refFromAddresses.get(0); + } + + /** + * Method to get a list of addressses that are references from the given address + * @param address the given address + * @return a list of addresses that are references from the given address + */ + private List
getReferenceFromAddresses(Address address) { + + Reference[] referencesFrom = getReferencesFrom(address); + + // get only the address references at the given address (ie no stack refs, ...) + List
refFromAddresses = new ArrayList
(); + for (Reference referenceFrom : referencesFrom) { + if (referenceFrom.isMemoryReference()) { + refFromAddresses.add(referenceFrom.getToAddress()); + } + } + + return refFromAddresses; + } + + private Address getSingleAddressOfSymbolContainingBothStrings(String string1, String string2) + throws CancelledException { + + List
symbolAddressList = new ArrayList
(); + + SymbolIterator symbols = symbolTable.getSymbolIterator("*" + string1 + "*", true); + + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + Address symbolAddress = symbol.getAddress(); + + if (symbol.getName().contains(string2)) { + if (!symbolAddressList.contains(symbolAddress)) { + symbolAddressList.add(symbolAddress); + } + } + } + if (symbolAddressList.size() == 1) { + return symbolAddressList.get(0); + } + return null; + } + + /** + * Method to return a symbol with the given name in the given namespace which is in the given + * parent namespace or null if one is not found + * @param parentNamespaceName name of parent namespace + * @param namespaceName name of symbol namespace + * @param symbolName name of symbol + * @return Symbol with given name, namespace and parent namespace or null if doesn't exist + * @throws CancelledException if cancelled + */ + private Symbol getSymbolInNamespaces(String parentNamespaceName, String namespaceName, + String symbolName) throws CancelledException { + + SymbolIterator symbols = symbolTable.getSymbols(symbolName); + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (symbol.getParentNamespace().getName().equals(namespaceName)) { + Namespace namespace = symbol.getParentNamespace(); + if (namespace.getParentNamespace().getName().equals(parentNamespaceName)) { + return symbol; + } + } + } + return null; + } + + /** + * Method to set the global variable isGcc + */ + private void setIsGcc() { + + isGcc = + currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase( + "gcc"); + } + + private void createTypeinfoStructs(List typeinfoSymbols) throws CancelledException { + + StructureDataType classTypeInfoStructure = createClassTypeInfoStructure(); + StructureDataType siClassTypeInfoStructure = + createSiClassTypeInfoStructure(classTypeInfoStructure); + StructureDataType baseClassTypeInfoStructure = + createBaseClassTypeInfoStructure(classTypeInfoStructure); + + Iterator typeinfoIterator = typeinfoSymbols.iterator(); + while (typeinfoIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol typeinfoSymbol = typeinfoIterator.next(); + Address typeinfoAddress = typeinfoSymbol.getAddress(); + + // skip the typeinfo symbols from the three special typeinfos + if (isSpecialTypeinfo(typeinfoAddress)) { + continue; + } + + Address specialTypeinfoRef = getSingleReferencedAddress(typeinfoAddress); + if (specialTypeinfoRef == null) { + println("No special typeinfo reference found. Cannot process typeinfo struct at " + + typeinfoAddress.toString()); + continue; + } + + if (!isSpecialTypeinfo(specialTypeinfoRef)) { + continue; + } + + try { + // create a "no inheritance" struct here + if (specialTypeinfoRef.equals(class_type_info)) { + clearListing(typeinfoAddress, + typeinfoAddress.add(classTypeInfoStructure.getLength())); + createData(typeinfoAddress, classTypeInfoStructure); + continue; + } + + // create a "single inheritance" struct here + if (specialTypeinfoRef.equals(si_class_type_info)) { + clearListing(typeinfoAddress, + typeinfoAddress.add(siClassTypeInfoStructure.getLength() - 1)); + createData(typeinfoAddress, siClassTypeInfoStructure); + continue; + } + + // create a "virtual multip inheritance" struct here + if (specialTypeinfoRef.equals(vmi_class_type_info)) { + + // get num base classes + int offsetOfNumBases = 2 * defaultPointerSize + 4; + int numBases = getInt(typeinfoAddress.add(offsetOfNumBases)); + + // get or create the vmiClassTypeInfoStruct + Structure vmiClassTypeinfoStructure = + (Structure) dataTypeManager.getDataType(classDataTypesCategoryPath, + "VmiClassTypeInfoStructure" + numBases); + if (vmiClassTypeinfoStructure == null) { + vmiClassTypeinfoStructure = + createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); + } + clearListing(typeinfoAddress, + typeinfoAddress.add(vmiClassTypeinfoStructure.getLength() - 1)); + createData(typeinfoAddress, vmiClassTypeinfoStructure); + + } + } + catch (Exception e) { + println("ERROR: Could not apply structure to " + typeinfoAddress); + } + } + + } + + /** + * Method to check if given typeinfo is one of the three special ones + * @param typeinfoAddress the given typeinfo address + * @return true if it is a special one, false otherwise + */ + private boolean isSpecialTypeinfo(Address typeinfoAddress) { + if (typeinfoAddress.equals(class_type_info) || typeinfoAddress.equals(si_class_type_info) || + typeinfoAddress.equals(vmi_class_type_info)) { + return true; + } + return false; + } + + private StructureDataType createClassTypeInfoStructure() { + + StructureDataType classTypeInfoStructure = new StructureDataType(classDataTypesCategoryPath, + "ClassTypeInfoStructure", 0, dataTypeManager); + + CharDataType characterDT = new CharDataType(); + DataType pointer = dataTypeManager.getPointer(null); + DataType charPointer = dataTypeManager.getPointer(characterDT); + classTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); + classTypeInfoStructure.add(charPointer, "typeinfoName", null); + + classTypeInfoStructure.setInternallyAligned(true); + + return classTypeInfoStructure; + } + + private StructureDataType createSiClassTypeInfoStructure( + StructureDataType classTypeInfoStructure) { + + StructureDataType siClassTypeInfoStructure = new StructureDataType( + classDataTypesCategoryPath, "SiClassTypeInfoStructure", 0, dataTypeManager); + + CharDataType characterDT = new CharDataType(); + DataType pointer = dataTypeManager.getPointer(null); + DataType charPointer = dataTypeManager.getPointer(characterDT); + //TODO: ?? replace with classTypeInfoStruct? + siClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); + siClassTypeInfoStructure.add(charPointer, "typeinfoName", null); + + DataType pointerToClassTypeInfoStruct = dataTypeManager.getPointer(classTypeInfoStructure); + siClassTypeInfoStructure.add(pointerToClassTypeInfoStruct, "baseClassTypeInfoPtr", null); + + siClassTypeInfoStructure.setInternallyAligned(true); + + return siClassTypeInfoStructure; + } + + private StructureDataType createBaseClassTypeInfoStructure( + StructureDataType classTypeInfoStructure) { + + StructureDataType baseclassTypeInfoStructure = new StructureDataType( + classDataTypesCategoryPath, "BaseClassTypeInfoStructure", 0, dataTypeManager); + + DataType classTypeInfoPointer = dataTypeManager.getPointer(classTypeInfoStructure); + + LongDataType longDT = new LongDataType(); + + baseclassTypeInfoStructure.add(classTypeInfoPointer, "classTypeinfoPtr", null); + baseclassTypeInfoStructure.add(longDT, "offsetFlags", null); + + baseclassTypeInfoStructure.setInternallyAligned(true); + + return baseclassTypeInfoStructure; + + } + + private StructureDataType createVmiClassTypeInfoStructure( + StructureDataType baseClassTypeInfoStructure, int numBaseClasses) { + + StructureDataType vmiClassTypeInfoStructure = + new StructureDataType(classDataTypesCategoryPath, + "VmiClassTypeInfoStructure" + numBaseClasses, 0, dataTypeManager); + + CharDataType characterDT = new CharDataType(); + UnsignedIntegerDataType unsignedIntDT = new UnsignedIntegerDataType(); + + DataType pointer = dataTypeManager.getPointer(null); + DataType charPointer = dataTypeManager.getPointer(characterDT); + + //TODO: ?? replace with classTypeInfoStruct? + vmiClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); + vmiClassTypeInfoStructure.add(charPointer, "typeinfoName", null); + vmiClassTypeInfoStructure.add(unsignedIntDT, "flags", null); + vmiClassTypeInfoStructure.add(unsignedIntDT, "numBaseClasses", null); + + // make array of base class type info structs + ArrayDataType baseClassArray = new ArrayDataType(baseClassTypeInfoStructure, numBaseClasses, + baseClassTypeInfoStructure.getLength()); + vmiClassTypeInfoStructure.add(baseClassArray, "baseClassPtrArray", null); + + vmiClassTypeInfoStructure.setInternallyAligned(true); + + return vmiClassTypeInfoStructure; + } + + /** + * Method to process the primary vtable for each "vtable" label + * @return the vftable Address in the vtable + * @throws Exception if Data cannot be created + */ + private void processVtables() throws Exception { + + + // find all vtable symbols + List listOfVtableSymbols = getListOfSymbolsInAddressSet( + currentProgram.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); + + Iterator vtableIterator = listOfVtableSymbols.iterator(); + while (vtableIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol vtableSymbol = vtableIterator.next(); + Namespace vtableNamespace = vtableSymbol.getParentNamespace(); + Address vtableAddress = vtableSymbol.getAddress(); + + processVtable(vtableAddress, vtableNamespace, true); + + + } + return; + } + + private void processVtable(Address vtableAddress, Namespace vtableNamespace, boolean isPrimary) + throws CancelledException { + + // skip the special tables + if (vtableAddress.equals(class_type_info_vtable) || + vtableAddress.equals(si_class_type_info_vtable) || + vtableAddress.equals(vmi_class_type_info_vtable)) { + return; + } + + Data dataAt = getDataAt(vtableAddress); + + // first check to see it is an erroneous vtable that has been made a byte array + // if so, clear it and start looking for the typeinfo reference + if (dataAt != null && dataAt.isArray()) { + clearListing(vtableAddress); + + } + if (dataAt != null && !dataAt.getDataType().getName().equals("long")) { + clearListing(vtableAddress); + } + + // find the special type info ref + Address typeinfoAddress = findNextTypeinfoRef(vtableAddress); + if (typeinfoAddress == null) { + println(vtableNamespace.getName() + " vtable has no typeinfo ref after vtable at " + + vtableAddress.toString()); + return; + } + + // create the typeinfo pointer if there isn't already one + Data typeinfoPtr = getDataAt(typeinfoAddress); + if (typeinfoPtr == null) { + DataType nullPointer = dataTypeManager.getPointer(null); + try { + createData(typeinfoAddress, nullPointer); + } + catch (Exception e) { + println("Could not create typeinfo pointer at " + typeinfoAddress.toString()); + } + } + + // create longs from top of vtable to the typeinfoAddress + createLongs(vtableAddress, typeinfoAddress); + + Address vftableAddress = getAddress(typeinfoAddress, defaultPointerSize); + + if (vftableAddress == null) { + return; + } + + + int numFunctionPointers = getNumFunctionPointers(vftableAddress, true, true); + + // if at least one function pointer make vftable label - the createVftable method will + // create the table later + if (numFunctionPointers > 0) { + + String vftableLabel = VFTABLE_LABEL; + if (!isPrimary) { + vftableLabel = "internal_" + vftableLabel; + } + + try { + Symbol vftableSymbol = symbolTable.createLabel(vftableAddress, vftableLabel, + vtableNamespace, SourceType.ANALYSIS); + + createVftableArray(vftableAddress, numFunctionPointers); + } + catch (IllegalArgumentException e) { + println("Could not label vftable at " + vftableAddress.toString()); + + } + catch (InvalidInputException e) { + println("Could not label vftable at " + vftableAddress.toString()); + + } + catch (CancelledException e) { + return; + } + catch (AddressOutOfBoundsException e) { + println("Couldn't create vftable due to Address out of bounds issue"); + return; + } + } + + + // check for an internal vtable and make a symbol there if there is one + // will process them later + Address possibleInternalVtableAddress = + getAddress(vftableAddress, defaultPointerSize * numFunctionPointers); + // if there is no symbol or a non-default symbol then the nextAddress is an internal + // vtable + if (possibleInternalVtableAddress == null) { + return; + } + Symbol possibleInternalVtableSymbol = + symbolTable.getPrimarySymbol(possibleInternalVtableAddress); + if (possibleInternalVtableSymbol != null && + possibleInternalVtableSymbol.getSource() != SourceType.DEFAULT && + (!possibleInternalVtableSymbol.getParentNamespace().equals(vtableNamespace) || + !possibleInternalVtableSymbol.getName().contains("vtable"))) { + return; + } + + if (possibleInternalVtableSymbol == null || + (possibleInternalVtableSymbol.getSource() == SourceType.DEFAULT && + (isValidVtableStart(possibleInternalVtableAddress) || + isValidVftableStart(possibleInternalVtableAddress)))) { + try { + symbolTable.createLabel(possibleInternalVtableAddress, + "internal_vtable_" + possibleInternalVtableAddress.toString(), + vtableNamespace, SourceType.ANALYSIS); + processVtable(possibleInternalVtableAddress, vtableNamespace, false); + } + catch (IllegalArgumentException e) { + println("Could not label internal vtable at " + + possibleInternalVtableAddress.toString()); + } + catch (InvalidInputException e) { + println("Could not label internal vtable at " + + possibleInternalVtableAddress.toString()); + } + + } + + } + + private Data createVftableArray(Address vftableAddress, int numFunctionPointers) + throws CancelledException, AddressOutOfBoundsException { + + clearListing(vftableAddress, + vftableAddress.add((numFunctionPointers * defaultPointerSize - 1))); + + DataType pointerDataType = dataTypeManager.getPointer(null); + ArrayDataType vftableArrayDataType = + new ArrayDataType(pointerDataType, numFunctionPointers, defaultPointerSize); + try { + Data vftableArrayData = createData(vftableAddress, vftableArrayDataType); + return vftableArrayData; + } + catch (Exception e) { + return null; + } + + } + + /** + * Method to check for a valid vtable at the given address + * @param vtableAddress the given address + * @return true if there is a valid vtable at the given address, false otherwise + */ + private boolean isValidVtableStart(Address vtableAddress) { + + // check that no refs into the first 2*defaultptr bytes + // skip top of table since that will have references to it + Address address = getAddress(vtableAddress, 1); + if (address == null) { + return false; + } + if (!areNoReferencesInto(address, 2 * defaultPointerSize - 1)) { + return false; + } + + // check that no pointers + if (!areNoReferencesFrom(vtableAddress, 2 * defaultPointerSize)) { + return false; + } + + // check that no other data exept possibly longs at correct offsets + if (!isNoDataCreatedExceptMaybeLongs(vtableAddress, 2 * defaultPointerSize)) { + return false; + } + + // TODO: maybe print a warning if the first item is not all zeros bc usually they are -- but pass + // it even then + + return true; + } + + private boolean isValidVftableStart(Address vftableAddress) throws CancelledException { + + // no refs into first defaaultPointerSize bytes + Address address = getAddress(vftableAddress, 1); + if (address == null) { + return false; + } + + if (!areNoReferencesInto(address, defaultPointerSize - 1)) { + return false; + } + + if (hasNumZeros(vftableAddress, defaultPointerSize)) { + return true; + } + + Data data = getDataAt(vftableAddress); + if (data != null) { + if (!data.isPointer()) { + return false; + } + Address referencedAddress = getSingleReferencedAddress(vftableAddress); + if (referencedAddress == null) { + return false; + } + Function functionAt = getFunctionAt(referencedAddress); + if (functionAt != null) { + return true; + } + } + else { + try { + Long longValue = getLong(address); + Address functionAddress = address.getNewAddress(longValue); + Function functionAt = getFunctionAt(functionAddress); + if (functionAt != null) { + return true; + } + } + catch (MemoryAccessException e) { + return false; + } + catch (AddressOutOfBoundsException e) { + return false; + } + + } + + return false; + } + + /** + * Method to check for num zeros at the given address + * @param address the given address + * @param numZeros the number of zeros to check for + * @return true if there are numZero zeros at the given address + * @throws CancelledException if cancelled + */ + private boolean hasNumZeros(Address address, int numZeros) throws CancelledException { + + int index = 0; + try { + while (index < numZeros) { + monitor.checkCanceled(); + if (getByte(address.add(index)) != 0x00) { + return false; + } + index++; + } + } + catch (MemoryAccessException e) { + return false; + } + catch (AddressOutOfBoundsException e) { + return false; + } + return true; + } + + private boolean areNoReferencesInto(Address topAddress, int length) { + + int offset = 0; + + MemoryBlock currentMemoryBlock = currentProgram.getMemory().getBlock(topAddress); + + while (offset < length) { + + Address address = getAddress(topAddress, offset); + + if (address == null) { + return false; + } + + if (!currentMemoryBlock.contains(address)) { + return false; + } + + Reference[] referencesTo = getReferencesTo(address); + if (referencesTo.length > 0) { + return false; + } + + offset++; + + } + return true; + } + + private boolean areNoReferencesFrom(Address topAddress, int length) { + + int offset = 0; + + MemoryBlock currentMemoryBlock = currentProgram.getMemory().getBlock(topAddress); + + while (offset < length) { + + Address address = getAddress(topAddress, offset); + + if (address == null) { + return false; + } + + if (!currentMemoryBlock.contains(address)) { + return false; + } + + List
referenceFromAddresses = getReferenceFromAddresses(address); + + if (referenceFromAddresses.size() > 0) { + return false; + } + + offset++; + + } + + return true; + + } + + private boolean isNoDataCreatedExceptMaybeLongs(Address startAddress, int length) { + + int offset = 0; + + MemoryBlock currentMemoryBlock = currentProgram.getMemory().getBlock(startAddress); + + while (offset < length) { + + Address address = getAddress(startAddress, offset); + + if (address == null) { + return false; + } + + if (!currentMemoryBlock.contains(address)) { + return false; + } + + Data data = getDataAt(address); + + // if there is data and it isn't on a pointer size boundary then return null + // if there is data and it is on a pointer size boundary but isn't a long then + // return null + // otherwise, continue + if (data != null) { + if (offset % defaultPointerSize == 0 && + data.getBaseDataType().getName().equals("long")) { + offset += defaultPointerSize; + continue; + } + return false; + } + offset++; + } + + return true; + + } + + private int getNumFunctionPointers(Address topAddress, boolean allowNullFunctionPtrs, + boolean allowDefaultRefsInMiddle) throws CancelledException { + + int numFunctionPointers = 0; + Address address = topAddress; + MemoryBlock currentBlock = currentProgram.getMemory().getBlock(topAddress); + + boolean stillInCurrentTable = true; + while (address != null && currentBlock.contains(address) && stillInCurrentTable && + (isPossibleFunctionPointer(address) || + (allowNullFunctionPtrs && isPossibleNullPointer(address)))) { + + numFunctionPointers++; + address = address.add(defaultPointerSize); + Symbol symbol = getSymbolAt(address); + if (symbol == null) { + continue; + } + // never let non-default refs in middle + if (symbol.getSource() != SourceType.DEFAULT) { + stillInCurrentTable = false; + } + + // if it gets here it is default + if (!allowDefaultRefsInMiddle) { + stillInCurrentTable = false; + } + } + + return numFunctionPointers; + + } + + /** + * Method to determine if there are enough zeros to make a null poihnter and no references into + * or out of the middle + * @param address the given address + * @return true if the given address could be a valid null pointer, false if not + */ + private boolean isPossibleNullPointer(Address address) throws CancelledException { + if (!hasNumZeros(address, defaultPointerSize)) { + return false; + } + return true; + } + + /** + * Method to determine if the given address contains a possible function pointer + * @param address the given address + * @return true if the given address contains a possible function pointer or false otherwise + */ + private boolean isPossibleFunctionPointer(Address address) { + + Address possibleFunctionPointer = getPointer(address); + if (possibleFunctionPointer == null) { + return false; + } + + Function function = getFunctionAt(possibleFunctionPointer); + if (function != null) { + return true; + } + return false; + } + + /** + * Method to get the pointer formed by the bytes at the current address + * @param address the given address + * @return the pointer formed by the bytes at the current address + */ + private Address getPointer(Address address) { + + try { + long offset = 0; + + if (defaultPointerSize == 4) { + offset = getInt(address); + } + if (defaultPointerSize == 8) { + offset = getLong(address); + } + if (offset == 0) { + return null; + } + Address possibleFunctionPointer = currentAddress.getNewAddress(offset); + return possibleFunctionPointer; + + } + catch (MemoryAccessException e) { + return null; + } + catch (AddressOutOfBoundsException e) { + return null; + } + } + + + private Address findNextTypeinfoRef(Address startAddress) { + + int offset = 0; + + Address address = getAddress(startAddress, offset); + + MemoryBlock currentMemoryBlock = currentProgram.getMemory().getBlock(startAddress); + + while (address != null && currentMemoryBlock.contains(address)) { + + //TODO: consider just returning once any symbol is found since I have + // never seen a ref to the longs or the typeinfo -- that way if there + // ever is a case where there is no typeinfo ref in primary vtable but there is in + // a secondary - don't think that is ever supposed to happen though -- it will get + // stopped by the symbols at vftable or internal vtable or next primary vtable + Symbol symbol = symbolTable.getPrimarySymbol(address); + // if the symbol we find is not a default symbol + // because we have reached the end of the item we are searching + if (!address.equals(startAddress) && symbol != null && + symbol.getSource() != SourceType.DEFAULT) { + return null; + } + + Address possibleTypeinfo = getPointer(address); + if (possibleTypeinfo == null) { + offset += defaultPointerSize; + address = getAddress(startAddress, offset); + continue; + } + + Symbol possibleTypeinfoSymbol = symbolTable.getPrimarySymbol(possibleTypeinfo); + if (possibleTypeinfoSymbol != null && + possibleTypeinfoSymbol.getName().equals("typeinfo")) { + return address; + } + offset += defaultPointerSize; + address = getAddress(startAddress, offset); + + } + + return null; + } + + /** + * Method to get a list of symbols either matching exactly (if exact flag is true) or containing (if exact flag is false) the given symbol name + * @param addressSet the address set to find matching symbols in + * @param symbolName the symbol name to match + * @param exact flag used to determine whether to return only exact symbol name matches or ones that contain the given symbol + * @return list of symbols in the address set with the given symbol name, only exact ones if exact flag is true or ones that contain the symbol if exact is false + * @throws CancelledException if cancelled + */ + private List getListOfSymbolsInAddressSet(AddressSet addressSet, String symbolName, + boolean exact) throws CancelledException { + + List symbolsInSet = new ArrayList(); + + SymbolIterator symbols = symbolTable.getSymbols(addressSet, SymbolType.LABEL, true); + + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (exact && symbol.getName().equals(symbolName)) { + symbolsInSet.add(symbol); + continue; + } + if (!exact && symbol.getName().contains(symbolName)) { + symbolsInSet.add(symbol); + } + } + return symbolsInSet; + } + + /** + * Method to create a series of long data types from the given start address to the given end + * address + * @param start the starting address + * @param end the ending address + */ + private void createLongs(Address start, Address end) { + + LongDataType longDT = new LongDataType(); + int offset = 0; + Address address = start; + while (address != null && !address.equals(end)) { + try { + clearListing(address); + createData(address, longDT); + offset += defaultPointerSize; + address = getAddress(start, offset); + } + catch (Exception e) { + return; + } + } + + } + + /** + * Method to get address at address + offset + * @param address the given address + * @param offset the given offset + * @return the address at address + offset or null if it doesn't exist + */ + private Address getAddress(Address address, int offset) { + try { + Address newAddress = address.add(offset); + return newAddress; + } + catch (AddressOutOfBoundsException e) { + return null; + } + } + +} + diff --git a/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java b/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java new file mode 100644 index 0000000000..aa451d717a --- /dev/null +++ b/Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java @@ -0,0 +1,201 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//Script to graph class hierarchies given metadata found in class structure description that +// was applied using the ExtractClassInfoFromRTTIScript. +//@category C++ +import java.util.*; + +import ghidra.app.script.GhidraScript; +import ghidra.app.services.GraphDisplayBroker; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.data.*; +import ghidra.service.graph.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class GraphClassesScript extends GhidraScript { + + List classStructures = new ArrayList(); + + @Override + public void run() throws Exception { + + if (currentProgram == null) { + println("There is no open program"); + return; + } + + DataTypeManager dataTypeManager = currentProgram.getDataTypeManager(); + + String path = new String("ClassDataTypes"); + CategoryPath dataTypePath = new CategoryPath(CategoryPath.ROOT, path); + + Category category = dataTypeManager.getCategory(dataTypePath); + if (category == null) { + println( + "/ClassDataTypes folder does not exist so there is no class data to process. Please run the ExtractClassInfoFromRTTIScript to generate the necessary information needed to run this script."); + return; + } + + Category[] subCategories = category.getCategories(); + + getClassStructures(subCategories); + + AttributedGraph graph = createGraph(); + if (graph.getVertexCount() == 0) { + println( + "There was no metadata in the class structures so a graph could not be created. Please run the ExtractClassInfoFromRTTIScript to generate the necessary information needed to run this script."); + } + else { + showGraph(graph); + } + + } + + private void getClassStructures(Category[] categories) throws CancelledException { + + for (Category category : categories) { + monitor.checkCanceled(); + DataType[] dataTypes = category.getDataTypes(); + for (DataType dataType : dataTypes) { + monitor.checkCanceled(); + if (dataType.getName().equals(category.getName()) && + dataType instanceof Structure) { + + // if the data type name is the same as the folder name then + // it is the main class structure + Structure classStructure = (Structure) dataType; + if (!classStructures.contains(classStructure)) { + classStructures.add(classStructure); + } + + } + } + + Category[] subcategories = category.getCategories(); + + if (subcategories.length > 0) { + getClassStructures(subcategories); + } + } + } + + private AttributedGraph createGraph() throws CancelledException { + + AttributedGraph g = new AttributedGraph(); + + Iterator classStructuresIterator = classStructures.iterator(); + while (classStructuresIterator.hasNext()) { + + monitor.checkCanceled(); + + Structure classStructure = classStructuresIterator.next(); + + String description = classStructure.getDescription(); + String mainClassName = getClassName(description); + + if (mainClassName == null) { + continue; + } + + AttributedVertex classVertex = g.addVertex(mainClassName); + + int numParents = 0; + while (description.contains(":")) { + + numParents++; + + int indexOfColon = description.indexOf(":", 0); + + description = description.substring(indexOfColon + 1); + + int endOfBlock = description.indexOf(":", 0); + if (endOfBlock == -1) { + endOfBlock = description.length(); + } + + String parentName = description.substring(0, endOfBlock); + + description = description.substring(endOfBlock); + + boolean isVirtualParent = false; + if (parentName.contains("virtual")) { + isVirtualParent = true; + } + + parentName = parentName.replace("virtual", ""); + parentName = parentName.replace(" ", ""); + + + AttributedVertex parentVertex = g.addVertex(parentName); + + AttributedEdge edge = g.addEdge(parentVertex, classVertex); + if (isVirtualParent) { + edge.setAttribute("Color", "Orange"); + } + // else leave it default lime green + } + + // no parent = blue vertex + if (numParents == 0) { + classVertex.setAttribute("Color", "Blue"); + } + // single parent = green vertex + else if (numParents == 1) { + classVertex.setAttribute("Color", "Green"); + } + // multiple parents = red vertex + else { + classVertex.setAttribute("Color", "Red"); + } + } + + return g; + } + + private void showGraph(AttributedGraph graph) throws Exception { + + GraphDisplay display; + PluginTool tool = state.getTool(); + GraphDisplayBroker broker = tool.getService(GraphDisplayBroker.class); + GraphDisplayProvider service = broker.getGraphDisplayProvider("Default Graph Display"); + display = service.getGraphDisplay(false, TaskMonitor.DUMMY); + display.setGraph(graph, "test graph", false, TaskMonitor.DUMMY); + } + + private String getClassName(String description) { + + // parse description for class hierarchy + if (!description.startsWith("class")) { + return null; + } + + // skip "class " to get overall class + description = description.substring(6); + int indexOfColon = description.indexOf(":", 0); + String mainClassName; + if (indexOfColon == -1) { + mainClassName = description; + } + else { + mainClassName = description.substring(0, indexOfColon - 1); + } + mainClassName = mainClassName.replace(" ", ""); + + return mainClassName; + } + +} diff --git a/Ghidra/Features/Base/ghidra_scripts/SearchForImageBaseOffsets.java b/Ghidra/Features/Base/ghidra_scripts/SearchForImageBaseOffsets.java new file mode 100644 index 0000000000..c86b9109a3 --- /dev/null +++ b/Ghidra/Features/Base/ghidra_scripts/SearchForImageBaseOffsets.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.Address; +import ghidra.util.exception.CancelledException; + +public class SearchForImageBaseOffsets extends GhidraScript { + + @Override + public void run() throws Exception { + + if (currentProgram == null) { + println("No open program"); + return; + } + + if (currentProgram.getMemory().isBigEndian()) { + println("This script only looks for little endian image base offsets"); + return; + } + + Address imageBase = currentProgram.getImageBase(); + + long currentAddressOffset = currentAddress.getOffset(); + long imageBaseOffset = imageBase.getOffset(); + + long currentAddressIbo = imageBaseOffset ^ currentAddressOffset; + + byte searchBytes[] = createLittleEndianByteArray(currentAddressIbo, 8); + println("searching for possible ibo64 references to " + currentAddress.toString() + " ..."); + searchForByteArray(searchBytes); + + searchBytes = createLittleEndianByteArray(currentAddressIbo, 4); + println("searching for possible ibo32 references to " + currentAddress.toString() + " ..."); + searchForByteArray(searchBytes); + + } + + /** + * Method to create a byte array out of the given long value + * @param value the given value + * @param numBytes the number of bytes from the low end of the value to copy into the array + * @return the little endian byte array for the given value + * @throws CancelledException if cancelled + */ + private byte[] createLittleEndianByteArray(long value, int numBytes) + throws CancelledException { + + + byte byteArray[] = new byte[numBytes]; + + for (int i = 0; i < numBytes; i++) { + monitor.checkCanceled(); + byteArray[i] = (byte) (value >> (8 * i) & 0xff); + } + + return byteArray; + } + + private void searchForByteArray(byte[] byteArray) throws CancelledException { + Address start = currentProgram.getMinAddress(); + Address found = find(start, byteArray); + while (found != null) { + monitor.checkCanceled(); + println(found.toString()); + start = found.add(1); + found = find(start, byteArray); + } + } + +} diff --git a/Ghidra/Features/Base/ghidra_scripts/UpdateClassFunctionDataScript.java b/Ghidra/Features/Base/ghidra_scripts/UpdateClassFunctionDataScript.java new file mode 100644 index 0000000000..167ef267f8 --- /dev/null +++ b/Ghidra/Features/Base/ghidra_scripts/UpdateClassFunctionDataScript.java @@ -0,0 +1,191 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//Script to update the given class's virtual functions' function signature data types and +// the given class's vfunction structure field name for any differing functions in +// the class virtual function table(s). To run, put the cursor on any of the desired class's +// virtual functions or at the top a class vftable. The script will not work if the _vftable +// structure is not applied to the vftable using the ExtractClassInfoFromRTTIScript. +//@category C++ + +import java.util.*; + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.DuplicateNameException; + +public class UpdateClassFunctionDataScript extends GhidraScript { + @Override + public void run() throws Exception { + + if (currentProgram == null) { + println("There is no open program"); + return; + } + + + Function function = getFunctionContaining(currentAddress); + if (function != null) { + + Namespace parentNamespace = function.getParentNamespace(); + + Parameter thisParam = function.getParameter(0); + if (thisParam.getName().equals("this")) { + DataType dataType = thisParam.getDataType(); + if (dataType instanceof Pointer) { + Pointer pointer = (Pointer) dataType; + DataType baseDataType = pointer.getDataType(); + if (baseDataType.getName().equals(parentNamespace.getName())) { + // call update + println("updating class " + parentNamespace.getName()); + updateClassFunctionDataTypes(parentNamespace); + return; + } + } + + } + + } + + Symbol primarySymbol = currentProgram.getSymbolTable().getPrimarySymbol(currentAddress); + if (primarySymbol.getName().equals("vftable") || + primarySymbol.getName().substring(1).startsWith("vftable")) { + updateClassFunctionDataTypes(primarySymbol.getParentNamespace()); + return; + } + + } + + private void updateClassFunctionDataTypes(Namespace classNamespace) + throws CancelledException, DuplicateNameException, DataTypeDependencyException { + + List classVftableSymbols = getClassVftableSymbols(classNamespace); + + Iterator vftableIterator = classVftableSymbols.iterator(); + while (vftableIterator.hasNext()) { + monitor.checkCanceled(); + Symbol vftableSymbol = vftableIterator.next(); + Address vftableAddress = vftableSymbol.getAddress(); + Data data = getDataAt(vftableAddress); + if (data == null) { + continue; + } + DataType baseDataType = data.getBaseDataType(); + if (!(baseDataType instanceof Structure)) { + continue; + } + + Structure vfunctionStructure = (Structure) baseDataType; + + Category category = getDataTypeCategory(vfunctionStructure); + + if (category == null) { + continue; + } + + // check that the structure name starts with _vtable and that it is in + // the dt folder with name + if (category.getName().equals(classNamespace.getName()) && + vfunctionStructure.getName().startsWith(classNamespace.getName() + "_vftable")) { + println( + "Updating vfunction signature data types and (if necessary) vtable structure for vftable at address " + + vftableAddress.toString()); + updateVfunctionDataTypes(data, vfunctionStructure, vftableAddress); + } + } + + } + + /** + * Method to find any function signatures in the given vfunction structure that have changed + * and update the function signature data types + * @throws DuplicateNameException + * @throws DataTypeDependencyException + */ + private void updateVfunctionDataTypes(Data structureAtAddress, Structure vfunctionStructure, + Address vftableAddress) throws DuplicateNameException, DataTypeDependencyException { + + DataTypeManager dtMan = currentProgram.getDataTypeManager(); + + int numVfunctions = structureAtAddress.getNumComponents(); + + for (int i = 0; i < numVfunctions; i++) { + Data dataComponent = structureAtAddress.getComponent(i); + + Reference[] referencesFrom = dataComponent.getReferencesFrom(); + if (referencesFrom.length != 1) { + continue; + } + Address functionAddress = referencesFrom[0].getToAddress(); + Function vfunction = getFunctionAt(functionAddress); + if (vfunction == null) { + continue; + } + FunctionDefinitionDataType functionSignatureDataType = + (FunctionDefinitionDataType) vfunction.getSignature(); + + DataTypeComponent structureComponent = vfunctionStructure.getComponent(i); + DataType componentDataType = structureComponent.getDataType(); + if (!(componentDataType instanceof Pointer)) { + continue; + } + + Pointer pointer = (Pointer) componentDataType; + DataType pointedToDataType = pointer.getDataType(); + if (functionSignatureDataType.equals(pointedToDataType)) { + continue; + } + // update data type with new new signature + dtMan.replaceDataType(pointedToDataType, functionSignatureDataType, true); + if (!structureComponent.getFieldName().equals(vfunction.getName())) { + structureComponent.setFieldName(vfunction.getName()); + } + } + + } + + private Category getDataTypeCategory(DataType dataType) { + + DataTypeManager dataTypeManager = currentProgram.getDataTypeManager(); + CategoryPath originalPath = dataType.getCategoryPath(); + Category category = dataTypeManager.getCategory(originalPath); + + return category; + } + + private List getClassVftableSymbols(Namespace classNamespace) + throws CancelledException { + + SymbolTable symbolTable = currentProgram.getSymbolTable(); + List vftableSymbols = new ArrayList(); + + SymbolIterator symbols = symbolTable.getSymbols(classNamespace); + while (symbols.hasNext()) { + + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (symbol.getName().equals("vftable") || + symbol.getName().substring(1).startsWith("vftable")) { + vftableSymbols.add(symbol); + } + + } + return vftableSymbols; + } +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerScriptUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerScriptUtils.java new file mode 100644 index 0000000000..e25970eda7 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/DecompilerScriptUtils.java @@ -0,0 +1,280 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import ghidra.app.decompiler.*; +import ghidra.framework.options.ToolOptions; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.util.OptionsService; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.ParameterDefinition; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.program.model.pcode.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class DecompilerScriptUtils { + + private Program program; + private PluginTool tool; + private TaskMonitor monitor; + + private DecompInterface decompInterface; + + DecompilerScriptUtils(Program program, PluginTool tool, TaskMonitor monitor) { + this.program = program; + this.monitor = monitor; + this.tool = tool; + + decompInterface = setupDecompilerInterface(); + } + + /** + * Method to setup the decompiler interface for the given program + * @return the interface to the decompiler + */ + public DecompInterface setupDecompilerInterface() { + + decompInterface = new DecompInterface(); + + DecompileOptions options; + options = new DecompileOptions(); + OptionsService service = tool.getService(OptionsService.class); + if (service != null) { + ToolOptions opt = service.getOptions("Decompiler"); + options.grabFromToolAndProgram(null, opt, program); + } + decompInterface.setOptions(options); + + decompInterface.toggleCCode(true); + decompInterface.toggleSyntaxTree(true); + decompInterface.setSimplificationStyle("decompile"); + + if (!decompInterface.openProgram(program)) { + return null; + } + return decompInterface; + + } + + public DecompInterface getDecompilerInterface() { + return decompInterface; + } + + + /** + * Method to decompile the given function and return the function's HighFunction + * @param function the given function + * @return the HighFunction for the given function or null if there are issues decompiling the function + */ + public HighFunction getHighFunction(Function function) { + + DecompileResults res = decompInterface.decompileFunction(function, + decompInterface.getOptions().getDefaultTimeout(), null); + + decompInterface.flushCache(); + return res.getHighFunction(); + } + + /** + * Method to get the decompiler version of the given function's return type (which is not always + * the same as the listing version) + * @param function the given function + * @return the decompiler version of the given function's return type (which is not always the + * same as the listing version) + */ + public DataType getDecompilerReturnType(Function function) { + + DecompileResults decompRes = decompInterface.decompileFunction(function, + decompInterface.getOptions().getDefaultTimeout(), monitor); + + //If can't decompile, return null + if (decompRes == null || decompRes.getHighFunction() == null || + decompRes.getHighFunction().getFunctionPrototype() == null) { + return null; + } + + return decompRes.getHighFunction().getFunctionPrototype().getReturnType(); + } + + + /** + * Method to retrieve the function signature string from the decompiler function prototype. NOTE: + * if there is a this param, it will not be included. + * @param function the given function + * @param includeReturn if true, include the return type in the signature string + * @return the function signature string + * @throws CancelledException if cancelled + */ + public String getFunctionSignatureString(Function function, boolean includeReturn) + throws CancelledException { + + if (function == null) { + return null; + } + + StringBuffer stringBuffer = new StringBuffer(); + + DecompileResults decompRes = decompInterface.decompileFunction(function, + decompInterface.getOptions().getDefaultTimeout(), monitor); + + //If can't decompile, show the listing version of the function signature + if (decompRes == null || decompRes.getHighFunction() == null || + decompRes.getHighFunction().getFunctionPrototype() == null) { + return null; + } + + + HighFunction highFunction = decompRes.getHighFunction(); + + FunctionPrototype functionPrototype = highFunction.getFunctionPrototype(); + + if (includeReturn) { + stringBuffer.append(functionPrototype.getReturnType().getDisplayName() + " "); + } + + stringBuffer.append(function.getName() + "("); + ParameterDefinition[] parameterDefinitions = functionPrototype.getParameterDefinitions(); + + if (parameterDefinitions == null) { + stringBuffer.append(");"); + } + else { + int paramCount = parameterDefinitions.length; + for (int i = 0; i < parameterDefinitions.length; i++) { + monitor.checkCanceled(); + ParameterDefinition param = parameterDefinitions[i]; + + if (param.getName().equals("this")) { + continue; + } + + stringBuffer.append(param.getDataType().getDisplayName() + " " + param.getName()); + + if (i == paramCount) { + stringBuffer.append(");"); + } + else { + stringBuffer.append(", "); + } + } + } + + return stringBuffer.toString(); + } + + /** + * Get the parameters from the decompiler for the given function + * @param function the given function + * @return the decompiler parameters for the given function + */ + public ParameterDefinition[] getParametersFromDecompiler(Function function) { + + DecompileResults decompRes = decompInterface.decompileFunction(function, + decompInterface.getOptions().getDefaultTimeout(), monitor); + + if (decompRes == null || decompRes.getHighFunction() == null) { + return null; + } + + return decompRes.getHighFunction().getFunctionPrototype().getParameterDefinitions(); + } + + /** + * Method to dispose the decompiler interface + */ + public void disposeDecompilerInterface() { + decompInterface.closeProgram(); + decompInterface.dispose(); + } + + public Address getAssignedAddressFromPcode(Varnode storedValue) { + + long addressOffset; + if (storedValue.isConstant()) { + addressOffset = storedValue.getOffset(); + Address possibleAddress = toAddr(addressOffset); + if (possibleAddress == null || !program.getMemory().contains(possibleAddress)) { + return null; + } + return possibleAddress; + } + + PcodeOp valuePcodeOp = storedValue.getDef(); + + if (valuePcodeOp == null) { + return null; + } + + if (valuePcodeOp.getOpcode() == PcodeOp.CAST || valuePcodeOp.getOpcode() == PcodeOp.COPY) { + + Varnode constantVarnode = valuePcodeOp.getInput(0); + return getAssignedAddressFromPcode(constantVarnode); + + } + else if (valuePcodeOp.getOpcode() != PcodeOp.PTRSUB) { + return null; + } + + // don't need to check isConst bc always is + Varnode constantVarnode = valuePcodeOp.getInput(1); + addressOffset = constantVarnode.getOffset(); + Address possibleAddress = toAddr(addressOffset); + if (possibleAddress == null || !program.getMemory().contains(possibleAddress)) { + return null; + } + return possibleAddress; + } + + /** + * Method to get the called address from the given CALL pcodeOp's input Varnode + * @param pcodeOpInput the Varnode from a CALL pcodeOp input + * @return the calledAddress + */ + public Address getCalledAddressFromCallingPcodeOp(Varnode pcodeOpInput) { + + PcodeOp def = pcodeOpInput.getDef(); + if (def == null) { + return null; + } + + Varnode defInput = def.getInput(1); + if (defInput == null) { + return null; + } + + Address defInputAddress = defInput.getAddress(); + if (defInputAddress == null) { + return null; + } + + long offset = defInputAddress.getOffset(); + + Address calledAddress = program.getMinAddress().getNewAddress(offset); + + return calledAddress; + } + + /** + * Returns a new address with the specified offset in the default address space. + * @param offset the offset for the new address + * @return a new address with the specified offset in the default address space + */ + public final Address toAddr(long offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } + +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/EditStructureUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/EditStructureUtils.java new file mode 100644 index 0000000000..b2ff78adf2 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/EditStructureUtils.java @@ -0,0 +1,381 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; + +import ghidra.program.model.data.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class EditStructureUtils { + + EditStructureUtils() { + + } + + + /** + * Method to determine if the given containing struct has components at the given offset + * that are either the same, all undefined1's or equivalently sized undefineds as the components + * in the given possible newInternalStruct + * @param containingStruct the given structure to check + * @param offset the offset at the containing struct to check for equivalent components + * @param newInternalStruct the possible new internal struct to replace at the given offset in the + * given containing struct + * @param monitor task monitor + * @return true if components in the containing struct are replaceable with the possible new + * internal struct, false otherwise + * @throws CancelledException if cancelled + */ + public boolean hasReplaceableComponentsAtOffset(Structure containingStruct, int offset, + Structure newInternalStruct, TaskMonitor monitor) throws CancelledException { + + DataTypeComponent[] newStructComponents = newInternalStruct.getComponents(); + + for (DataTypeComponent newStructComponent : newStructComponents) { + + monitor.checkCanceled(); + + int structOffset = newStructComponent.getOffset(); + + DataTypeComponent currentComponentAtOffset = + containingStruct.getComponentAt(offset + structOffset); + + DataType newStructCompDt = newStructComponent.getDataType(); + DataType containingComDt = currentComponentAtOffset.getDataType(); + + // if component dts are equal continue + if (newStructCompDt.equals(containingComDt)) { + continue; + } + + // if containing is all undefined1s at equivalent location then continue + if (hasEnoughUndefined1sAtOffset(containingStruct, offset + structOffset, + newStructCompDt.getLength(), monitor)) { + continue; + } + + // otherwise if lengths not equal then return false + if (newStructCompDt.getLength() != containingComDt.getLength()) { + return false; + } + + // component lengths are equal if it gets here + // if containing is not undefined then return false because the components would be + // incompatible types + if (!containingComDt.getName().startsWith("undefined")) { + return false; + } + + } + return true; + } + + /** + * Method to determine if there are at least the given length of undefined size 1 components at the given offset in the given structure + * @param structure the given structure + * @param offset the given offset + * @param length the length of undefine size 1 components to check for starting at given offset + * @param monitor task monitor + * @return true if there are at least length undefined size 1 components at the given offset in the given structure + * @throws CancelledException if cancelled + */ + public boolean hasEnoughUndefined1sAtOffset(Structure structure, int offset, int length, + TaskMonitor monitor) throws CancelledException { + + if (structure.getLength() < offset + length) { + return false; + } + + for (int i = offset; i < offset + length; i++) { + monitor.checkCanceled(); + DataTypeComponent component = structure.getComponentAt(i); + if (component == null) { + return false; + } + DataType dataType = component.getDataType(); + if (dataType.getName().equals("undefined") && dataType.getLength() == 1) { + continue; + } + return false; + } + return true; + } + + /** + * Method to check that all components starting with the one at the given offset and encompassing + * all up to the given length from the given offset are an undefined data type. If so, clear them all and return true. + * If not, do nothing and return false. + * @param structure the given structure + * @param offset the given offset in the structure + * @param length the total length from the offset to hopefully clear + * @param monitor task monitor + * @return true if successfully cleared from offset to offset+length, false otherwise + * @throws CancelledException if cancelled + */ + public boolean clearLengthAtOffset(Structure structure, int offset, int length, + TaskMonitor monitor) throws CancelledException { + + if (structure.getLength() < offset + length) { + return false; + } + + List offsetsToClear = new ArrayList(); + + int endOfClear = offset + length; + + while (offset < endOfClear) { + + monitor.checkCanceled(); + + DataTypeComponent component = structure.getComponentAt(offset); + DataType dataType = component.getDataType(); + + // return false if it would clear too much + if (offset + dataType.getLength() > endOfClear) { + return false; + } + + offsetsToClear.add(offset); + offset += dataType.getLength(); + continue; + + } + + if (offsetsToClear.isEmpty()) { + return false; + } + + Iterator offsetIterator = offsetsToClear.iterator(); + while (offsetIterator.hasNext()) { + Integer componentOffset = offsetIterator.next(); + // need to get ordinal from component using offset because after clearing + // component size > 1, the index changes + DataTypeComponent component = structure.getComponentAt(componentOffset); + int index = component.getOrdinal(); + structure.clearComponent(index); + } + return true; + } + + /** + * Method to determine if data type is an undefined size 1 data type + * @param dataType the given data type + * @return true if given data type is undefined size 1, false otherwise + */ + public boolean isUndefined1(DataType dataType) { + + if (isUndefined(dataType) && dataType.getLength() == 1) { + return true; + } + return false; + } + + /** + * Method to determine if data type is an undefined data type of any size + * @param dataType the given data type + * @return true if given data type is undefined of any size, false otherwise + */ + public boolean isUndefined(DataType dataType) { + if (dataType.getName().contains("undefined")) { + return true; + } + return false; + } + + /** + * Method to determine if there are at least the given length of undefined (any size) components at the given offset in the given structure + * @param structure the given structure + * @param offset the given offset + * @param length the total length of undefined components to check for starting at given offset + * @param monitor task monitor + * @return true if there are at least total length of undefined components at the given offset in the given structure + * @throws CancelledException if cancelled + */ + public boolean hasEnoughUndefinedsOfAnyLengthAtOffset(Structure structure, int offset, + int length, TaskMonitor monitor) throws CancelledException { + + if (structure.getLength() < offset + length) { + return false; + } + + int endOfRange = offset + length; + + while (offset < endOfRange) { + + monitor.checkCanceled(); + + DataTypeComponent component = structure.getComponentAt(offset); + DataType dataType = component.getDataType(); + if (isUndefined(dataType)) { + + offset += dataType.getLength(); + if (offset > endOfRange) { + return false; + } + + continue; + + } + return false; + } + return true; + } + + /** + * Method to add a field to the given structure. If the structure already has data + * at the given offset, don't replace. If there is undefined data there then replace + * it with the data type. If the structure empty, insert the data type at the given offset. + * If the structure is not big enough and not-empty, grow it so there is room to replace. + * @param structure the given structure + * @param offset the offset to add a field + * @param dataType the data type to add to the field at the given offset + * @param fieldName the name of field + * @param monitor task monitor + * @return the updated structure data type + * @throws IllegalArgumentException if issue inserting data type into structure + * @throws CancelledException if cancelled + */ + public Structure addDataTypeToStructure(Structure structure, int offset, + DataType dataType, String fieldName, TaskMonitor monitor) + throws CancelledException, IllegalArgumentException { + + int dataTypeLength = dataType.getLength(); + + int endOfDataTypeInStruct = offset + dataTypeLength; + + int roomForData = structure.getLength() - endOfDataTypeInStruct; + + // if structure isn't defined insert + if (structure.isNotYetDefined()) { + structure.insertAtOffset(offset, dataType, dataTypeLength, fieldName, null); + return structure; + } + + // if not enough room, grow the structure + if (roomForData < 0) { + structure.growStructure(0 - roomForData); + } + + // else replace only if data already there are enough undefined data types at + // that offset to fit the new data type + if (hasEnoughUndefined1sAtOffset(structure, offset, dataTypeLength, monitor)) { + structure.replaceAtOffset(offset, dataType, dataTypeLength, fieldName, null); + } + + return structure; + } + + /** + * Method to determine if the given structure has room at the given offset to have a component of the given length added to it + * @param structureDataType the given structure + * @param offset the offset to check for available room + * @param lengthToAdd the length of bytes wanted to add at the offset + * @param monitor task monitor + * @return true if the given structure has room at the given offset to have a component of the given length added to it + * @throws CancelledException if cancelled + */ + public boolean canAdd(Structure structureDataType, int offset, int lengthToAdd, + TaskMonitor monitor) + throws CancelledException { + + // not big enough so return true so it can be grown + DataTypeComponent component = structureDataType.getComponentAt(offset); + if (component == null) { + return true; + } + + // no matter what size, if the data type at the offset is defined, return false + // so it is not replaced + if (!component.getDataType().getName().equals("undefined")) { + return false; + } + + // if structure isn't big enough but what is there is all undefined + // return true to grow it + int structLen = structureDataType.getLength(); + int spaceAvailable = structLen - (offset + lengthToAdd); + + if (spaceAvailable < 0) { + int overflow = 0 - spaceAvailable; + return hasEnoughUndefined1sAtOffset(structureDataType, offset, structLen - overflow, + monitor); + } + + // if structure is big enough and there is room at the offset return true + return hasEnoughUndefined1sAtOffset(structureDataType, offset, lengthToAdd, monitor); + + } + + /** + * Method to retrieve the number of undefined size 1 components in the given structure before the given offset + * @param structure the given structure + * @param offset the given offset + * @param monitor task monitor + * @return the number of undefined size 1 components in the given structure before the given offset + * @throws CancelledException if cancelled + */ + public int getNumberOfUndefinedsBeforeOffset(Structure structure, int offset, + TaskMonitor monitor) throws CancelledException { + + int numUndefineds = 0; + int index = offset - 1; + + while (index >= 0) { + monitor.checkCanceled(); + DataTypeComponent component = structure.getComponentAt(index); + if (component.getDataType().getName().equals("undefined") && + component.getLength() == 1) { + index--; + numUndefineds++; + } + else { + return numUndefineds; + } + } + return numUndefineds; + } + + /** + * Method to retrieve the number of undefined size 1 components starting at the given offset in the given structure + * @param structure the given structure + * @param offset the given offset + * @param monitor task monitor + * @return the number of undefined size 1 components starting at the given offset in the given structure + * @throws CancelledException if cancelled + */ + public int getNumberOfUndefinedsStartingAtOffset(Structure structure, int offset, + TaskMonitor monitor) throws CancelledException { + + int numUndefineds = 0; + int index = offset; + + while (index < structure.getLength()) { + monitor.checkCanceled(); + DataTypeComponent component = structure.getComponentAt(index); + if (component.getDataType().getName().equals("undefined") && + component.getLength() == 1) { + index++; + numUndefineds++; + } + else { + return numUndefineds; + } + } + return numUndefineds; + } + + +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/ExtraScriptUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/ExtraScriptUtils.java new file mode 100644 index 0000000000..e935fc3b8f --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/ExtraScriptUtils.java @@ -0,0 +1,1376 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; + +import ghidra.app.cmd.function.CreateFunctionCmd; +import ghidra.app.plugin.core.analysis.ReferenceAddressPair; +import ghidra.app.util.PseudoDisassembler; +import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.address.*; +import ghidra.program.model.block.CodeBlock; +import ghidra.program.model.block.IsolatedEntrySubModel; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function.FunctionUpdateType; +import ghidra.program.model.mem.DumbMemBufferImpl; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.symbol.*; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +public class ExtraScriptUtils extends FlatProgramAPI { + + Program program; + TaskMonitor taskMonitor; + int defaultPointerSize; + + ExtraScriptUtils(Program program, TaskMonitor taskMonitor) { + this.program = program; + this.taskMonitor = taskMonitor; + + currentProgram = program; + monitor = taskMonitor; + + defaultPointerSize = program.getDefaultPointerSize(); + } + + // Return true if the passed data is an array or structure of pointers. + // Return false otherwise. + public boolean isArrayOrStructureOfAllPointers(Data data) + throws CancelledException { + + if (data == null) { + return false; + } + + if (!data.isArray() && !data.isStructure()) { + return false; + } + + int numComponents = data.getNumComponents(); + + for (int ii = 0; ii < numComponents; ++ii) { + taskMonitor.checkCanceled(); + + Data component = data.getComponent(ii); + if (!component.isPointer()) { + return false; + } + } + return true; + } + + /** + * Method to get the pointer formed by the bytes at the current address + * @param address the given address + * @return the pointer formed by the bytes at the current address + */ + public Address getPointer(Address address) { + + try { + long offset = 0; + + if (defaultPointerSize == 4) { + offset = getInt(address); + } + if (defaultPointerSize == 8) { + offset = getLong(address); + } + if (offset == 0) { + return null; + } + + Address possibleFunctionPointer = address.getNewAddress(offset); + return possibleFunctionPointer; + + } + catch (MemoryAccessException e) { + return null; + } + catch (AddressOutOfBoundsException e) { + return null; + } + } + + /** + * Method to check to see if there is a valid function pointer at the given address. If it is + * valid but not created, create it + * @param address the given address + * @param allowNullFunctionPointer if true, allow null pointers as a valid function pointer + * @return true if it is a functin pointer, else returns false + * @throws CancelledException if cancelled + */ + public boolean isFunctionPointer(Address address, boolean allowNullFunctionPointer) + throws CancelledException { + + // check for or create null pointer if valid number of zeros at address + if (isNullPointer(address) && allowNullFunctionPointer) { + return true; + } + + // check for or create function pointer if valid function pointed to + Data data = program.getListing().getDefinedDataAt(address); + if (data != null) { + if (data.isPointer() && getPointedToFunction(address) != null) { + return true; + } + } + else { + + PointerDataType pointerDataType = new PointerDataType(); + + try { + data = createData(address, pointerDataType); + if (getPointedToFunction(address) != null) { + return true; + } + clearListing(address); + return false; + } + catch (Exception e) { + return false; + } + + } + return false; + + } + + /** + * Method to determine if the given address is a null pointer. If it isn't but is valid length + * of zeros, try creating one. + * @param address the given address + * @return true if given address is a valid null pointer, false otherwise + * @throws CancelledException if cancelled + */ + public boolean isNullPointer(Address address) throws CancelledException { + + if (!hasNumZeros(address, defaultPointerSize)) { + return false; + } + + DataType nullPointer = program.getDataTypeManager().getPointer(null); + Listing listing = program.getListing(); + Data d = listing.getDefinedDataAt(address); + if (d == null) { + try { + d = createData(address, nullPointer); + } + catch (Exception e) { + return false; + } + + } + + PointerDataType pointerDataType = new PointerDataType(); + if (d.getDataType().isEquivalent(pointerDataType)) { + return true; + } + + return false; + + } + + /** + * Method to check for num zeros at the given address + * @param address the given address + * @param numZeros the number of zeros to check for + * @return true if there are numZero zeros at the given address + * @throws CancelledException if cancelled + */ + public boolean hasNumZeros(Address address, int numZeros) throws CancelledException { + + int index = 0; + try { + while (index < numZeros) { + monitor.checkCanceled(); + if (getByte(address.add(index)) != 0x00) { + return false; + } + index++; + } + } + catch (MemoryAccessException e) { + return false; + } + catch (AddressOutOfBoundsException e) { + return false; + } + return true; + } + + /** + * Method to get the function pointed to by the given address if there is one. If the pointed to function + * is a thunk, get the thunked function + * @param address the given address + * @return the (thunked if a thunk) function pointed to by the given address + */ + public Function getPointedToFunction(Address address) { + + List
referencesFrom = getReferenceFromAddresses(address); + if (referencesFrom.size() != 1) { + return null; + } + + Address functionAddress = referencesFrom.get(0); + Function function = getFunctionAt(functionAddress); + if (function == null) { + // try to create function + function = createFunction(functionAddress, null); + if (function == null) { + return null; + } + } + + //if function is a thunk, get the thunked function + if (function.isThunk()) { + Function thunkedFunction = function.getThunkedFunction(true); + function = thunkedFunction; + } + return function; + } + + /** + * Method to get a list of addressses that are references from the given address + * @param address the given address + * @return a list of addresses that are references from the given address + */ + public List
getReferenceFromAddresses(Address address) { + + Reference[] referencesFrom = getReferencesFrom(address); + + // get only the address references at the given address (ie no stack refs, ...) + List
refFromAddresses = new ArrayList
(); + for (Reference referenceFrom : referencesFrom) { + if (referenceFrom.isMemoryReference()) { + refFromAddresses.add(referenceFrom.getToAddress()); + } + } + + return refFromAddresses; + } + + /** + * Method to get address at address + offset + * @param address the given address + * @param offset the given offset + * @return the address at address + offset or null if it doesn't exist + */ + public Address getAddress(Address address, int offset) { + try { + Address newAddress = address.add(offset); + return newAddress; + } + catch (AddressOutOfBoundsException e) { + return null; + } + } + + /** + * Method to determine if the given instruction is a terminating instruction (it return, ...) + * @param instruction the given instruction + * @return true if the given instruction is a terminating instruction, false otherwise + */ + public boolean isTerminatingInstruction(Instruction instruction) { + + FlowType flowType = instruction.getFlowType(); + FlowType overrideType = + FlowOverride.getModifiedFlowType(flowType, instruction.getFlowOverride()); + + if (flowType.isTerminal() || overrideType.isTerminal()) { + return true; + } + Function functionContaining = getFunctionContaining(instruction.getAddress()); + if (functionContaining != null) { + if (functionContaining.isThunk() && flowType.isJump()) { + return true; + } + if (flowType.isJump()) { + return true; + } + } + return false; + } + + /** + * Method to create the first function after the last terminating function before the given address + * @param address the given addres + * @param expectedFiller the expected filler byte value + * @return the created function or null if not created + */ + public Function createFunctionBefore(Address address, Byte expectedFiller) { + + PseudoDisassembler pseudoDisassembler = new PseudoDisassembler(program); + + Instruction instructionBefore = getInstructionBefore(address); + + if (instructionBefore == null || !isTerminatingInstruction(instructionBefore)) { + return null; + } + + Address maxAddress = instructionBefore.getMaxAddress(); + int maxLen = (int) (address.getOffset() - maxAddress.getOffset()); + if (maxLen <= 0) { + return null; + } + + int offset = 1; + + Byte filler; + try { + filler = getByte(maxAddress.add(offset)); + while (expectedFiller.equals(filler) && offset <= maxLen) { + offset++; + filler = getByte(maxAddress.add(offset)); + } + } + catch (MemoryAccessException e) { + return null; + } + catch (AddressOutOfBoundsException e) { + return null; + } + + Address functionStart = maxAddress.add(offset); + + if (!pseudoDisassembler.isValidSubroutine(functionStart, true)) { + return null; + } + if (getInstructionAt(functionStart) == null) { + disassemble(functionStart); + } + + Function function = createFunction(functionStart, null); + + return function; + } + + public Byte determineFillerByte() throws CancelledException, MemoryAccessException { + + Byte filler = null; + int functionsToCheck = 30; + Address minAddress = currentProgram.getMinAddress(); + Function currentFunction = getFunctionAfter(minAddress); + + while (currentFunction != null && functionsToCheck != 0) { + monitor.checkCanceled(); + Address maxAddress = currentFunction.getBody().getMaxAddress(); + Address fillerAddress = getAddress(maxAddress, 1); + + // if no next address or next function is directly after current then skip + if (fillerAddress == null || getFunctionContaining(fillerAddress) != null) { + currentFunction = getFunctionAfter(maxAddress); + continue; + } + + // get the number of same byte fillers after current function + int numFiller = getNumberOfSameFillerBytesStartingAtAddress(fillerAddress); + if (numFiller == 0) { + currentFunction = getFunctionAfter(maxAddress); + continue; + } + + // if there is filler but the next function isn't directly after the filler then skip + if (numFiller > 0) { + functionsToCheck--; + Address addressAfterLastFiller = getAddress(fillerAddress, numFiller); + if (addressAfterLastFiller == null || + getFunctionAt(addressAfterLastFiller) == null) { + currentFunction = getFunctionAfter(maxAddress); + continue; + } + // if same filler value fills in all addresses between two functions then get the + // filler value + Byte currentFiller = getByte(fillerAddress); + if (filler == null) { + filler = currentFiller; + currentFunction = getFunctionAfter(maxAddress); + continue; + } + if (!filler.equals(currentFiller)) { + return null; + } + } + } + // get number of undefined bytes + // get address of end of undefineds and make sure it is a function + // if so, see if all bytes are same + // make the second function the new current function + + return filler; + } + + public int getNumberOfSameFillerBytesStartingAtAddress(Address firstAddress) + throws CancelledException, MemoryAccessException { + + AddressSetView validMemory = program.getMemory().getLoadedAndInitializedAddressSet(); + + if (firstAddress == null) { + return 0; + } + Address address = firstAddress; + int numSameByte = 0; + byte currentByte = getByte(address); + while (validMemory.contains(address) && getFunctionContaining(address) == null) { + monitor.checkCanceled(); + numSameByte++; + address = getAddress(firstAddress, numSameByte); + if (address == null) { + return numSameByte; + } + byte nextByte = getByte(address); + if (nextByte != currentByte) { + return numSameByte; + } + } + + return numSameByte; + } + + /** + * Method to create functions given the list of addresses known to be contained in a function but + * that are not yet created + * @param containedAddresses list of address contained in an undefined function + * @throws CancelledException, MemoryAccessException + * @throws MemoryAccessException if issue accessing memory + */ + public void createUndefinedFunctions(List
containedAddresses) + throws CancelledException, MemoryAccessException { + + Byte filler = determineFillerByte(); + if (filler == null) { + // println("Can't determine filler byte so cannot create undefined functions"); + return; + } + + Iterator
iterator = containedAddresses.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Address address = iterator.next(); + + // a previously created one might call the one that contains this so + // it may have already been created - if so, skip + Function functionContaining = getFunctionContaining(address); + if (functionContaining != null) { + continue; + } + + Function newFunction = createFunctionBefore(address, filler); + if (newFunction == null) { + //println("Can't find function containing " + address.toString()); + continue; + } + + AddressSetView functionBody = newFunction.getBody(); + while (!functionBody.contains(address)) { + newFunction = createFunctionBefore(address, filler); + if (newFunction == null) { + continue; + } + functionBody = newFunction.getBody(); + } + } + } + + /** + * Method to create a function in the given program at the given address + * @param prog the given program + * @param addr the given address + * @param tMonitor a cancellable task monitor + * @return true if the function was created, false otherwise + */ + public boolean createFunction(Program prog, Address addr, TaskMonitor tMonitor) { + + try { + AddressSet subroutineAddresses = getSubroutineAddresses(prog, addr, tMonitor); + if (subroutineAddresses.isEmpty()) { + return false; + } + + CreateFunctionCmd cmd = new CreateFunctionCmd(null, subroutineAddresses.getMinAddress(), + null, SourceType.DEFAULT); + if (cmd.applyTo(prog, tMonitor)) { + return true; + } + + return false; + } + catch (CancelledException e) { + return false; + } + + } + + /** + * Method to figure out a subroutine address set given an address contained in it + * @param program the given program + * @param address address in the potential subroutine + * @param monitor allows canceling + * @return address set of the subroutine to be created + * @throws CancelledException if cancelled + */ + public static AddressSet getSubroutineAddresses(Program program, Address address, + TaskMonitor monitor) throws CancelledException { + + // Create a new address set to hold the entire selection. + AddressSet subroutineAddresses = new AddressSet(); + + IsolatedEntrySubModel model = new IsolatedEntrySubModel(program); + CodeBlock[] codeBlocksContaining = model.getCodeBlocksContaining(address, monitor); + + for (CodeBlock element : codeBlocksContaining) { + + if (monitor.isCancelled()) { + return subroutineAddresses; + } + subroutineAddresses.add(element); + } + + return subroutineAddresses; + } + + /** + * Method to make the given function a thiscall + * @param function the given function + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void makeFunctionThiscall(Function function) + throws InvalidInputException, DuplicateNameException { + + if (function.getCallingConventionName().equals("__thiscall")) { + return; + } + + ReturnParameterImpl returnType = + new ReturnParameterImpl(function.getSignature().getReturnType(), currentProgram); + + function.updateFunction("__thiscall", returnType, + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, function.getSignatureSource(), + function.getParameters()); + } + + /** + * Method to get a list of symbols either matching exactly (if exact flag is true) or containing (if exact flag is false) the given symbol name + * @param addressSet the address set to find matching symbols in + * @param symbolName the symbol name to match + * @param exact flag used to determine whether to return only exact symbol name matches or ones that contain the given symbol + * @return list of symbols in the address set with the given symbol name, only exact ones if exact flag is true or ones that contain the symbol if exact is false + * @throws CancelledException if cancelled + */ + public List getListOfSymbolsInAddressSet(AddressSet addressSet, String symbolName, + boolean exact) throws CancelledException { + + List symbolsInSet = new ArrayList(); + + SymbolIterator symbols = + currentProgram.getSymbolTable().getSymbols(addressSet, SymbolType.LABEL, true); + + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (exact && symbol.getName().equals(symbolName)) { + symbolsInSet.add(symbol); + continue; + } + if (!exact && symbol.getName().contains(symbolName)) { + symbolsInSet.add(symbol); + } + } + return symbolsInSet; + } + + /** + * Method to return the referenced address at the given address + * Note: this will work whether there is a created reference or not + * @param address the address to look for a referenced address at + * @param getIboIf64bit if true, get the address corresponding to the image base offset instead + * of the full reference address + * @return the first referenced address from the given address + */ + public Address getReferencedAddress(Address address, boolean getIboIf64bit) { + + int addressSize = address.getSize(); + if (addressSize == 64 && getIboIf64bit) { + ImageBaseOffset32DataType ibo32 = + new ImageBaseOffset32DataType(program.getDataTypeManager()); + int length = ibo32.getLength(); + DumbMemBufferImpl compMemBuffer = + new DumbMemBufferImpl(currentProgram.getMemory(), address); + Object value = ibo32.getValue(compMemBuffer, ibo32.getDefaultSettings(), length); + if (value instanceof Address) { + Address iboAddress = (Address) value; + return iboAddress; + } + return null; + } + + try { + if (addressSize == 32) { + long offset32 = getInt(address); + return address.getNewAddress(offset32); + + } + else if (addressSize == 64) { + + long offset64 = getLong(address); + return address.getNewAddress(offset64); + + } + else { + return null; + } + } + catch (MemoryAccessException e) { + return null; + } + + } + + /** + * Method to return a list of symbols with the given name and namespace. + * @param symbolName the symbol name to retrieve + * @param namespace the namespace to look for symbols with the given name + * @param exact if true, return only exact symbol names, if false, return symbols that contain the given n name + * @return List of symbols in given namespace either with exact matching name or containing name + * @throws CancelledException when cancelled + */ + public List getListOfSymbolsByNameInNamespace(String symbolName, Namespace namespace, + boolean exact) throws CancelledException { + + List symbolList = new ArrayList(); + + SymbolIterator symbols = program.getSymbolTable().getSymbols(namespace); + + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (exact && symbol.getName().equals(symbolName)) { + symbolList.add(symbol); + continue; + } + if (!exact && symbol.getName().contains(symbolName)) { + symbolList.add(symbol); + } + } + return symbolList; + } + + public boolean doesFunctionACallAnyListedFunction(Function aFunction, List bFunctions) + throws CancelledException { + Iterator bFunctionsIterator = bFunctions.iterator(); + while (bFunctionsIterator.hasNext()) { + monitor.checkCanceled(); + Function bFunction = bFunctionsIterator.next(); + if (doesFunctionACallFunctionB(aFunction, bFunction)) { + return true; + } + } + return false; + } + + /** + * Get the nth called function from calling function + * @param callingFunction The calling function + * @param callIndex the called function index (ie 1 = first called function) + * @return the nth called function in calling function + * @throws CancelledException if cancelled + */ + public Function getCalledFunctionByCallOrder(Function callingFunction, int callIndex) + throws CancelledException { + + List orderedReferenceAddressPairsFromCallingFunction = + getOrderedReferenceAddressPairsFromCallingFunction(callingFunction); + if (callIndex > orderedReferenceAddressPairsFromCallingFunction.size()) { + return null; + } + + 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; + + } + + /** + * Method to retrieve an ordered list (ordered by reference address low to high) of + * ReferenceAddressPairs of called functions given the calling function + * @param callingFunction the calling function + * @return an ordered list (ordered by reference address low to high) of ReferenceAddressPairs + * of called functions given the calling function + * @throws CancelledException if cancelled + */ + public List getOrderedReferenceAddressPairsFromCallingFunction( + Function callingFunction) throws CancelledException { + + List referenceAddressPairs = new ArrayList(); + + Iterator calledFunctionsIterator = + callingFunction.getCalledFunctions(monitor).iterator(); + while (calledFunctionsIterator.hasNext()) { + monitor.checkCanceled(); + Function calledFunction = calledFunctionsIterator.next(); + List
referencesToFunctionBFromFunctionA = + getReferencesToFunctionBFromFunctionA(callingFunction, calledFunction); + // add them to list of ref address pairs + Iterator
iterator = referencesToFunctionBFromFunctionA.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Address sourceRefAddr = iterator.next(); + referenceAddressPairs.add( + new ReferenceAddressPair(sourceRefAddr, calledFunction.getEntryPoint())); + } + } + + referenceAddressPairs.sort((a1, a2) -> a1.getSource().compareTo(a2.getSource())); + return referenceAddressPairs; + } + + /** + * Method to get reference addresses to function b from function a + * @param aFunction function a + * @param bFunction function b + * @return a list of reference address to function b from function a + * @throws CancelledException if cancelled + */ + public List
getReferencesToFunctionBFromFunctionA(Function aFunction, + Function bFunction) throws CancelledException { + + List
referenceAddresses = new ArrayList
(); + + ReferenceIterator referencesToFunctionBIterator = + program.getReferenceManager().getReferencesTo(bFunction.getEntryPoint()); + + while (referencesToFunctionBIterator.hasNext()) { + + monitor.checkCanceled(); + Reference ref = referencesToFunctionBIterator.next(); + + if (aFunction.getBody().contains(ref.getFromAddress())) { + referenceAddresses.add(ref.getFromAddress()); + } + } + return referenceAddresses; + } + + /** + * Method to determine if function a calls function b (or its thunk) + * @param aFunction function a + * @param bFunction function b + * @return true if function a calls function b (or its thunk), false otherwise + * @throws CancelledException if cancelled + */ + public boolean doesFunctionACallFunctionB(Function aFunction, Function bFunction) + throws CancelledException { + + Set calledFunctions = aFunction.getCalledFunctions(monitor); + if (calledFunctions.contains(bFunction)) { + return true; + } + Iterator functionIterator = calledFunctions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); + Function calledFunction = functionIterator.next(); + if (calledFunction.isThunk()) { + calledFunction = calledFunction.getThunkedFunction(true); + if (calledFunction.equals(bFunction)) { + return true; + } + } + } + return false; + } + + + /** + * Method to retrieve a single referenced address from the given address + * @param address the given address to look for a single referenced address + * @return the address referred to or null if none or more than one referenced + */ + public Address getSingleReferencedAddress(Address address) { + + List
refFromAddresses = getReferenceFromAddresses(address); + + if (refFromAddresses.size() != 1) { + return null; + } + + return refFromAddresses.get(0); + } + + /** + * Method to check for pointer to empty structure data type + * @param dataType the DataType to check + * @return true if empty, false if not empty + */ + public boolean isPointerToEmptyStructure(DataType dataType) { + if (dataType instanceof Pointer) { + Pointer ptr = (Pointer) dataType; + DataType baseDataType = ptr.getDataType(); + if (baseDataType instanceof Structure && baseDataType.isNotYetDefined()) { + return true; + } + } + return false; + } + + /** + * Method to create a PointerDataType to an appropriately sized undefined data type or + * a generic pointer data type + * @param dataType DataType + * @return PointerDataType to appropriately sized undefined data type + */ + public PointerDataType createPointerToUndefinedDataType(DataType dataType) { + + PointerDataType pdt = new PointerDataType(); + int dtLen = dataType.getLength(); + if (dataType instanceof Pointer) { + if (dtLen == 4) { + pdt = new PointerDataType(new Undefined4DataType()); + } + if (dtLen == 8) { + pdt = new PointerDataType(new Undefined8DataType()); + } + } + return pdt; + } + + /** + * Method to retrieve the minimum address on the given list + * @param list the list of addresses + * @return the minimum address on the given list + */ + public Address getMinimumAddressOnList(List
list) { + + Collections.sort(list); + return list.get(0); + } + + /** + * Method to retrieve the maximum address on the given list + * @param list the list of addresses + * @return the maximum address on the given list + */ + public Address getMaximumAddressOnList(List
list) { + + Collections.sort(list, Collections.reverseOrder()); + return list.get(0); + } + + /** + * Method to retrieve the referenced Functions from the given referenceToClassMap + * @param referenceToClassMap map of addresses that contain a reference to either a vftable or + * called function for a particular function + * @return List of functions referenced in the map + * @throws CancelledException if cancelled + */ + public List
getReferencesToFunctions(Map referenceToClassMap) + throws CancelledException { + + List
referencesToFunctions = new ArrayList
(); + + List
referenceAddresses = new ArrayList
(referenceToClassMap.keySet()); + Iterator
referenceIterator = referenceAddresses.iterator(); + while (referenceIterator.hasNext()) { + monitor.checkCanceled(); + Address referenceAddress = referenceIterator.next(); + Address referencedAddress = getSingleReferencedAddress(referenceAddress); + if (referencedAddress == null) { + continue; + } + Function function = getFunctionAt(referencedAddress); + + // skip the ones that reference a vftable + if (function != null) { + referencesToFunctions.add(referenceAddress); + } + } + + return referencesToFunctions; + } + + /** + * Method to get the function referenced at the given address + * @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 + */ + public Function getReferencedFunction(Address address, boolean getThunkedFunction) { + + Address referencedAddress = getSingleReferencedAddress(address); + + if (referencedAddress == null) { + return null; + } + + Function function = getFunctionAt(referencedAddress); + + if (function == null) { + return null; + } + + if (!getThunkedFunction) { + return function; + } + + if (function.isThunk()) { + function = function.getThunkedFunction(true); + } + + return function; + + } + + /** + * Method to return the Structure data type that the given data type points to or null + * if given data type is not a pointer of if it doesn't point to a structure + * @param dataType the given data type + * @return the base structure data type or null if the base data type isn't a structure + */ + public Structure getBaseStructureDataType(DataType dataType) { + if (dataType instanceof Pointer) { + Pointer ptr = (Pointer) dataType; + DataType baseDataType = ptr.getDataType(); + if (baseDataType instanceof Structure) { + return (Structure) baseDataType; + } + return null; + } + + return null; + } + + /** + * Method to check for pointer to empty structure data type + * @param dataType the DataType to check + * @return true if empty, false if not empty + */ + public boolean isEmptyStructure(DataType dataType) { + + if (dataType instanceof Structure && dataType.isNotYetDefined()) { + return true; + } + + return false; + } + + /** + * Method to remove all symbols at the given address + * @param address the given address + * @throws CancelledException if cancelled + */ + public void removeAllSymbolsAtAddress(Address address) throws CancelledException { + + SymbolTable symbolTable = program.getSymbolTable(); + + Symbol primarySymbol = symbolTable.getPrimarySymbol(address); + + while (primarySymbol != null && primarySymbol.getSource() != SourceType.DEFAULT) { + monitor.checkCanceled(); + symbolTable.removeSymbolSpecial(primarySymbol); + primarySymbol = symbolTable.getPrimarySymbol(address); + } + } + + /** + * Method to add the given comment to an existing plate comment unless it already exists in the comment + * @param address the given address + * @param comment the comment to add to the plate comment at the given address + */ + public void addUniqueStringToPlateComment(Address address, String comment) { + + String plateComment = getPlateComment(address); + + if (plateComment == null) { + return; + } + + if (!plateComment.contains(comment)) { + setPlateComment(address, plateComment + "\r\n" + comment); + } + } + + /** + * Method to determine if there are any symbols in the given namespace + * @param namespace the given namespace + * @return true if there are any symbols in the given namespace, false otherwise + */ + public boolean hasSymbolsInNamespace(Namespace namespace) { + + SymbolIterator namespaceSymbols = program.getSymbolTable().getSymbols(namespace); + + if (namespaceSymbols.hasNext()) { + return true; + } + return false; + + } + + /** + * Create data type manager path that will be used when data types are created to place them in the correct folder + * @param parent parent CategoryPath + * @param categoryName name of the new category in the parent path + * @return CategoryPath for new categoryName + */ + public CategoryPath createDataTypeCategoryPath(CategoryPath parent, String categoryName) { + + CategoryPath dataTypePath; + + // if single namespace no parsing necessary, just create using given categoryName + if (!categoryName.contains("::")) { + dataTypePath = new CategoryPath(parent, categoryName); + return dataTypePath; + } + + // if category name contains :: but not valid template info then just + // replace ::'s with /'s to form multi level path + if (!containsTemplate(categoryName)) { + categoryName = categoryName.replace("::", "/"); + } + + // if category name contains both :: and matched template brackets then only replace the + // :: that are not contained inside template brackets + else { + boolean insideBrackets = false; + int numOpenedBrackets = 0; + int index = 0; + String newCategoryName = new String(); + while (index < categoryName.length()) { + try { + monitor.checkCanceled(); + } + catch (CancelledException e) { + return null; + } + + if (categoryName.substring(index).startsWith("::") && !insideBrackets) { + newCategoryName = newCategoryName.concat("/"); + index += 2; + continue; + } + + String character = categoryName.substring(index, index + 1); + + newCategoryName = newCategoryName.concat(character); + index++; + + if (character.equals("<")) { + insideBrackets = true; + numOpenedBrackets++; + } + if (character.equals(">")) { + numOpenedBrackets--; + } + if (numOpenedBrackets == 0) { + insideBrackets = false; + } + } + categoryName = newCategoryName; + } + + String path; + if (parent.getName().equals("")) { + path = "/" + categoryName; + } + else { + path = "/" + parent.getName() + "/" + categoryName; + } + dataTypePath = new CategoryPath(path); + + return dataTypePath; + + } + + /** + * Method to check the given string to see if it contains valid template(s) + * @param name the given name to check + * @return true if name contains valid template(s), false otherwise + */ + private boolean containsTemplate(String name) { + + if (!name.contains("<")) { + return false; + } + + int numOpenLips = getNumSubstrings(name, "<"); + int numClosedLips = getNumSubstrings(name, ">"); + + if (numOpenLips > 0 && numClosedLips > 0 && numOpenLips == numClosedLips) { + return true; + } + return false; + } + + private boolean containsSimpleTemplate(String name) { + + int indexOf = name.indexOf(","); + if (indexOf == -1) { + return true; + } + return false; + } + + /** + * Method to return the number of the given substrings contained in the given string + * @param string the given string + * @param substring the given substring + * @return the number of the given substrings in the given string + */ + private int getNumSubstrings(String string, String substring) { + + int num = 0; + + int indexOf = string.indexOf(substring); + while (indexOf >= 0) { + num++; + string = string.substring(indexOf + 1); + indexOf = string.indexOf(substring); + } + return num; + } + + /** + * Method to generate unique shorted names for classes with templates + * @param recoveredClasses the list of classes in the program + * @throws CancelledException if cancelled + */ + public void createShortenedTemplateNamesForClasses(List recoveredClasses) + throws CancelledException { + + List classesWithTemplates = new ArrayList(); + + // create list with only classes that have templates in name and add completely stripped + // template name to class var + Iterator recoveredClassesIterator = recoveredClasses.iterator(); + while (recoveredClassesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassesIterator.next(); + + String className = recoveredClass.getName(); + + if (containsTemplate(className)) { + String shortenedName = removeTemplate(className) + "<...>"; + recoveredClass.addShortenedTemplatedName(shortenedName); + classesWithTemplates.add(recoveredClass); + } + } + + // iterate over map and remove entries that already have unique shortened names on map and + // add those unique names to class as shorted name + // for those with non-unique names, process them as a group of matched names + List classesToProcess = new ArrayList(classesWithTemplates); + + Iterator classWithTemplatesIterator = classesWithTemplates.iterator(); + + while (classWithTemplatesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass currentClass = classWithTemplatesIterator.next(); + + // skip if already processed + if (!classesToProcess.contains(currentClass)) { + continue; + } + + String currentShortenedName = currentClass.getShortenedTemplateName(); + + // if removing the middle of template results in unique name then keep that name and + // remove class from list to process + List classesWithSameShortenedName = + getClassesWithSameShortenedName(classesToProcess, currentShortenedName); + + if (classesWithSameShortenedName.size() == 1) { + classesToProcess.remove(currentClass); + continue; + } + + Iterator classesWithSameShortnameIterator = + classesWithSameShortenedName.iterator(); + while (classesWithSameShortnameIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass classWithSameShortName = classesWithSameShortnameIterator.next(); + + // if removing the middle of template results in duplicate names then + // check for a simple internal to the template (ie just one item in it + // if that is the case then just keep the original name + if (containsSimpleTemplate(classWithSameShortName.getName())) { + classWithSameShortName.addShortenedTemplatedName(new String()); + classesWithSameShortnameIterator.remove(); + classesToProcess.remove(classWithSameShortName); + } + } + + // if none left after removing simple ones then continue processing next class + if (classesWithSameShortenedName.isEmpty()) { + continue; + } + + // if only one left after removing the simple templates then use it with first part of + // template internal + if (classesWithSameShortenedName.size() == 1) { + RecoveredClass classWithSameShortName = classesWithSameShortenedName.get(0); + String newName = getNewShortenedTemplateName(classWithSameShortName, 1); + classWithSameShortName.addShortenedTemplatedName(newName); + classesToProcess.remove(classWithSameShortName); + continue; + } + + // if more than one complex left over after all the removals above, then keep trying to + // get unique name + int commaIndex = 1; + while (!classesWithSameShortenedName.isEmpty()) { + + List leftoversWithSameShortName = + new ArrayList(classesWithSameShortenedName); + + // update all their shorted names to include up to the n'th comma + Iterator leftoversIterator = leftoversWithSameShortName.iterator(); + + while (leftoversIterator.hasNext()) { + + monitor.checkCanceled(); + RecoveredClass currentClassWithSameShortName = leftoversIterator.next(); + currentClassWithSameShortName.addShortenedTemplatedName( + getNewShortenedTemplateName(currentClassWithSameShortName, commaIndex)); + } + + // now iterate and see if any are unique and if so remove from list + // if not, add up to next comma and so on until all are unique + List leftovers2WithSameShortName = + new ArrayList(classesWithSameShortenedName); + Iterator leftovers2Iterator = + leftovers2WithSameShortName.iterator(); + + while (leftovers2Iterator.hasNext()) { + + monitor.checkCanceled(); + RecoveredClass currentClassWithSameShortName = leftovers2Iterator.next(); + + String shortenedTemplateName = + currentClassWithSameShortName.getShortenedTemplateName(); + + List classesWithSameShortName = + getClassesWithSameShortenedName(classesToProcess, shortenedTemplateName); + + if (classesWithSameShortName.size() == 1) { + classesToProcess.remove(classesWithSameShortName.get(0)); + classesWithSameShortenedName.remove(classesWithSameShortName.get(0)); + } + } + + commaIndex++; + } + + } + + } + + /** + * Method to remove the template part of the given label name + * @param name the name of label + * @return the label name with template parts removed + */ + public String removeTemplate(String name) { + int indexOfOpenTemplate = name.indexOf('<'); + String nameWithoutTemplates = name; + if (indexOfOpenTemplate >= 0) { + int indexOfCloseTemplate = name.lastIndexOf('>'); + if (indexOfCloseTemplate >= 0) { + nameWithoutTemplates = name.substring(0, indexOfOpenTemplate) + + name.substring(indexOfCloseTemplate, name.length() - 1); + } + } + return nameWithoutTemplates; + } + + public String getNewShortenedTemplateName(RecoveredClass recoveredClass, int commaIndex) { + + String className = recoveredClass.getName(); + + int lastComma = 0; + int nextComma = 0; + while (commaIndex > 0) { + nextComma = className.indexOf(",", lastComma); + // if it gets to the end before the given commaIndex then we can't shorten + // return whole thing + if (nextComma == -1) { + return recoveredClass.getName(); + } + lastComma = nextComma + 1; + commaIndex--; + } + + String shortenedName = className.substring(0, nextComma) + " ...>"; + return shortenedName; + } + + public List getClassesWithSameShortenedName( + List templateClasses, String shortenedName) throws CancelledException { + + List classesWithSameShortenedName = new ArrayList(); + + Iterator classIterator = templateClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass currentClass = classIterator.next(); + + if (currentClass.getShortenedTemplateName().equals(shortenedName)) { + classesWithSameShortenedName.add(currentClass); + } + } + return classesWithSameShortenedName; + + } + + /** + * Returns a hex string representation of the integer. + * + * @param i the integer + * @param zeropad true if the value should be zero padded + * @param header true if "0x" should be prepended + * @return the hex formatted string + */ + // taken from GhidraScript + public String toHexString(int i, boolean zeropad, boolean header) { + String s = Integer.toHexString(i); + if (zeropad) { + s = zeropad(s, 8); + } + return (header ? "0x" : "") + s; + } + + // taken from GhidraScript + private static String zeropad(String s, int len) { + if (s == null) { + s = ""; + } + StringBuffer buffer = new StringBuffer(s); + int zerosNeeded = len - s.length(); + for (int i = 0; i < zerosNeeded; ++i) { + buffer.insert(0, '0'); + } + return buffer.toString(); + } + + +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RTTIClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/RTTIClassRecoverer.java new file mode 100644 index 0000000000..35c3779ef5 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RTTIClassRecoverer.java @@ -0,0 +1,292 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; + +import ghidra.app.util.NamespaceUtils; +import ghidra.framework.options.Options; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.Namespace; +import ghidra.program.model.symbol.SymbolType; +import ghidra.program.util.ProgramLocation; +import ghidra.util.Msg; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +public class RTTIClassRecoverer extends RecoveredClassUtils { + + boolean programHasRTTIApplied = false; + + String ghidraVersion; + Program program; + TaskMonitor monitor; + + RTTIClassRecoverer(Program program, ProgramLocation location, PluginTool tool, + FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, + boolean nameVfunctions, + TaskMonitor monitor) { + + super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, + monitor); + + this.program = program; + this.monitor = monitor; + this.location = location; + this.tool = tool; + this.api = api; + this.createBookmarks = createBookmarks; + this.useShortTemplates = useShortTemplates; + this.nameVfunctions = nameVfunctions; + + ghidraVersion = getVersionOfGhidra(); + } + + + public DecompilerScriptUtils getDecompilerUtils() { + return decompilerUtils; + } + + public int getDefaultPointerSize() { + return defaultPointerSize; + } + + public DataTypeManager getDataTypeManager() { + return dataTypeManager; + } + + public boolean containsRTTI() throws CancelledException { + return true; + } + + public boolean isValidProgramType() { + return true; + } + + public boolean isValidProgramSize() { + + if (defaultPointerSize != 4 && defaultPointerSize != 8) { + return false; + } + return true; + } + + /** + * Get the version of Ghidra that was used to analyze this program + * @return a string containing the version number of Ghidra used to analyze the current program + */ + public String getVersionOfGhidra() { + + Options options = program.getOptions("Program Information"); + Object ghidraVersionObject = options.getObject("Created With Ghidra Version", null); + + return ghidraVersionObject.toString(); + } + + + + public void fixUpProgram() { + return; + } + + + public List createRecoveredClasses() { + + return new ArrayList(); + } + + + + + /** + * Method to promote the namespace is a class namespace. + * @param vftableNamespace the namespace for the vftable + * @return true if namespace is (now) a class namespace or false if it could not be promoted. + */ + public Namespace promoteToClassNamespace(Namespace vftableNamespace) { + + try { + Namespace newClass = NamespaceUtils.convertNamespaceToClass(vftableNamespace); + + SymbolType symbolType = newClass.getSymbol().getSymbolType(); + if (symbolType == SymbolType.CLASS) { + return newClass; + } + Msg.debug(this, + "Could not promote " + vftableNamespace.getName() + " to a class namespace"); + return null; + } + catch (InvalidInputException e) { + + Msg.debug(this, "Could not promote " + vftableNamespace.getName() + + " to a class namespace because " + e.getMessage()); + return null; + } + } + + + /** + * Method to iterate over all the RecoveredClass objects and see if there is an existing class structure data type already + * if so, add it to the RecoveredClass object + * @param recoveredClasses List of RecoveredClass objects + * @throws CancelledException when cancelled + */ + public void retrieveExistingClassStructures(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + // if class is non-virtual have to search for an existing class datatype + if (!recoveredClass.hasVftable()) { + DataType[] possibleExistingClassStructures = + extraUtils.getDataTypes(recoveredClass.getName()); + if (possibleExistingClassStructures.length == 0) { + continue; + } + for (int i = 0; i < possibleExistingClassStructures.length; i++) { + monitor.checkCanceled(); + if (!(possibleExistingClassStructures[i] instanceof Structure)) { + continue; + } + if (possibleExistingClassStructures[i].isNotYetDefined()) { + continue; + } + + Structure existingClassStructure = + (Structure) possibleExistingClassStructures[i]; + + recoveredClass.addExistingClassStructure(existingClassStructure); + break; + } + } + //Iterate over constructor/destructor functions + List constructorOrDestructorFunctions = + recoveredClass.getConstructorOrDestructorFunctions(); + Iterator constDestIterator = constructorOrDestructorFunctions.iterator(); + while (constDestIterator.hasNext()) { + monitor.checkCanceled(); + Function constDestFunction = constDestIterator.next(); + Namespace parentNamespace = constDestFunction.getParentNamespace(); + if (!parentNamespace.equals(recoveredClass.getClassNamespace())) { + continue; + } + + if (recoveredClass.hasExistingClassStructure()) { + continue; + } + + int parameterCount = constDestFunction.getParameterCount(); + + if (parameterCount == 0) { + continue; + } + + DataType dataType = constDestFunction.getParameter(0).getDataType(); + + CategoryPath dataTypePath = dataType.getDataTypePath().getCategoryPath(); + + if (!(dataType instanceof Pointer)) { + continue; + } + + String dataTypeName = dataType.getName(); + dataTypeName = dataTypeName.replace(" *", ""); + + if (!dataTypeName.equals(recoveredClass.getName())) { + continue; + } + + Structure existingClassStructure = + (Structure) dataTypeManager.getDataType(dataTypePath, dataTypeName); + + if (!existingClassStructure.isNotYetDefined()) { + recoveredClass.addExistingClassStructure(existingClassStructure); + break; + } + + } + } + } + + + + /** + * Method to get class data information from destructors if a class has no constructors + * @param recoveredClasses list of classes + * @throws CancelledException if cancelled + * @throws InvalidInputException if issues setting function return + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public void figureOutClassDataMembers(List recoveredClasses) + throws CancelledException, DuplicateNameException, InvalidInputException, + CircularDependencyException { + + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + // we can only figure out structure info for functions with vftable since that is + // what we use to determine which variable is being used to store the class structure + if (!recoveredClass.hasVftable()) { + continue; + } + + // if the class already has an existing class structure from pdb then no need to process + if (recoveredClass.hasExistingClassStructure()) { + continue; + } + + List memberFunctionsToProcess = new ArrayList(); + + memberFunctionsToProcess.addAll(recoveredClass.getConstructorList()); + memberFunctionsToProcess.addAll(recoveredClass.getDestructorList()); + memberFunctionsToProcess.addAll(recoveredClass.getIndeterminateList()); + + memberFunctionsToProcess.addAll(recoveredClass.getInlinedConstructorList()); + + Iterator memberFunctionIterator = memberFunctionsToProcess.iterator(); + while (memberFunctionIterator.hasNext()) { + monitor.checkCanceled(); + Function memberFunction = memberFunctionIterator.next(); + + if (getVftableReferences(memberFunction) == null) { + continue; + } + + // skip if other classes contain this function as an inline inlined destructor or + // inlined indeterminate + if (isInlineDestructorOrIndeterminateInAnyClass(memberFunction)) { + continue; + } + + gatherClassMemberDataInfoForFunction(recoveredClass, memberFunction); + + } + } + } + + + + + + +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/RTTIGccClassRecoverer.java new file mode 100644 index 0000000000..bac999e471 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RTTIGccClassRecoverer.java @@ -0,0 +1,1820 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; +import java.util.stream.Collectors; + +import ghidra.app.cmd.label.DemanglerCmd; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.symbol.*; +import ghidra.program.util.ProgramLocation; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; +import ghidra.util.task.TaskMonitor; + +public class RTTIGccClassRecoverer extends RTTIClassRecoverer { + + private static final String VTABLE_LABEL = "vtable"; + + Map classToTypeinfoMap = new HashMap(); + Address class_type_info_vtable = null; + Address si_class_type_info_vtable = null; + Address vmi_class_type_info_vtable = null; + Address class_type_info = null; + Address si_class_type_info = null; + Address vmi_class_type_info = null; + + List nonInheritedGccClasses = new ArrayList(); + List singleInheritedGccClasses = new ArrayList(); + List multiInheritedGccClasses = new ArrayList(); + + RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool, + FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, + boolean nameVfunctions, + TaskMonitor monitor) { + + super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, + monitor); + } + + @Override + public boolean containsRTTI() throws CancelledException { + + if (!hasSpecialVtable()) { + return false; + } + + return true; + } + + @Override + public boolean isValidProgramType() { + if (!isGcc()) { + return false; + } + return true; + } + + @Override + public List createRecoveredClasses() { + + + try { + + List recoveredClasses = processGccRTTI(); + if (recoveredClasses == null) { + Msg.debug(this, "Could not recover gcc rtti classes"); + return null; + } + + createCalledFunctionMap(recoveredClasses); + + createClassHierarchyListAndMapForGcc(recoveredClasses); + + assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); + + createVftableOrderMap(recoveredClasses); + + retrieveExistingClassStructures(recoveredClasses); + + figureOutClassDataMembers(recoveredClasses); + + return recoveredClasses; + } + catch (CancelledException e) { + e.printStackTrace(); + return null; + } + catch (Exception e) { + e.printStackTrace(); + return null; + } + + + + } + + private boolean isGcc() { + + boolean isGcc = + program.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase("gcc"); + return isGcc; + } + + /** + * Method to check for at least one special RTTI vtable + * @return + * @throws CancelledException + */ + private boolean hasSpecialVtable() throws CancelledException { + + boolean hasSpecialVtable = createSpecialVtables(); + return hasSpecialVtable; + +// class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info"); +// si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info"); +// vmi_class_type_info_vtable = +// findSpecialVtable("__cxxabiv1", "__vmi_class_type_info"); +// if (class_type_info_vtable == null && si_class_type_info_vtable == null && +// vmi_class_type_info_vtable == null) { +// +// return false; +// } +// return true; + } + + private Address findSpecialVtable(String namespace, String name) throws CancelledException { + + Address vtableAddress = null; + + Symbol symbolInNamespaces = getSymbolInNamespaces(namespace, name, VTABLE_LABEL); + + if (symbolInNamespaces != null) { + if (!symbolInNamespaces.isPrimary()) { + symbolInNamespaces.setPrimary(); + } + vtableAddress = symbolInNamespaces.getAddress(); + + return vtableAddress; + } + + // if there is just one address that has symbols containing both strings then it suggests + // mangled symbol since the above didn't find it + Address addressContainingBothStrings = + getSingleAddressOfSymbolContainingBothStrings(namespace, name); + if (addressContainingBothStrings == null) { + return null; + } + + // try demangling all the symbols at this address + Symbol[] vtableSymbols = symbolTable.getSymbols(addressContainingBothStrings); + for (Symbol vtableSymbol : vtableSymbols) { + DemanglerCmd cmd = + new DemanglerCmd(addressContainingBothStrings, vtableSymbol.getName()); + cmd.applyTo(program, monitor); + + } + + // now check again to see if we can find the namespace/name + symbolInNamespaces = getSymbolInNamespaces(namespace, name, VTABLE_LABEL); + + if (symbolInNamespaces != null) { + if (!symbolInNamespaces.isPrimary()) { + symbolInNamespaces.setPrimary(); + } + vtableAddress = symbolInNamespaces.getAddress(); + + return vtableAddress; + } + + return null; + + } + + private List processGccRTTI() throws CancelledException, Exception { + + // attempt to find the three special typeinfo structs and vtables +// boolean continueProcessing = findSpecialTypeinfosAndVtables(); +// if (!continueProcessing) { +// return null; +// } + // create rtti vtables and typeinfo structs + // find the three special vtables and replace the incorrectly made array with + // data types found in vtable + //TODO: *****change to not be a script - make another class and call from here and from script + //runScript("GccRttiAnalysisScript.java"); + createGccRttiData(); + + + // find all typeinfo symbols and get their class namespace and create RecoveredClass object + List typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet( + program.getAddressFactory().getAddressSet(), "typeinfo", true); + + // create class objects for each typeinfo struct and make a class to typeinfo mapping for each + List nonVftableRecoveredClasses = + createClassesFromTypeinfoSymbols(typeinfoSymbols); + + // process vtables and create classes for the vtables that have no typeinfo + List vftableSymbols = findVftablesFromVtables(); + Msg.debug(this, "Found " + vftableSymbols.size() + "vftableSymbols"); + + List recoveredClasses = + recoverClassesFromVftables(vftableSymbols, true, true); + + if (!nonVftableRecoveredClasses.isEmpty()) { + for (RecoveredClass nonVClass : nonVftableRecoveredClasses) { + monitor.checkCanceled(); + if (!recoveredClasses.contains(nonVClass)) { + recoveredClasses.add(nonVClass); + } + } + } + + // TODO: are all recovered classes in the map? + + // update the recoveredClass list with the typeinfo classes that do not have vtables + Set typeinfoClasses = classToTypeinfoMap.keySet(); + Iterator typeinfoClassIterator = typeinfoClasses.iterator(); + while (typeinfoClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass typeinfoClass = typeinfoClassIterator.next(); + if (!recoveredClasses.contains(typeinfoClass)) { + recoveredClasses.add(typeinfoClass); + } + } + + // add properties and parents to each class + Msg.debug(this, "Found " + typeinfoSymbols.size() + " typeinfo symbols"); + Iterator typeinfoIterator = typeinfoSymbols.iterator(); + while (typeinfoIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol typeinfoSymbol = typeinfoIterator.next(); + Address typeinfoAddress = typeinfoSymbol.getAddress(); + + // skip the typeinfo symbols from the three special typeinfos + if (typeinfoAddress.equals(class_type_info) || + typeinfoAddress.equals(si_class_type_info) || + typeinfoAddress.equals(vmi_class_type_info)) { + continue; + } + + Namespace classNamespace = typeinfoSymbol.getParentNamespace(); + + RecoveredClass recoveredClass = getClass(classNamespace); + + // we don't know yet if this class has vftable so just add without for now + if (recoveredClass == null) { + // this shoudln't be null at this point + Msg.debug(this, "Shouldn't be a null class here: " + classNamespace.getName()); + recoveredClass = addNoVftableClass(classNamespace); + recoveredClasses.add(recoveredClass); + } + + Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); + if (specialTypeinfoRef == null) { + Msg.debug(this, + "No special typeinfo reference found. Cannot process typeinfo struct at " + + typeinfoAddress.toString()); + continue; + } + + if (!isSpecialTypeinfo(specialTypeinfoRef)) { + Msg.debug(this, + "Special typeinfo reference is not equal to one of the three special type infos. Cannot process typeinfo struct at " + + typeinfoAddress.toString()); + continue; + } + + if (specialTypeinfoRef.equals(class_type_info)) { + nonInheritedGccClasses.add(recoveredClass); + continue; + } + + // per docs those on this list are + // classes containing only a single, public, non-virtual base at offset zero + if (specialTypeinfoRef.equals(si_class_type_info)) { + singleInheritedGccClasses.add(recoveredClass); + + RecoveredClass parentClass = getSiClassParent(typeinfoAddress); + if (parentClass != null) { + Msg.debug(this, + recoveredClass.getName() + " adding parent " + parentClass.getName()); + updateClassWithParent(parentClass, recoveredClass); + + if (!recoveredClasses.contains(parentClass)) { + Msg.debug(this, recoveredClass.getName() + + " adding parent I didn't know about" + parentClass.getName()); + recoveredClasses.add(parentClass); + } + + } + else { + Msg.debug(this, "Could not get si parent from typeinfoAddress " + + typeinfoAddress.toString()); + } + + continue; + } + + if (specialTypeinfoRef.equals(vmi_class_type_info)) { + + multiInheritedGccClasses.add(recoveredClass); + + recoveredClass.setHasMultipleInheritance(true); + recoveredClass.setHasParentClass(true); + + // TODO: get info out of the flags in the typeinfo for this class +// if (recoveredClass.inheritsVirtualAncestor()) { +// recoveredClass.setHasMultipleVirtualInheritance(true); +// } + + List parents = addGccClassParents(recoveredClass, typeinfoAddress); + if (!recoveredClasses.containsAll(parents)) { + Msg.debug(this, + "missing some parents from " + recoveredClass.getName() + " on list"); + } +// newNonVftableClasses = +// addMissingClasses(parents, newNonVftableClasses, recoveredClasses); + } + } + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + Msg.debug(this, "Processing class " + recoveredClass.getName()); + + List
vftableAddresses = recoveredClass.getVftableAddresses(); + if (vftableAddresses.isEmpty()) { + Msg.debug(this, recoveredClass.getName() + " has no vftable"); + } + + Iterator
vftableAddressIterator = vftableAddresses.iterator(); + while (vftableAddressIterator.hasNext()) { + monitor.checkCanceled(); + Address vftableAddress = vftableAddressIterator.next(); + + Address offsetAddress = vftableAddress.subtract(2 * defaultPointerSize); + int offsetValue = (int) api.getLong(offsetAddress); + recoveredClass.addClassOffsetToVftableMapping(offsetValue, vftableAddress); + } + + } + + Msg.debug(this, "Gets to create called function map."); + createCalledFunctionMap(recoveredClasses); + Msg.debug(this, "Gets to assign c/ds."); + assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); + + return recoveredClasses; + + } + + private void createGccRttiData() throws CancelledException, Exception { + + // find the three special vtables and replace the incorrectly made array with + // data types found in vtable + //createSpecialVtables(); + + // find all typeinfo symbols and get their class namespace and create RecoveredClass object + List typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet( + program.getAddressFactory().getAddressSet(), "typeinfo", true); + + // create the appropriate type of type info struct at the various typeinfo symbol locations + createTypeinfoStructs(typeinfoSymbols); + + // process vtables and create classes for the vtables that have no typeinfo + processVtables(); + } + + /** + * Method to process the primary vtable for each "vtable" label + * @return the vftable Address in the vtable + * @throws Exception if Data cannot be created + */ + private void processVtables() throws Exception { + + // find all vtable symbols + List listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet( + program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); + + Iterator vtableIterator = listOfVtableSymbols.iterator(); + while (vtableIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol vtableSymbol = vtableIterator.next(); + Namespace vtableNamespace = vtableSymbol.getParentNamespace(); + Address vtableAddress = vtableSymbol.getAddress(); + + processVtable(vtableAddress, vtableNamespace, true); + + } + return; + } + + private void processVtable(Address vtableAddress, Namespace vtableNamespace, boolean isPrimary) + throws CancelledException { + + // skip the special tables + if (vtableAddress.equals(class_type_info_vtable) || + vtableAddress.equals(si_class_type_info_vtable) || + vtableAddress.equals(vmi_class_type_info_vtable)) { + return; + } + + Data dataAt = api.getDataAt(vtableAddress); + + // first check to see it is an erroneous vtable that has been made a byte array + // if so, clear it and start looking for the typeinfo reference + if (dataAt != null && dataAt.isArray()) { + api.clearListing(vtableAddress); + + } + if (dataAt != null && !dataAt.getDataType().getName().equals("long")) { + api.clearListing(vtableAddress); + } + + // find the special type info ref + Address typeinfoAddress = findNextTypeinfoRef(vtableAddress); + if (typeinfoAddress == null) { + Msg.debug(this, vtableNamespace.getName() + + " vtable has no typeinfo ref after vtable at " + vtableAddress.toString()); + return; + } + + // create the typeinfo pointer if there isn't already one + Data typeinfoPtr = api.getDataAt(typeinfoAddress); + if (typeinfoPtr == null) { + DataType nullPointer = dataTypeManager.getPointer(null); + try { + api.createData(typeinfoAddress, nullPointer); + } + catch (Exception e) { + Msg.debug(this, + "Could not create typeinfo pointer at " + typeinfoAddress.toString()); + } + } + + // create longs from top of vtable to the typeinfoAddress + createLongs(vtableAddress, typeinfoAddress); + + Address vftableAddress = getAddress(typeinfoAddress, defaultPointerSize); + + if (vftableAddress == null) { + return; + } + + int numFunctionPointers = getNumFunctionPointers(vftableAddress, true, true); + + // if at least one function pointer make vftable label - the createVftable method will + // create the table later + if (numFunctionPointers > 0) { + + String vftableLabel = VFTABLE_LABEL; + if (!isPrimary) { + vftableLabel = "internal_" + vftableLabel; + } + + try { + Symbol vftableSymbol = symbolTable.createLabel(vftableAddress, vftableLabel, + vtableNamespace, SourceType.ANALYSIS); + + createVftableArray(vftableAddress, numFunctionPointers); + } + catch (IllegalArgumentException e) { + Msg.debug(this, "Could not label vftable at " + vftableAddress.toString()); + + } + catch (InvalidInputException e) { + Msg.debug(this, "Could not label vftable at " + vftableAddress.toString()); + + } + catch (CancelledException e) { + return; + } + catch (AddressOutOfBoundsException e) { + Msg.debug(this, "Couldn't create vftable due to Address out of bounds issue"); + return; + } + } + + // check for an internal vtable and make a symbol there if there is one + // will process them later + Address possibleInternalVtableAddress = + getAddress(vftableAddress, defaultPointerSize * numFunctionPointers); + // if there is no symbol or a non-default symbol then the nextAddress is an internal + // vtable + if (possibleInternalVtableAddress == null) { + return; + } + Symbol possibleInternalVtableSymbol = + symbolTable.getPrimarySymbol(possibleInternalVtableAddress); + if (possibleInternalVtableSymbol != null && + possibleInternalVtableSymbol.getSource() != SourceType.DEFAULT && + (!possibleInternalVtableSymbol.getParentNamespace().equals(vtableNamespace) || + !possibleInternalVtableSymbol.getName().contains("vtable"))) { + return; + } + + if (possibleInternalVtableSymbol == null || + (possibleInternalVtableSymbol.getSource() == SourceType.DEFAULT && + (isValidVtableStart(possibleInternalVtableAddress) || + isValidVftableStart(possibleInternalVtableAddress)))) { + try { + symbolTable.createLabel(possibleInternalVtableAddress, + "internal_vtable_" + possibleInternalVtableAddress.toString(), vtableNamespace, + SourceType.ANALYSIS); + processVtable(possibleInternalVtableAddress, vtableNamespace, false); + } + catch (IllegalArgumentException e) { + Msg.debug(this, "Could not label internal vtable at " + + possibleInternalVtableAddress.toString()); + } + catch (InvalidInputException e) { + Msg.debug(this, "Could not label internal vtable at " + + possibleInternalVtableAddress.toString()); + } + + } + + } + + private Data createVftableArray(Address vftableAddress, int numFunctionPointers) + throws CancelledException, AddressOutOfBoundsException { + + api.clearListing(vftableAddress, + vftableAddress.add((numFunctionPointers * defaultPointerSize - 1))); + + DataType pointerDataType = dataTypeManager.getPointer(null); + ArrayDataType vftableArrayDataType = + new ArrayDataType(pointerDataType, numFunctionPointers, defaultPointerSize); + try { + Data vftableArrayData = api.createData(vftableAddress, vftableArrayDataType); + return vftableArrayData; + } + catch (Exception e) { + return null; + } + + } + + /** + * Method to check for a valid vtable at the given address + * @param vtableAddress the given address + * @return true if there is a valid vtable at the given address, false otherwise + */ + private boolean isValidVtableStart(Address vtableAddress) { + + // check that no refs into the first 2*defaultptr bytes + // skip top of table since that will have references to it + Address address = getAddress(vtableAddress, 1); + if (address == null) { + return false; + } + if (!areNoReferencesInto(address, 2 * defaultPointerSize - 1)) { + return false; + } + + // check that no pointers + if (!areNoReferencesFrom(vtableAddress, 2 * defaultPointerSize)) { + return false; + } + + // check that no other data exept possibly longs at correct offsets + if (!isNoDataCreatedExceptMaybeLongs(vtableAddress, 2 * defaultPointerSize)) { + return false; + } + + // TODO: maybe print a warning if the first item is not all zeros bc usually they are -- but pass + // it even then + + return true; + } + + private boolean areNoReferencesInto(Address topAddress, int length) { + + int offset = 0; + + MemoryBlock currentMemoryBlock = program.getMemory().getBlock(topAddress); + + while (offset < length) { + + Address address = getAddress(topAddress, offset); + + if (address == null) { + return false; + } + + if (!currentMemoryBlock.contains(address)) { + return false; + } + + Reference[] referencesTo = extraUtils.getReferencesTo(address); + if (referencesTo.length > 0) { + return false; + } + + offset++; + + } + return true; + } + + private boolean areNoReferencesFrom(Address topAddress, int length) { + + int offset = 0; + + MemoryBlock currentMemoryBlock = program.getMemory().getBlock(topAddress); + + while (offset < length) { + + Address address = getAddress(topAddress, offset); + + if (address == null) { + return false; + } + + if (!currentMemoryBlock.contains(address)) { + return false; + } + + List
referenceFromAddresses = extraUtils.getReferenceFromAddresses(address); + + if (referenceFromAddresses.size() > 0) { + return false; + } + + offset++; + + } + + return true; + + } + + private boolean isNoDataCreatedExceptMaybeLongs(Address startAddress, int length) { + + int offset = 0; + + MemoryBlock currentMemoryBlock = program.getMemory().getBlock(startAddress); + + while (offset < length) { + + Address address = getAddress(startAddress, offset); + + if (address == null) { + return false; + } + + if (!currentMemoryBlock.contains(address)) { + return false; + } + + Data data = api.getDataAt(address); + + // if there is data and it isn't on a pointer size boundary then return null + // if there is data and it is on a pointer size boundary but isn't a long then + // return null + // otherwise, continue + if (data != null) { + if (offset % defaultPointerSize == 0 && + data.getBaseDataType().getName().equals("long")) { + offset += defaultPointerSize; + continue; + } + return false; + } + offset++; + } + + return true; + + } + + private boolean isValidVftableStart(Address vftableAddress) throws CancelledException { + + // no refs into first defaaultPointerSize bytes + Address address = getAddress(vftableAddress, 1); + if (address == null) { + return false; + } + + if (!areNoReferencesInto(address, defaultPointerSize - 1)) { + return false; + } + + if (extraUtils.hasNumZeros(vftableAddress, defaultPointerSize)) { + return true; + } + + Data data = api.getDataAt(vftableAddress); + if (data != null) { + if (!data.isPointer()) { + return false; + } + Address referencedAddress = extraUtils.getSingleReferencedAddress(vftableAddress); + if (referencedAddress == null) { + return false; + } + Function functionAt = api.getFunctionAt(referencedAddress); + if (functionAt != null) { + return true; + } + } + else { + try { + Long longValue = api.getLong(address); + Address functionAddress = address.getNewAddress(longValue); + Function functionAt = api.getFunctionAt(functionAddress); + if (functionAt != null) { + return true; + } + } + catch (MemoryAccessException e) { + return false; + } + catch (AddressOutOfBoundsException e) { + return false; + } + + } + + return false; + } + + + /** + * Method to find the (up to three) special gcc vtables and replace the incorrectly made array with the + * correct data types. Also creates a type info symbol at the correct offset in the table. + * @throws CancelledException if cancelled + */ + private void createSpecialVtablesOld() throws CancelledException { + + if (class_type_info_vtable != null) { + class_type_info = createSpecialVtable(class_type_info_vtable); + } + + if (si_class_type_info_vtable != null) { + si_class_type_info = createSpecialVtable(si_class_type_info_vtable); + } + + if (vmi_class_type_info_vtable != null) { + vmi_class_type_info = createSpecialVtable(vmi_class_type_info_vtable); + } + + return; + } + + /** + * Method to replace the array incorrectly placed at special vftable with longs followed by + * typeinfo label + * @param vtableAddress the given special vtable address + * @return the address of the typeinfo in the vtable if replace was successful, null otherwise + * @throws CancelledException if cancelled + */ + private Address createSpecialVtable(Address vtableAddress) throws CancelledException { + + Symbol vtableSymbol = symbolTable.getPrimarySymbol(vtableAddress); + + api.clearListing(vtableAddress); + try { + int vtableLongs = createVtableLongs(vtableAddress); + + if (vtableLongs > 0) { + + Address typeinfoAddress = vtableAddress.add(vtableLongs * defaultPointerSize); + symbolTable.createLabel(typeinfoAddress, "typeinfo", + vtableSymbol.getParentNamespace(), SourceType.ANALYSIS); + return typeinfoAddress; + } + return null; + } + + catch (AddressOutOfBoundsException e) { + return null; + } + catch (IllegalArgumentException e) { + return null; + } + catch (InvalidInputException e) { + return null; + } + + } + + /** + * Method to create long data type at the given vtable address and return the number created OR + * if they are already created, just return how many there are + * @param vtableAddress the address of the given vtable + * @return the number of long data types at vtableAddress + */ + private int createVtableLongs(Address vtableAddress) { + + AddressSetView programAddressSet = program.getMemory().getAllInitializedAddressSet(); + DataType pointer = dataTypeManager.getPointer(null); + LongDataType longDT = new LongDataType(); + + int offset = 0; + int numLongs = 0; + while (true) { + + Address address = vtableAddress.add(offset); + + // Except for the first one which should have a symbol, if there is a symbol at the + // address, stop making longs because it there are no references into the vtable longs + if (offset > 0 && symbolTable.getSymbols(address).length > 0) { + return numLongs; + } + + // create a pointer and check to see if it is a reference to a valid memory location + try { + api.createData(address, pointer); + Address referencedAddress = extraUtils.getSingleReferencedAddress(address); + + // if it isn't valid, clear what we just created and increment to offset so + // the next can be checked + if (referencedAddress == null || !programAddressSet.contains(referencedAddress)) { + api.clearListing(address); + api.createData(address, longDT); + offset += defaultPointerSize; + numLongs++; + } + // if it is valid, leave the pointer created and get out of the loop + else { + return numLongs; + } + } + catch (Exception e) { + return numLongs; + } + + } + } + + private void createTypeinfoStructs(List typeinfoSymbols) throws CancelledException { + + StructureDataType classTypeInfoStructure = createClassTypeInfoStructure(); + StructureDataType siClassTypeInfoStructure = + createSiClassTypeInfoStructure(classTypeInfoStructure); + StructureDataType baseClassTypeInfoStructure = + createBaseClassTypeInfoStructure(classTypeInfoStructure); + + Iterator typeinfoIterator = typeinfoSymbols.iterator(); + while (typeinfoIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol typeinfoSymbol = typeinfoIterator.next(); + Address typeinfoAddress = typeinfoSymbol.getAddress(); + + // skip the typeinfo symbols from the three special typeinfos + if (isSpecialTypeinfo(typeinfoAddress)) { + continue; + } + + Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); + if (specialTypeinfoRef == null) { + Msg.debug(this, + "No special typeinfo reference found. Cannot process typeinfo struct at " + + typeinfoAddress.toString()); + continue; + } + + if (!isSpecialTypeinfo(specialTypeinfoRef)) { + continue; + } + + try { + // create a "no inheritance" struct here + if (specialTypeinfoRef.equals(class_type_info)) { + api.clearListing(typeinfoAddress, + typeinfoAddress.add(classTypeInfoStructure.getLength())); + api.createData(typeinfoAddress, classTypeInfoStructure); + continue; + } + + // create a "single inheritance" struct here + if (specialTypeinfoRef.equals(si_class_type_info)) { + api.clearListing(typeinfoAddress, + typeinfoAddress.add(siClassTypeInfoStructure.getLength() - 1)); + api.createData(typeinfoAddress, siClassTypeInfoStructure); + continue; + } + + // create a "virtual multip inheritance" struct here + if (specialTypeinfoRef.equals(vmi_class_type_info)) { + + // get num base classes + int offsetOfNumBases = 2 * defaultPointerSize + 4; + int numBases = api.getInt(typeinfoAddress.add(offsetOfNumBases)); + + // get or create the vmiClassTypeInfoStruct + Structure vmiClassTypeinfoStructure = + (Structure) dataTypeManager.getDataType(classDataTypesCategoryPath, + "VmiClassTypeInfoStructure" + numBases); + if (vmiClassTypeinfoStructure == null) { + vmiClassTypeinfoStructure = + createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); + } + api.clearListing(typeinfoAddress, + typeinfoAddress.add(vmiClassTypeinfoStructure.getLength() - 1)); + api.createData(typeinfoAddress, vmiClassTypeinfoStructure); + + } + } + catch (Exception e) { + Msg.debug(this, "ERROR: Could not apply structure to " + typeinfoAddress); + } + } + + } + + private StructureDataType createClassTypeInfoStructure() { + + StructureDataType classTypeInfoStructure = new StructureDataType(classDataTypesCategoryPath, + "ClassTypeInfoStructure", 0, dataTypeManager); + + CharDataType characterDT = new CharDataType(); + DataType pointer = dataTypeManager.getPointer(null); + DataType charPointer = dataTypeManager.getPointer(characterDT); + classTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); + classTypeInfoStructure.add(charPointer, "typeinfoName", null); + + classTypeInfoStructure.setInternallyAligned(true); + + return classTypeInfoStructure; + } + + private StructureDataType createSiClassTypeInfoStructure( + StructureDataType classTypeInfoStructure) { + + StructureDataType siClassTypeInfoStructure = new StructureDataType( + classDataTypesCategoryPath, "SiClassTypeInfoStructure", 0, dataTypeManager); + + CharDataType characterDT = new CharDataType(); + DataType pointer = dataTypeManager.getPointer(null); + DataType charPointer = dataTypeManager.getPointer(characterDT); + //TODO: ?? replace with classTypeInfoStruct? + siClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); + siClassTypeInfoStructure.add(charPointer, "typeinfoName", null); + + DataType pointerToClassTypeInfoStruct = dataTypeManager.getPointer(classTypeInfoStructure); + siClassTypeInfoStructure.add(pointerToClassTypeInfoStruct, "baseClassTypeInfoPtr", null); + + siClassTypeInfoStructure.setInternallyAligned(true); + + return siClassTypeInfoStructure; + } + + private StructureDataType createBaseClassTypeInfoStructure( + StructureDataType classTypeInfoStructure) { + + StructureDataType baseclassTypeInfoStructure = new StructureDataType( + classDataTypesCategoryPath, "BaseClassTypeInfoStructure", 0, dataTypeManager); + + DataType classTypeInfoPointer = dataTypeManager.getPointer(classTypeInfoStructure); + + LongDataType longDT = new LongDataType(); + + baseclassTypeInfoStructure.add(classTypeInfoPointer, "classTypeinfoPtr", null); + baseclassTypeInfoStructure.add(longDT, "offsetFlags", null); + + baseclassTypeInfoStructure.setInternallyAligned(true); + + return baseclassTypeInfoStructure; + + } + + private StructureDataType createVmiClassTypeInfoStructure( + StructureDataType baseClassTypeInfoStructure, int numBaseClasses) { + + StructureDataType vmiClassTypeInfoStructure = + new StructureDataType(classDataTypesCategoryPath, + "VmiClassTypeInfoStructure" + numBaseClasses, 0, dataTypeManager); + + CharDataType characterDT = new CharDataType(); + UnsignedIntegerDataType unsignedIntDT = new UnsignedIntegerDataType(); + + DataType pointer = dataTypeManager.getPointer(null); + DataType charPointer = dataTypeManager.getPointer(characterDT); + + //TODO: ?? replace with classTypeInfoStruct? + vmiClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); + vmiClassTypeInfoStructure.add(charPointer, "typeinfoName", null); + vmiClassTypeInfoStructure.add(unsignedIntDT, "flags", null); + vmiClassTypeInfoStructure.add(unsignedIntDT, "numBaseClasses", null); + + // make array of base class type info structs + ArrayDataType baseClassArray = new ArrayDataType(baseClassTypeInfoStructure, numBaseClasses, + baseClassTypeInfoStructure.getLength()); + vmiClassTypeInfoStructure.add(baseClassArray, "baseClassPtrArray", null); + + vmiClassTypeInfoStructure.setInternallyAligned(true); + + return vmiClassTypeInfoStructure; + } + + /** + * Method to add parents to the given gcc class + * @param recoveredClass the given class + * @param typeinfoAddress the address of the typeinfo + * @return list of parents for the given class + * @throws CancelledException if cancelled + */ + private List addGccClassParents(RecoveredClass recoveredClass, + Address typeinfoAddress) throws CancelledException { + + Data typeinfoStructure = api.getDataAt(typeinfoAddress); + List parentClassList = new ArrayList(); + + // get inheritance shape info flag + // zero means just normal multi I think + // 0x01: class has non-diamond repeated inheritance - this just means multiple parents have + // the same parent + // 0x02: class is diamond shaped + + int inheritanceTypeFlagOffset = defaultPointerSize * 2; + Address inheritanceFlagAddress = + extraUtils.getAddress(typeinfoAddress, inheritanceTypeFlagOffset); + if (inheritanceFlagAddress == null) { + + //TODO: throw exception + Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() + + " plus offset " + inheritanceTypeFlagOffset); + return parentClassList; + } + + // TODO: process the inheritance flag + try { + int inheritanceFlagValue = api.getInt(inheritanceFlagAddress); + Msg.debug(this, + inheritanceFlagAddress.toString() + " inheritanceFlag = " + inheritanceFlagValue); + } + catch (MemoryAccessException e) { + Msg.debug(this, "couldn't get int at address " + inheritanceFlagAddress.toString()); + } + + int baseClassArrayOffset = defaultPointerSize * 3; + Data baseClassArrayData = typeinfoStructure.getComponentAt(baseClassArrayOffset); + + if (baseClassArrayData == null || !baseClassArrayData.isArray() || + !baseClassArrayData.getBaseDataType().getName().startsWith( + "BaseClassTypeInfoStructure")) { + + // TODO: throw exception + return parentClassList; + } + + int numParents = baseClassArrayData.getNumComponents(); + + for (int i = 0; i < numParents; i++) { + // get parent from pointer to parent typeinfo + Address parentRefAddress = + extraUtils.getAddress(typeinfoAddress, + baseClassArrayOffset + (i * 2 * defaultPointerSize)); + if (parentRefAddress == null) { + Msg.debug(this, "Could not access address " + typeinfoAddress.toString() + + " plus offset " + baseClassArrayOffset); + continue; + } + + RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentRefAddress); + + if (parentClass != null) { + Msg.debug(this, + recoveredClass.getName() + " adding vmi parent " + parentClass.getName()); + updateClassWithParent(parentClass, recoveredClass); + parentClassList.add(parentClass); + } + + // get long value flag + Address flagAddress = extraUtils.getAddress(typeinfoAddress, + baseClassArrayOffset + (i * 2 * defaultPointerSize + defaultPointerSize)); + if (flagAddress == null) { + Msg.debug(this, "Could not access address " + typeinfoAddress.toString() + + " plus offset " + baseClassArrayOffset); + continue; + } + + // from doc: + //All but the lower 8 bits of __offset_flags are a signed offset. For a + //non-virtual base, this is the offset in the object of the base subobject. + //For a virtual base, this is the offset in the virtual table of the + //virtual base offset for the virtual base referenced (negative). + + //The low-order byte of __offset_flags contains flags, as given by the masks + //from the enumeration __offset_flags_masks: + + //0x1: Base class is virtual + //0x2: Base class is public + try { + long flags = api.getLong(flagAddress); + + // TODO: process flag + // split out the offset from the virt/public flag + // offset >> 8 & 0xffffff + + } + catch (MemoryAccessException e) { + Msg.debug(this, "couldn't get long at address " + flagAddress.toString()); + } + } + + Msg.debug(this, recoveredClass.getName() + " has " + numParents + " parents"); + return parentClassList; + + } + + /** + * Get the parent class given the typeinfo address of an Si class + * @param typeinfoAddress the given Si class's typeinfo Address + * @return the parent class + */ + private RecoveredClass getSiClassParent(Address typeinfoAddress) { + + int offset = defaultPointerSize * 2; + + Address parentTypeinfoRef = extraUtils.getAddress(typeinfoAddress, offset); + if (parentTypeinfoRef == null) { + Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() + + " plus offset " + offset); + return null; + } + + RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentTypeinfoRef); + return parentClass; + + } + + /** + * Method to return the parent class given a reference to the parent class's typeinfo struct + * @param parentTypeinfoRef the given parent typeinfo reference + * @return the associated parent class + */ + private RecoveredClass getParentClassFromParentTypeInfoRef(Address parentTypeinfoRef) { + Address parentAddress = extraUtils.getSingleReferencedAddress(parentTypeinfoRef); + if (parentAddress == null) { + return null; + } + Symbol parentSymbol = symbolTable.getPrimarySymbol(parentAddress); + if (parentSymbol == null) { + return null; + } + Namespace parentNamespace = parentSymbol.getParentNamespace(); + if (parentNamespace == null) { + return null; + } + RecoveredClass parentClass = getClass(parentNamespace); + + if (parentClass == null) { + return null; + } + return parentClass; + } + + /** + * Method to find the (up to three) special gcc vtables and replace the incorrectly made array with the + * correct data types. Also creates a type info symbol at the correct offset in the table. + * @return true if all found tables have a typeinfo symbol created successfully + * @throws CancelledException if cancelled + */ + private boolean createSpecialVtables() throws CancelledException { + + class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info"); + class_type_info = null; + if (class_type_info_vtable == null) { + Msg.debug(this, "__class_type_info vtable not found --> no classes without parents"); + } + else { + class_type_info = createSpecialVtable(class_type_info_vtable); + if (class_type_info == null) { + Msg.debug(this, + "__class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + } + + si_class_type_info = null; + si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info"); + if (si_class_type_info_vtable == null) { + Msg.debug(this, "__si_class_type_info vtable not found --> no single parent classes"); + } + else { + si_class_type_info = createSpecialVtable(si_class_type_info_vtable); + if (si_class_type_info == null) { + Msg.debug(this, + "__si_class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + } + + vmi_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__vmi_class_type_info"); + vmi_class_type_info = null; + if (vmi_class_type_info_vtable == null) { + Msg.debug(this, "__vmi_class_type_info vtable not found --> no multi-parent classes"); + } + else { + vmi_class_type_info = createSpecialVtable(vmi_class_type_info_vtable); + if (vmi_class_type_info == null) { + Msg.debug(this, + "__vmi_class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + } + + if (class_type_info_vtable == null && si_class_type_info_vtable == null && + vmi_class_type_info_vtable == null) { + Msg.debug(this, + "Since there are no class typeinfo tables this program does not appear to have RTTI."); + return false; + } + return true; + } + + /** + * Method to find the (up to three) special gcc typeinfos and vtables + * @return true if all found tables have a typeinfo symbol created successfully + * @throws CancelledException if cancelled + */ + private boolean findSpecialTypeinfosAndVtables() throws CancelledException { + + class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info"); + + if (class_type_info_vtable == null) { + Msg.debug(this, + "***** __class_type_info vtable not found --> no classes without parents"); + } + else { + + Symbol class_type_info_symbol = + getSymbolInNamespaces("__cxxabiv1", "__class_type_info", "typeinfo"); + if (class_type_info_symbol == null) { + Msg.debug(this, + "__class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + class_type_info = class_type_info_symbol.getAddress(); + + } + + si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info"); + if (si_class_type_info_vtable == null) { + Msg.debug(this, "__si_class_type_info vtable not found --> no single parent classes"); + } + else { + Symbol si_class_type_info_symbol = + getSymbolInNamespaces("__cxxabiv1", "__si_class_type_info", "typeinfo"); + if (si_class_type_info_symbol == null) { + Msg.debug(this, + "__si_class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + si_class_type_info = si_class_type_info_symbol.getAddress(); + } + + vmi_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__vmi_class_type_info"); + if (vmi_class_type_info_vtable == null) { + Msg.debug(this, "__vmi_class_type_info vtable not found --> no multi-parent classes"); + } + else { + Symbol vmi_class_type_info_symbol = + getSymbolInNamespaces("__cxxabiv1", "__vmi_class_type_info", "typeinfo"); + if (vmi_class_type_info_symbol == null) { + Msg.debug(this, + "__vmi_class_type_info typeinfo not found -- cannot continue gcc rtti processing"); + return false; + } + vmi_class_type_info = vmi_class_type_info_symbol.getAddress(); + } + + if (class_type_info_vtable == null && si_class_type_info_vtable == null && + vmi_class_type_info_vtable == null) { + Msg.debug(this, + "Since there are no class typeinfo tables this program does not appear to have RTTI."); + return false; + } + return true; + + } + + // TODO: don't delete - its call is commented out waiting for more work above + private List addMissingClasses(List subset, + List newList, List classList) + throws CancelledException { + + // find classes common to the possible subset and the larger classList + List commonClasses = + classList.stream().distinct().filter(subset::contains).collect(Collectors.toList()); + + // remove any common classes (i.e. classes already on the classList) from the subset + if (!commonClasses.isEmpty()) { + subset.removeAll(commonClasses); + } + + // if subset is now empty then there are no new classes to add so just return the newList + if (subset.isEmpty()) { + return newList; + } + + // if subset has any new classes on it, add any that are not already on the newList to newList + Iterator newClassesIterator = subset.iterator(); + while (newClassesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass newClass = newClassesIterator.next(); + if (!newList.contains(newClass)) { + newList.add(newClass); + } + } + + return newList; + } + +//TODO: repurpose to find first vftable in vtable?? + private List
findVftablesInVtableUsingTypeinfoRefs(Address vtableAddress) + throws CancelledException { + + List
vftableAddresses = new ArrayList
(); + + Address address = vtableAddress; + + MemoryBlock currentMemoryBlock = program.getMemory().getBlock(vtableAddress); + + while (address != null && currentMemoryBlock.contains(address)) { + + Address nextTypeinfoRef = findNextTypeinfoRef(address); + if (nextTypeinfoRef == null) { + return vftableAddresses; + } + + address = extraUtils.getAddress(nextTypeinfoRef, defaultPointerSize); + if (extraUtils.isFunctionPointer(address, true)) { + vftableAddresses.add(address); + } + + } + return vftableAddresses; + + } + + /** + * Method to find the next reference to a typeinfo symbol after the given address + * @param startAddress the address to start looking from + * @return the address of the next typeinfo address after the given address + */ + private Address findNextTypeinfoRef(Address startAddress) { + + int offset = 0; + + Address address = extraUtils.getAddress(startAddress, offset); + + MemoryBlock currentMemoryBlock = program.getMemory().getBlock(startAddress); + + while (address != null && currentMemoryBlock.contains(address)) { + + Symbol symbol = symbolTable.getPrimarySymbol(address); + // if the symbol we find is not a default symbol + // because we have reached the end of the item we are searching + if (!address.equals(startAddress) && symbol != null && + symbol.getSource() != SourceType.DEFAULT) { + return null; + } + + Address possibleTypeinfo = extraUtils.getPointer(address); + if (possibleTypeinfo == null) { + offset += defaultPointerSize; + address = extraUtils.getAddress(startAddress, offset); + continue; + } + + Symbol possibleTypeinfoSymbol = symbolTable.getPrimarySymbol(possibleTypeinfo); + if (possibleTypeinfoSymbol != null && + possibleTypeinfoSymbol.getName().equals("typeinfo")) { + return address; + } + offset += defaultPointerSize; + address = extraUtils.getAddress(startAddress, offset); + + } + + return null; + } + + /** + * Method to process the primary vtable for each "vtable" label + * @return the vftable Address in the vtable + * @throws Exception if Data cannot be created + */ + private List findVftablesFromVtables() throws Exception { + + List vftableSymbols = new ArrayList(); + + // find all vtable symbols + List listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet( + program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); + + Iterator vtableIterator = listOfVtableSymbols.iterator(); + while (vtableIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol vtableSymbol = vtableIterator.next(); + Namespace vtableNamespace = vtableSymbol.getParentNamespace(); + Address vtableAddress = vtableSymbol.getAddress(); + + // skip the special tables + if (vtableAddress.equals(class_type_info_vtable) || + vtableAddress.equals(si_class_type_info_vtable) || + vtableAddress.equals(vmi_class_type_info_vtable)) { + continue; + } + + // find the classes that have vtable but no typeinfo structure + // we know this because all classes that have typeinfo were added to the following map + // previously + RecoveredClass classWithNoTypeinfoStruct = getClass(vtableNamespace); + if (classWithNoTypeinfoStruct == null) { + addNoVftableClass(vtableNamespace); + continue; + } + + Data vtableData = api.getDataAt(vtableAddress); + if (vtableData == null) { + continue; + } + + // find the special type info ref + Address typeinfoAddress = findNextTypeinfoRef(vtableAddress); + if (typeinfoAddress == null) { + Msg.debug(this, vtableAddress.toString() + " " + vtableNamespace.getName() + + " vtable has no typeinfo ref"); + continue; + } + + Address vftableAddress = extraUtils.getAddress(typeinfoAddress, defaultPointerSize); + // no valid address here so continue + if (vftableAddress == null) { + //TODO: print to see if any happen + // if so should also add to no vftable class + continue; + } + Symbol vftableSymbol = symbolTable.getPrimarySymbol(vftableAddress); + if (vftableSymbol == null) { + continue; + } + if (vftableSymbol.getName().equals(VFTABLE_LABEL)) { + vftableSymbols.add(vftableSymbol); + } + + } + return vftableSymbols; + } + + /** + * Method to check if given typeinfo is one of the three special ones + * @param typeinfoAddress the given typeinfo address + * @return true if it is a special one, false otherwise + */ + private boolean isSpecialTypeinfo(Address typeinfoAddress) { + if (typeinfoAddress.equals(class_type_info) || typeinfoAddress.equals(si_class_type_info) || + typeinfoAddress.equals(vmi_class_type_info)) { + return true; + } + return false; + } + + private List createClassesFromTypeinfoSymbols(List typeinfoSymbols) + throws CancelledException { + + List recoveredClasses = new ArrayList(); + + Iterator typeinfoIterator = typeinfoSymbols.iterator(); + while (typeinfoIterator.hasNext()) { + + monitor.checkCanceled(); + + Symbol typeinfoSymbol = typeinfoIterator.next(); + Address typeinfoAddress = typeinfoSymbol.getAddress(); + + // skip the typeinfo symbols from the three special typeinfos + if (isSpecialTypeinfo(typeinfoAddress)) { + continue; + } + + Namespace classNamespace = typeinfoSymbol.getParentNamespace(); + + RecoveredClass recoveredClass = getClass(classNamespace); + + + // we don't know yet if this class has vftable so just add without for now + if (recoveredClass == null) { + recoveredClass = addNoVftableClass(classNamespace); + recoveredClasses.add(recoveredClass); + + classToTypeinfoMap.put(recoveredClass, typeinfoAddress); + } + + if (recoveredClass != null && !classToTypeinfoMap.containsKey(recoveredClass)) { + classToTypeinfoMap.put(recoveredClass, typeinfoAddress); + } + + if (!recoveredClasses.contains(recoveredClass)) { + recoveredClasses.add(recoveredClass); + } + + Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); + if (specialTypeinfoRef == null) { + Msg.debug(this, + "No special typeinfo reference found. Cannot process typeinfo struct at " + + typeinfoAddress.toString()); + continue; + } + + if (!isSpecialTypeinfo(specialTypeinfoRef)) { + continue; + } + + // per docs those on this list + // have no bases (ie parents), and is also a base type for the other two class type + // representations ie (si and vmi) + // ??? it isn't clear whether these are always public or not + if (specialTypeinfoRef.equals(class_type_info)) { + // //TODO: make this a method - addGccNoInhClass + //nonInheritedClasses.add(recoveredClass); + recoveredClass.setHasSingleInheritance(true); + recoveredClass.setHasParentClass(false); + recoveredClass.setInheritsVirtualAncestor(false); + // TODO: add public ??? + continue; + } + + // per docs those on this list are + // classes containing only a single, public, non-virtual base at offset zero + // update: it isn't clear if never inherit virtual - may have found example + if (specialTypeinfoRef.equals(si_class_type_info)) { + // singleInheritedClasses.add(recoveredClass); + // TODO: make this a method and pull the part out of add parents that handles the + // single parent one + recoveredClass.setHasSingleInheritance(true); + recoveredClass.setInheritsVirtualAncestor(false); + recoveredClass.setIsPublicClass(true); + + continue; + } + + if (specialTypeinfoRef.equals(vmi_class_type_info)) { + + // multiInheritedClasses.add(recoveredClass); + recoveredClass.setHasMultipleInheritance(true); + recoveredClass.setHasParentClass(true); + } + } + return recoveredClasses; + } + + /** + * Use information from RTTI Base class Arrays to create class hierarchy lists and maps + * @param recoveredClasses list of classes to process + * @throws CancelledException if cancelled + */ + private void createClassHierarchyListAndMapForGcc(List recoveredClasses) + throws CancelledException, Exception { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List classHierarchyList = new ArrayList(); + + // no parent case + if (nonInheritedGccClasses.contains(recoveredClass)) { + classHierarchyList = getGccNoClassHierarchy(recoveredClass); + recoveredClass.setClassHierarchy(classHierarchyList); + continue; + } + + // case where there is all single inheritance in a class ancestry chain + if (singleInheritedGccClasses.contains(recoveredClass)) { + classHierarchyList = getGccSingleClassHierarchy(recoveredClass); + recoveredClass.setClassHierarchy(classHierarchyList); + continue; + } + + } + + recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List classHierarchyList = new ArrayList(); + + // once all the non and single inheritance ones are created, create the multi ones + // case where there is multi-inheritance somewhere in the chain + if (multiInheritedGccClasses.contains(recoveredClass)) { + classHierarchyList = getGccMultiClassHierarchy(recoveredClass); + recoveredClass.setClassHierarchy(classHierarchyList); + } + } + + // create parent class hierarchy maps + recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List parentList = recoveredClass.getParentList(); + Iterator parentIterator = parentList.iterator(); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parentClass = parentIterator.next(); + recoveredClass.addClassHierarchyMapping(parentClass, + parentClass.getClassHierarchy()); + } + } + + // TODO: create base type maps to add if virtual parent or not + + } + + /** + * Create the class hierarchy list for a class with no inheritance + * @param recoveredClass the given class + * @return the class hierarchy list for the given class with no inheritance + */ + List getGccNoClassHierarchy(RecoveredClass recoveredClass) { + List classHierarchyList = new ArrayList(); + classHierarchyList.add(recoveredClass); + return classHierarchyList; + } + + /** + * Create the class hierarchy for a class with only single inheritance parents + * @param recoveredClass the given class + * @return the class hierarchy for the given class with only single inheritance parents + * @throws CancelledException if cancelled + */ + List getGccSingleClassHierarchy(RecoveredClass recoveredClass) + throws CancelledException { + + List classHierarchyList = new ArrayList(); + + RecoveredClass currentClass = recoveredClass; + classHierarchyList.add(currentClass); + + while (currentClass.hasParentClass()) { + monitor.checkCanceled(); + currentClass = currentClass.getParentList().get(0); + classHierarchyList.add(currentClass); + } + return classHierarchyList; + } + + /** + * Create the class hierarchy list for a class with multiple inheritance + * @param recoveredClass the given class + * @return the class hierarchy list for the given class with multiple inheritance + * @throws CancelledException if cancelled + */ + List getGccMultiClassHierarchy(RecoveredClass recoveredClass) + throws CancelledException { + + List classHierarchyList = new ArrayList(); + + classHierarchyList.add(recoveredClass); + + List parentList = recoveredClass.getParentList(); + Iterator parentIterator = parentList.iterator(); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass parentClass = parentIterator.next(); + if (nonInheritedGccClasses.contains(parentClass)) { + classHierarchyList.addAll(parentClass.getClassHierarchy()); + continue; + } + if (singleInheritedGccClasses.contains(parentClass)) { + classHierarchyList.addAll(parentClass.getClassHierarchy()); + continue; + } + if (multiInheritedGccClasses.contains(parentClass)) { + classHierarchyList.addAll(getGccMultiClassHierarchy(parentClass)); + } + } + return classHierarchyList; + + } + + /** + * Method to create a series of long data types from the given start address to the given end + * address + * @param start the starting address + * @param end the ending address + */ + private void createLongs(Address start, Address end) { + + LongDataType longDT = new LongDataType(); + int offset = 0; + Address address = start; + while (address != null && !address.equals(end)) { + try { + api.clearListing(address); + api.createData(address, longDT); + offset += defaultPointerSize; + address = getAddress(start, offset); + } + catch (Exception e) { + return; + } + } + + } + + /** + * Method to get address at address + offset + * @param address the given address + * @param offset the given offset + * @return the address at address + offset or null if it doesn't exist + */ + private Address getAddress(Address address, int offset) { + try { + Address newAddress = address.add(offset); + return newAddress; + } + catch (AddressOutOfBoundsException e) { + return null; + } + } + + private int getNumFunctionPointers(Address topAddress, boolean allowNullFunctionPtrs, + boolean allowDefaultRefsInMiddle) throws CancelledException { + + int numFunctionPointers = 0; + Address address = topAddress; + MemoryBlock currentBlock = program.getMemory().getBlock(topAddress); + + boolean stillInCurrentTable = true; + while (address != null && currentBlock.contains(address) && stillInCurrentTable && + (isPossibleFunctionPointer(address) || + (allowNullFunctionPtrs && isPossibleNullPointer(address)))) { + + numFunctionPointers++; + address = address.add(defaultPointerSize); + Symbol symbol = api.getSymbolAt(address); + if (symbol == null) { + continue; + } + // never let non-default refs in middle + if (symbol.getSource() != SourceType.DEFAULT) { + stillInCurrentTable = false; + } + + // if it gets here it is default + if (!allowDefaultRefsInMiddle) { + stillInCurrentTable = false; + } + } + + return numFunctionPointers; + + } + + /** + * Method to determine if there are enough zeros to make a null poihnter and no references into + * or out of the middle + * @param address the given address + * @return true if the given address could be a valid null pointer, false if not + */ + private boolean isPossibleNullPointer(Address address) throws CancelledException { + if (!extraUtils.hasNumZeros(address, defaultPointerSize)) { + return false; + } + return true; + } + + /** + * Method to determine if the given address contains a possible function pointer + * @param address the given address + * @return true if the given address contains a possible function pointer or false otherwise + */ + private boolean isPossibleFunctionPointer(Address address) { + + Address possibleFunctionPointer = extraUtils.getPointer(address); + if (possibleFunctionPointer == null) { + return false; + } + + Function function = api.getFunctionAt(possibleFunctionPointer); + if (function != null) { + return true; + } + return false; + } + +} + diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/RTTIWindowsClassRecoverer.java new file mode 100644 index 0000000000..6de2bab2d4 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RTTIWindowsClassRecoverer.java @@ -0,0 +1,2613 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; + +import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd; +import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd.OffsetPcodeOpPair; +import ghidra.app.util.opinion.PeLoader; +import ghidra.app.util.opinion.PeLoader.CompilerOpinion.CompilerEnum; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.pcode.HighFunction; +import ghidra.program.model.pcode.HighVariable; +import ghidra.program.model.symbol.*; +import ghidra.program.util.ProgramLocation; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { + + //TODO: make a passed in param + private static final boolean USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS = true; + + private static final String RTTI_BASE_CLASS_ARRAY_LABEL = "RTTI_Base_Class_Array"; + private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL = + "RTTI_Class_Hierarchy_Descriptor"; + private static final String RTTI_BASE_CLASS_DESCRIPTOR_LABEL = "RTTI_Base_Class_Descriptor"; + private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL = + "RTTI_Complete_Object_Locator"; + private static final String RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME = "RTTIBaseClassDescriptor"; + private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME = + "RTTICompleteObjectLocator"; + private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME = + "RTTIClassHierarchyDescriptor"; + private static final String VFTABLE_META_PTR_LABEL = "vftable_meta_ptr"; + private static final String VFTABLE_LABEL = "vftable"; + + + private static final String CLASS_VTABLE_STRUCT_NAME = "_vbtable"; + private static final String CLASS_VTABLE_PTR_FIELD_EXT = "vftablePtr"; + + + private static final int CHD_MULTINH = 0x00000001; //Multiple inheritance + private static final int CHD_VIRTINH = 0x00000002; //Virtual inheritance + private static final int CHD_AMBIGUOUS = 0x00000004; //Multiple inheritance with repeated base classes + private static final int NONE = -1; + private static final int UNKNOWN = -2; + + boolean isPDBLoaded; + + RTTIWindowsClassRecoverer(Program program, ProgramLocation location, PluginTool tool, + FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, + boolean nameVFunctions, boolean isPDBLoaded, + TaskMonitor monitor) throws CancelledException { + + super(program, location, tool, api, createBookmarks, useShortTemplates, nameVFunctions, + monitor); + + } + + @Override + public boolean containsRTTI() throws CancelledException { + + if (!hasTypeInfoVftable()) { + return false; + } + + return true; + } + + + @Override + public boolean isValidProgramType() { + if (!isVisualStudioOrClangPe()) { + return false; + } + return true; + } + + + @Override + public void fixUpProgram() { + + if (ghidraVersion.compareTo("10.0") < 0) { + try { + fixUpRttiAnalysis(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + // if there are undefined areas that reference vftables attempt to create functions + // containing them + List vftableSymbols; + try { + vftableSymbols = getListOfVftableSymbols(); + createMissingFunctions(vftableSymbols); + } + catch (CancelledException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return; + } + + @Override + public List createRecoveredClasses() { + + //TODO: check the classPath var for null and return null if it is null + + List vftableSymbols; + try { + vftableSymbols = getListOfVftableSymbols(); + + List recoveredClasses = + recoverClassesFromClassHierarchyDescriptors(vftableSymbols); + + determineVftableOffsetsfromRTTI(recoveredClasses); + + // If no new classes have been recovered, no need to continue. Return out of script. + if (recoveredClasses.isEmpty()) { + + return recoveredClasses; + } + + createCalledFunctionMap(recoveredClasses); + + // figure out class hierarchies using either RTTI or vftable refs + + monitor.setMessage("Assigning class inheritance and hierarchies"); + assignClassInheritanceAndHierarchies(recoveredClasses); + + // Since PDB has applied so much information, use it to figure out the class member data 4 + // items (if it has them) and the constructors and destructors. + if (isPDBLoaded) { + monitor.setMessage( + "Attempting to use pdb to assign class hierarchies and extend known pdb data " + + "type information ..."); + + retrieveExistingClassStructures(recoveredClasses); + + // assign constructors and destructors based on name + assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); + } + // 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); + + } + + // create order of vftable in constructor map for each class that has a constructor so far + createVftableOrderMap(recoveredClasses); + + determineParentClassInfoFromBaseClassArray(recoveredClasses); + + + assignParentClassToVftables(recoveredClasses); + + + // 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); + + if (USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS) { + extraUtils.createShortenedTemplateNamesForClasses(recoveredClasses); + } + + createAndApplyClassStructures(recoveredClasses); + + if (!isPDBLoaded) { + //println("Removing erroneous FID namespaces and corresponding class data types"); + removeEmptyClassesAndStructures(); + } + + return recoveredClasses; + } + catch (CancelledException e) { + e.printStackTrace(); + return null; + } + catch (Exception e) { + e.printStackTrace(); + return null; + } + + } + + + private boolean isVisualStudioOrClangPe() { + return program.getExecutableFormat().equals(PeLoader.PE_NAME) && + (program.getCompiler().equals(CompilerEnum.VisualStudio.toString()) || + program.getCompiler().equals(CompilerEnum.Clang.toString())); + } + + + private boolean hasTypeInfoVftable() throws CancelledException { + + List vftableSymbols = getListOfVftableSymbols(); + + for (Symbol symbol : vftableSymbols) { + monitor.checkCanceled(); + if (symbol.getParentNamespace().getName().equals("type_info")) { + return true; + } + } + return false; + } + + + + /** + * Method to determine if the current program has RTTI data applied to it + * @return true if the current program has RTTI data applied to it + * @throws CancelledException if cancelled + */ + private boolean programHasRTTIApplied() throws CancelledException { + + // First check to see if the RTTICompleteObjectLocator data type exists. If not there has + // been no RTTI applied + DataType completeObjLocatorDataType = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME); + if (completeObjLocatorDataType == null) { + return false; + } + + // Next check that a RTTICompleteObjectLocator has been applied somewhere to make sure that + // we don't have the case where pdb ran and created the data type but rtti didn't run so didn't + // apply any of the data types + return hasSymbolAndDataType(RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL, + completeObjLocatorDataType); + } + + private void runRTTIAnalyzer() throws Exception { +// Analyzer analyzer = new RttiAnalyzer(); +// analyzer.added(program, program.getAddressFactory().getAddressSet(), monitor, +// new MessageLog()); + } + + /** + * Method to find all the vftables in the program + * @return list of all vftable symbols + * @throws CancelledException when cancelled + */ + //TODO: pull into separate methods and check separately above + private boolean hasSymbolAndDataType(String symbolName, DataType datatype) + throws CancelledException { + + String pdbName = "`" + symbolName + "'"; + SymbolIterator symbols = + program.getSymbolTable().getSymbolIterator("*" + symbolName + "*", true); + + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (symbol.getName().equals(symbolName) || symbol.getName().equals(pdbName)) { + Data dataAt = program.getListing().getDefinedDataAt(symbol.getAddress()); + if (dataAt.getDataType().equals(datatype)) { + return true; + } + } + + } + return false; + } + + public void fixUpRttiAnalysis() throws CancelledException, Exception { + applyMissingRTTIStructures(); + } + + /** + * Method to find and apply missing RTTI structures + * @throws CancelledException if cancelled + * @throws Exception if error applying label or data + */ + private List applyMissingRTTIStructures() throws CancelledException, Exception { + + List completeObjectLocatorSymbols = createMissingRTTICompleteObjectLocator(); + + List baseClassDescriptorSymbols = createMissingBaseClassDescriptors(); + + List
classHierarchyDescriptors = createMissingClassHierarchyDescriptors( + baseClassDescriptorSymbols, completeObjectLocatorSymbols); + + createMissingBaseClassArrays(classHierarchyDescriptors); + + List vftableSymbols = createMissingVftableSymbols(completeObjectLocatorSymbols); + return vftableSymbols; + + } + + /** + * Method to iterate over all symbols with Base Class Descriptor symbol and if + * the correct data type has not already been created, do so. + * @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied + * @throws CancelledException when cancelled + * @throws Exception when data cannot be created + */ + private List createMissingRTTICompleteObjectLocator() + throws CancelledException, Exception { + + List completeObjectLocatorSymbols = new ArrayList(); + + SymbolIterator dataSymbols = + symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true); + + while (dataSymbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = dataSymbols.next(); + if (!symbol.getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL)) { + continue; + } + + Data data = extraUtils.getDataAt(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) { + completeObjectLocatorSymbols.add(symbol); + continue; + } + + // for some reason it was named but not created so create it + data = createCompleteObjectLocator(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) { + completeObjectLocatorSymbols.add(symbol); + continue; + } + + //println("Cannot create RTTI_CompleteObjectLocator at " + symbol.getAddress()); + + } + return completeObjectLocatorSymbols; + } + + /** + * Method to create a CompleteObjectLocator structure at the given address + * @param address the address where the structure will be created + * @return the created CompleteObjectLocator data or null if it couldn't be created + * @throws CancelledException if cancelled + * @throws Exception if error creating data + */ + private Data createCompleteObjectLocator(Address address) throws CancelledException, Exception { + + DataType completeObjLocatorDataType = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME); + if (completeObjLocatorDataType == null) { + return null; + } + + int sizeOfDt = completeObjLocatorDataType.getLength(); + + api.clearListing(address, address.add(sizeOfDt)); + Data completeObjectLocator = extraUtils.createData(address, completeObjLocatorDataType); + if (completeObjectLocator == null) { + return null; + } + return completeObjectLocator; + } + + /** + * Method to iterate over all symbols with Base Class Descriptor symbol and if + * the correct data type has not already been created, do so. + * @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied + * @throws Exception when cancelled + */ + private List createMissingBaseClassDescriptors() throws Exception { + + List baseClassDescriptorSymbols = new ArrayList(); + + SymbolIterator dataSymbols = + symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true); + + while (dataSymbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = dataSymbols.next(); + if (!symbol.getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_LABEL)) { + continue; + } + + Data data = extraUtils.getDataAt(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { + baseClassDescriptorSymbols.add(symbol); + continue; + } + + // for some reason it was named but not created so create it + data = createBaseClassDescriptor(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { + baseClassDescriptorSymbols.add(symbol); + continue; + } + + //println("Cannot create RTTI_Base_Class_Descriptor at " + symbol.getAddress()); + + } + return baseClassDescriptorSymbols; + } + + /** + * Method to create a BaseClassDescriptor structure at the given address + * @param baseClassDescriptorAddress the address where the structure will be created + * @return the created BaseClassDescriptor data or null if it couldn't be created + * @throws CancelledException if cancelled + * @throws Exception if error creating data + */ + private Data createBaseClassDescriptor(Address baseClassDescriptorAddress) + throws CancelledException, Exception { + + DataType baseClassDescriptor = + dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME); + + int sizeOfDt = baseClassDescriptor.getLength(); + + api.clearListing(baseClassDescriptorAddress, baseClassDescriptorAddress.add(sizeOfDt)); + Data baseClassDescArray = + extraUtils.createData(baseClassDescriptorAddress, baseClassDescriptor); + if (baseClassDescArray == null) { + return null; + } + return baseClassDescArray; + } + + /** + * Method to apply missing RTTI Base Class Descriptor structures and symbols + * @param address address to apply the missing structure and symbol + * @param numBaseClasses number of base classes in the array pointing to BaseClassDescriptors + * @param classNamespace name of the class + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws MemoryAccessException if cannot access memory + * @throws CancelledException if cancelled + * @throws Exception if issue making data + */ + private void createBaseClassDescriptors(Address address, int numBaseClasses, + Namespace classNamespace) throws CancelledException, MemoryAccessException, + AddressOutOfBoundsException, Exception { + + for (int i = 0; i < numBaseClasses; i++) { + + monitor.checkCanceled(); + + Address baseClassDescriptorAddress = getReferencedAddress(address.add(i * 4)); + + Data baseClassDescriptor = extraUtils.getDataAt(baseClassDescriptorAddress); + if (baseClassDescriptor == null || !baseClassDescriptor.getDataType().getName().equals( + RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { + + int num1 = extraUtils.getInt(baseClassDescriptorAddress.add(8)); + int num2 = extraUtils.getInt(baseClassDescriptorAddress.add(12)); + int num3 = extraUtils.getInt(baseClassDescriptorAddress.add(16)); + int num4 = extraUtils.getInt(baseClassDescriptorAddress.add(20)); + + baseClassDescriptor = createBaseClassDescriptor(baseClassDescriptorAddress); + if (baseClassDescriptor != null) { + symbolTable.createLabel( + baseClassDescriptorAddress, RTTI_BASE_CLASS_DESCRIPTOR_LABEL + "_at_(" + + num1 + "," + num2 + "," + num3 + "," + num4 + ")", + classNamespace, SourceType.ANALYSIS); + } +// else { +// println( +// "Failed to create a baseClassDescArray structure at " + address.toString()); +// } + } + } + } + + /** + * + * @param baseClassDescriptors the given list of BaseClassDescriptor symbols + * @param completeObjectLocators the given list of CompleteObjectLocator symbols + * @return list of ClassHierarchyDescriptor addresses + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws InvalidInputException if issue setting return type + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws Exception if there is an issue creating a label + */ + private List
createMissingClassHierarchyDescriptors(List baseClassDescriptors, + List completeObjectLocators) throws CancelledException, MemoryAccessException, + InvalidInputException, AddressOutOfBoundsException, Exception { + + List
classHierarchyDescriptorAddresses = new ArrayList
(); + + Iterator baseClassDescriptorIterator = baseClassDescriptors.iterator(); + while (baseClassDescriptorIterator.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = baseClassDescriptorIterator.next(); + Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor( + symbol.getAddress().add(24), symbol.getParentNamespace()); + + if (classHierarchyDescriptorAddress != null && + !classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) { + classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress); + } + + } + + Iterator completeObjectLocatorIterator = completeObjectLocators.iterator(); + while (completeObjectLocatorIterator.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = completeObjectLocatorIterator.next(); + Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor( + symbol.getAddress().add(16), symbol.getParentNamespace()); + if (classHierarchyDescriptorAddress != null && + !classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) { + classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress); + } + } + + return classHierarchyDescriptorAddresses; + + } + + /** + * + * @param address the address where the ClassHierarchyDescriptor is to be created + * @param classNamespace the namespace of the class + * @return the given class's ClassHierarchyDescriptor address + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws InvalidInputException if issue setting return type + * @throws Exception if issue creating label + */ + private Address createClassHierarchyDescriptor(Address address, Namespace classNamespace) + throws CancelledException, MemoryAccessException, InvalidInputException, Exception { + + Address classHierarchyDescriptorAddress = getReferencedAddress(address); + + Data classHierarchyStructure = extraUtils.getDataAt(classHierarchyDescriptorAddress); + + if (classHierarchyStructure != null && + classHierarchyStructure.getDataType().getName().equals( + RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME)) { + return classHierarchyDescriptorAddress; + + } + + Symbol classHierarchySymbol; + + classHierarchySymbol = symbolTable.createLabel(classHierarchyDescriptorAddress, + RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL, classNamespace, SourceType.ANALYSIS); + + classHierarchyStructure = createClassHierarchyStructure(classHierarchyDescriptorAddress); + + if (classHierarchyStructure == null) { +// println("Failed to create a classHierarchyDescriptor structure at " + +// classHierarchyDescriptorAddress.toString()); + symbolTable.removeSymbolSpecial(classHierarchySymbol); + return null; + } + return classHierarchyDescriptorAddress; + } + + /** + * Method to create a ClassHierarchyDescriptor structure at the given address + * @param classHierarchyDescriptorAddress the address where the structure will be created + * @return the created ClassHierarchyDescriptor data or null if it couldn't be created + * @throws CancelledException if cancelled + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws Exception if issue creating data + */ + private Data createClassHierarchyStructure(Address classHierarchyDescriptorAddress) + throws CancelledException, AddressOutOfBoundsException, Exception { + + DataType classHDatatype = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME); + int sizeOfDt = classHDatatype.getLength(); + api.clearListing(classHierarchyDescriptorAddress, + classHierarchyDescriptorAddress.add(sizeOfDt)); + + Data classHierarchyStructure = + extraUtils.createData(classHierarchyDescriptorAddress, classHDatatype); + if (classHierarchyStructure == null) { + return null; + } + return classHierarchyStructure; + } + + /** + * + * @param classHierarchyDescriptors the given list of applied ClassHierarchyDescriptor structures + * @return a list of base class array addresses + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws Exception if there is an issue creating a label + */ + private List
createMissingBaseClassArrays(List
classHierarchyDescriptors) + throws CancelledException, MemoryAccessException, AddressOutOfBoundsException, + Exception { + + List
baseClassArrayAddresses = new ArrayList
(); + + Iterator
classHierarchyDescriptorIterator = classHierarchyDescriptors.iterator(); + + while (classHierarchyDescriptorIterator.hasNext()) { + + monitor.checkCanceled(); + + Address classHierarchyDescriptorAddress = classHierarchyDescriptorIterator.next(); + Symbol classHierarchyDescriptorSymbol = + symbolTable.getPrimarySymbol(classHierarchyDescriptorAddress); + Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); + + int numBaseClasses = extraUtils.getInt(classHierarchyDescriptorAddress.add(8)); + + Address baseClassArrayAddress = + getReferencedAddress(classHierarchyDescriptorAddress.add(12)); + + Data baseClassDescArray = extraUtils.getDataAt(baseClassArrayAddress); + + if (baseClassDescArray != null && baseClassDescArray.isArray()) { + baseClassArrayAddresses.add(baseClassArrayAddress); + continue; + } + + baseClassDescArray = createBaseClassArray(baseClassArrayAddress, numBaseClasses); + if (baseClassDescArray != null && baseClassDescArray.isArray()) { + Symbol primarySymbol = symbolTable.getPrimarySymbol(baseClassArrayAddress); + if (primarySymbol == null || + !primarySymbol.getName().contains(RTTI_BASE_CLASS_ARRAY_LABEL)) { + + symbolTable.createLabel(baseClassArrayAddress, RTTI_BASE_CLASS_ARRAY_LABEL, + classNamespace, SourceType.ANALYSIS); + } + baseClassArrayAddresses.add(baseClassArrayAddress); + createBaseClassDescriptors(baseClassArrayAddress, numBaseClasses, classNamespace); + continue; + } + +// println("Failed to create a baseClassDescArray structure at " + +// baseClassArrayAddress.toString()); + } + return baseClassArrayAddresses; + } + + /** + * Method to create a base class array at the given address with the given number of base class's in the array + * @param baseClassArrayAddress the address where the array will be created + * @param numBaseClasses the number of BaseClass's in the array + * @return the created BaseClassArray data or null if cannot retrieve it + * @throws CancelledException if cancelled + * @throws Exception if error creating data + */ + private Data createBaseClassArray(Address baseClassArrayAddress, int numBaseClasses) + throws CancelledException, Exception { + + int sizeOfDt; + ArrayDataType baseClassDescArrayDT; + + int addressSize = baseClassArrayAddress.getSize(); + if (addressSize == 32) { + DataType baseClassDescriptor = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME); + PointerDataType baseClassDescriptorPtr = new PointerDataType(baseClassDescriptor); + sizeOfDt = baseClassDescriptorPtr.getLength(); + + baseClassDescArrayDT = + new ArrayDataType(baseClassDescriptorPtr, numBaseClasses, sizeOfDt); + } + else if (addressSize == 64) { + DataType imageBaseOffset = + dataTypeManager.getDataType(CategoryPath.ROOT, "ImageBaseOffset32"); + sizeOfDt = imageBaseOffset.getLength(); + baseClassDescArrayDT = new ArrayDataType(imageBaseOffset, numBaseClasses, sizeOfDt); + } + else { + return null; + } + + api.clearListing(baseClassArrayAddress, + baseClassArrayAddress.add(numBaseClasses * sizeOfDt)); + Data baseClassDescArray = + extraUtils.createData(baseClassArrayAddress, baseClassDescArrayDT); + + if (baseClassDescArray == null) { + return null; + } + return baseClassDescArray; + } + + /** + * Method to create missing vftables and return a list of them + * @param completeObjectLocatorSymbols the list of completeObjectLocatorSymbols + * @return list of vftable symbols + * @throws CancelledException if cancelled + * @throws InvalidInputException if invalid input + * @throws CircularDependencyException if namespace has circular dependency + * @throws DuplicateNameException if try to create label with duplicate name in namespace + */ + private List createMissingVftableSymbols(List completeObjectLocatorSymbols) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + List vftables = new ArrayList(); + + Iterator iterator = completeObjectLocatorSymbols.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Symbol completeObjectLocatorSymbol = iterator.next(); + + Address completeObjectLocatorAddress = completeObjectLocatorSymbol.getAddress(); + + Namespace classNamespace = completeObjectLocatorSymbol.getParentNamespace(); + if (classNamespace.equals(globalNamespace)) { + //println("no class namespace for " + completeObjectLocatorAddress.toString()); + continue; + } + + Reference[] referencesTo = extraUtils.getReferencesTo(completeObjectLocatorAddress); + if (referencesTo.length == 0) { + //println("no refs to " + completeObjectLocatorAddress.toString()); + continue; + } + + 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; + } + + // if not created, create vftable meta pointer label + + if (getGivenSymbol(vftableAddress, VFTABLE_META_PTR_LABEL, + classNamespace) == null) { + + symbolTable.createLabel(vftableMetaPointer, VFTABLE_META_PTR_LABEL, + classNamespace, SourceType.ANALYSIS); + } + + // if not created, create vftable label + Symbol vftableSymbol = + getGivenSymbol(vftableAddress, VFTABLE_LABEL, classNamespace); + if (vftableSymbol == null) { + + vftableSymbol = symbolTable.createLabel(vftableAddress, VFTABLE_LABEL, + classNamespace, SourceType.ANALYSIS); + + if (vftableSymbol == null) { + continue; + } + } + + if (!vftables.contains(vftableSymbol)) { + vftables.add(vftableSymbol); + } + + } + } + return vftables; + } + + /** + * Method to retrieve the symbol with the given address, containing name (containing to account + * for pdb case where sometimes has extra chars) and namespace + * @param address the given address + * @param name the given name + * @param namespace the given namespace + * @return the symbol with the given address, containing name, with given namespace + * @throws CancelledException if cancelled + */ + private Symbol getGivenSymbol(Address address, String name, Namespace namespace) + throws CancelledException { + + Symbol[] symbols = symbolTable.getSymbols(address); + for (Symbol sym : symbols) { + monitor.checkCanceled(); + if (sym.getName().contains(name) && sym.getParentNamespace().equals(namespace)) { + return sym; + } + } + return null; + } + + /** + * Method to return referenced address at the given address + * @param address the address to look for a referenced address at + * @return the first referenced address from the given address + * @throws MemoryAccessException if memory cannot be read + */ + private Address getReferencedAddress(Address address) throws MemoryAccessException { + + //TODO: switch to this then test then just rewrite the call and get rid of this method + // MSDataTypeUtils.getReferencedAddress(currentProgram, address); + // this will work whether there is a created reference or not + int addressSize = address.getSize(); + if (addressSize == 32) { + long offset = extraUtils.getInt(address); + + return address.getNewAddress(offset); + } + + // this currently will workn only if there is a created reference + // TODO: get ibo bytes and figure out what the ibo ref address would be + if (addressSize == 64) { + Reference refs[] = extraUtils.getReferencesFrom(address); + if (refs.length == 0) { + return null; + } + return refs[0].getToAddress(); + } + return null; + } + + /** + * Method to retrieve the AddressSet of the current program's initialized memory + * @return the AddressSet of the current program's initialized memory + * @throws CancelledException if cancelled + */ + private AddressSet getInitializedMemory() throws CancelledException { + + AddressSet dataAddresses = new AddressSet(); + MemoryBlock[] blocks = program.getMemory().getBlocks(); + + for (MemoryBlock block : blocks) { + monitor.checkCanceled(); + + if (block.isInitialized()) { + dataAddresses.add(block.getStart(), block.getEnd()); + } + } + return dataAddresses; + } + + /** + * Method to fix up the current program so that script will be more successful by finding + * missing vftable referencing functions and missing RTTI data structures. + * manually create some of them + * @throws CancelledException when cancelled + * @throws Exception when data cannot be created + */ + private void createMissingFunctions(List vftableSymbols) + throws CancelledException, Exception { + + List
unusedVftableReferences = + findVftableReferencesNotInFunction(vftableSymbols); + + if (unusedVftableReferences.size() > 0) { + extraUtils.createUndefinedFunctions(unusedVftableReferences); + } + + // create these automatically if found + findFunctionsUsingAtexit(); + } + + + + + + /** + * Method to recover the class information for each vftable symbol on the list + * * For each virtual function table: + * 1. get vftable's existing class + * 2. create matching data type category folder in dt manager + * 3. get list of virtual functions + * 4. create RecoveredClass object for the vftable class + * 5. add mapping from vftableAddress to class + * 6. add list of const/dest functions to RecoveredClass object + * 7. update list of all const/dest functions in currenetProgram + * 8. set RecoveredClass indeterminate list to const/dest list + * 9. update list of all indeterminate const/dest + * @param vftableSymbols List of vftable symbols + * @return List of RecoveredClass objects created corresponding to the vftable symbols + * @throws CancelledException if cancelled + * @throws Exception if issue creating data + */ + private List recoverClassesFromClassHierarchyDescriptors( + List vftableSymbols) throws CancelledException, Exception { + + List recoveredClasses = new ArrayList(); + + List classHierarchyDescriptorList = getListOfClassHierarchyDescriptors(); + + Iterator classHierarchyDescriptorIterator = classHierarchyDescriptorList.iterator(); + while (classHierarchyDescriptorIterator.hasNext()) { + monitor.checkCanceled(); + Symbol classHierarchyDescriptorSymbol = classHierarchyDescriptorIterator.next(); + Address classHierarchyDescriptorAddress = classHierarchyDescriptorSymbol.getAddress(); + + // Get class name from class vftable is in + Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); + + if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { +// println("RTTI_Class_Hierarchy_Descriptor at " + +// classHierarchyDescriptorAddress.toString() + +// " is not in a class namespace. Cannot process."); + continue; + } + + List vftableSymbolsInNamespace = + getVftablesInNamespace(vftableSymbols, classNamespace); + + //if there are no vftables in this class then create a new class object and make it + // non-vftable class + if (vftableSymbolsInNamespace.size() == 0) { + String className = classNamespace.getName(); + String classNameWithNamespace = classNamespace.getName(true); + + // Create Data Type Manager Category for given class + // TODO: make this global and check it for null + CategoryPath classPath = + extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, + classNameWithNamespace); + + RecoveredClass nonVftableClass = + new RecoveredClass(className, classPath, classNamespace, dataTypeManager); + nonVftableClass.setHasVftable(false); + // add recovered class to map + if (getClass(classNamespace) == null) { + updateNamespaceToClassMap(classNamespace, nonVftableClass); + + // add it to the running list of RecoveredClass objects + recoveredClasses.add(nonVftableClass); + } + } + // if there are vftables in the class, call the method to make + // a new class object using the vftable info + else { + List classesWithVftablesInNamespace = + recoverClassesFromVftables(vftableSymbolsInNamespace, false, false); + if (classesWithVftablesInNamespace.size() == 0) { + //println("No class recovered for namespace " + classNamespace.getName()); + continue; + } + if (classesWithVftablesInNamespace.size() > 1) { +// println("Unexpected multiple classes recovered for namespace " + +// classNamespace.getName()); + continue; + } + + recoveredClasses.add(classesWithVftablesInNamespace.get(0)); + + } + + } + + return recoveredClasses; + } + + /** + * Method to get a list of RTTI_Base_Class_Descriptor symbols + * @return List of Symbols named "RTTI_Class_Hierarchy_Descriptor" + * @throws CancelledException if cancelled + */ + private List getListOfClassHierarchyDescriptors() throws CancelledException { + + List classHierarchyDescriptorList = extraUtils.getListOfSymbolsInAddressSet( + getInitializedMemory(), RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL, false); + + return classHierarchyDescriptorList; + } + + /** + * Method to create map for each class containing offset in class structure for each class vftable using information + * found in the class's complete object locator structure(s) + * @param recoveredClasses List of classes + * @throws Exception when cancelled + */ + private void determineVftableOffsetsfromRTTI(List recoveredClasses) + throws Exception { + + PointerDataType pointerDataType = new PointerDataType(); + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List
vftableAddresses = recoveredClass.getVftableAddresses(); + Iterator
vftableIterator = vftableAddresses.iterator(); + while (vftableIterator.hasNext()) { + monitor.checkCanceled(); + Address vftableAddress = vftableIterator.next(); + Address ptrToColAddress = vftableAddress.subtract(defaultPointerSize); + + Data pointerToCompleteObjLocator = extraUtils.getDataAt(vftableAddress); + if (pointerToCompleteObjLocator == null) { + pointerToCompleteObjLocator = + extraUtils.createData(ptrToColAddress, pointerDataType); + } + + Address colAddress = extraUtils.getReferencedAddress(ptrToColAddress, false); + + if (colAddress == null) { +// println(recoveredClass.getName() + " couldn't get referenced col from " + +// ptrToColAddress.toString()); + continue; + } + + try { + Address addressOfOffset = colAddress.add(4); + + int offset = extraUtils.getInt(addressOfOffset); + + recoveredClass.addClassOffsetToVftableMapping(offset, vftableAddress); + } + catch (AddressOutOfBoundsException e) { +// println(recoveredClass.getName() + "error getting offset at address " + +// colAddress.toString() + " + 4"); + } + } + + } + + } + + /** + * Method to figure out the class hierarchies either with RTTI if it is present or with vftable + * references + * @param recoveredClasses List of classes to process + * @throws CancelledException if cancelled + * @throws AddressOutOfBoundsException AddressOutOfBoundsException + * @throws MemoryAccessException if memory cannot be read + */ + private void assignClassInheritanceAndHierarchies(List recoveredClasses) + throws CancelledException, MemoryAccessException, AddressOutOfBoundsException { + + // Use RTTI information to determine inheritance type and + // class hierarchy + Iterator recoveredClassesIterator = recoveredClasses.iterator(); + while (recoveredClassesIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassesIterator.next(); + + int inheritanceFlag = getClassInheritanceFlag(recoveredClass.getClassNamespace()); + if (inheritanceFlag == NONE) { +// println( +// "Could not get inheritance attribute from class hierarchy structure for " + +// "class " + recoveredClass.getName()); + recoveredClassesIterator.remove(); + continue; + } + setClassInheritanceType(recoveredClass, inheritanceFlag); + + } + getClassHierarchyFromRTTI(recoveredClasses); + } + + /** + * Use information from RTTI Base class Arrays to create class hierarchy lists and maps + * @param recoveredClasses list of classes to process + * @throws CancelledException if cancelled + */ + //TODO: split into two methods so I can reuse last part for gcc too + private void getClassHierarchyFromRTTI(List recoveredClasses) + throws CancelledException { + + // go through first collecting the class hierarchy lists from the RTTI + // determine inheritance type + // add parents if single inheritance + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List classHierarchyFromRTTI = getClassHierarchyFromRTTI(recoveredClass); + + if (classHierarchyFromRTTI.size() > 0) { + recoveredClass.setClassHierarchy(classHierarchyFromRTTI); + + // if single inheritance flag either no parent or one parent + if (recoveredClass.hasSingleInheritance()) { + + // update class accordingly with a parent or no parent + assignSingleInheritanceAncestorsUsingHierarchyList(classHierarchyFromRTTI); + + // if a parent, update class hierarchy map + List parentList = recoveredClass.getParentList(); + if (parentList.size() == 1) { + + RecoveredClass parentClass = parentList.get(0); + recoveredClass.addClassHierarchyMapping(parentClass, + parentClass.getClassHierarchy()); + } + + } + } + + } + + // Now that all hierarchy lists are collected iterate again and process the multi-inherited + // ones using the single hierarchy lists to help determine direct parents + recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + if (recoveredClass.hasMultipleInheritance()) { + + List classHierarchy = recoveredClass.getClassHierarchy(); + int index = 1; + while (index < classHierarchy.size()) { + monitor.checkCanceled(); + RecoveredClass parentClass = classHierarchy.get(index); + List parentClassHierarchy = parentClass.getClassHierarchy(); + recoveredClass.addClassHierarchyMapping(parentClass, parentClassHierarchy); + updateClassWithParent(parentClass, recoveredClass); + index += parentClassHierarchy.size(); + } + } + + } + + } + + /** + * Method to assign parent classes given an ordered list of hierarchy + * child, parent, grandparent, ... for single inheritance case + * @param hierarchyList ordered list of class hierarchy starting with child + * @throws CancelledException if cancelled + */ + private void assignSingleInheritanceAncestorsUsingHierarchyList( + List hierarchyList) throws CancelledException { + + RecoveredClass currentClass = hierarchyList.get(0); + + ListIterator listIterator = hierarchyList.listIterator(1); + while (listIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parentClass = listIterator.next(); + + if (!currentClass.hasParentClass()) { + updateClassWithParent(parentClass, currentClass); + } + + currentClass = parentClass; + } + + } + + /** + * Determine class hierarchies using RTTI Base Class Array info + * @param recoveredClass current class + * @return List of classes representing current class's hierarchy + * @throws CancelledException if cancelled + */ + private List getClassHierarchyFromRTTI(RecoveredClass recoveredClass) + throws CancelledException { + + List classHierarchy = new ArrayList(); + + List symbols = extraUtils.getListOfSymbolsByNameInNamespace( + RTTI_BASE_CLASS_ARRAY_LABEL, + recoveredClass.getClassNamespace(), false); + + if (symbols.size() == 1) { + Symbol rttiBaseClassSymbol = symbols.get(0); + Address rttiBaseClassAddress = rttiBaseClassSymbol.getAddress(); + Data rttiBaseClassDescriptorArray = api.getDataAt(rttiBaseClassAddress); + int numPointers = rttiBaseClassDescriptorArray.getNumComponents(); + + for (int i = 0; i < numPointers; ++i) { + monitor.checkCanceled(); + + // Get the it is pointing to + Address pointerAddress = rttiBaseClassDescriptorArray.getComponent(i).getAddress(); + + Address baseClassDescriptorAddress = + extraUtils.getSingleReferencedAddress(pointerAddress); + + if (baseClassDescriptorAddress == null) { + return classHierarchy; + } + + Symbol primarySymbol = symbolTable.getPrimarySymbol(baseClassDescriptorAddress); + if (primarySymbol == null) { + return classHierarchy; + } + + Namespace pointedToNamespace = primarySymbol.getParentNamespace(); + if (pointedToNamespace == null) { + return classHierarchy; + } + + // if the namespace isn't in the map then it is a class + // without a vftable and a new RecoveredClass object needs to be created + if (getClass(pointedToNamespace) == null) { + addNoVftableClass(pointedToNamespace); + } + + RecoveredClass pointedToClass = getClass(pointedToNamespace); + + if (classHierarchy.size() > 0 && + classHierarchy.get(classHierarchy.size() - 1).equals(pointedToClass)) { + continue; + } + + classHierarchy.add(pointedToClass); + + } + } + else if (symbols.size() > 1) { + //TODO: throw exception? + //println(recoveredClass.getName() + " has more than one base class array"); + } + return classHierarchy; + } + + /** + * Method to get class inheritance flag from the RTTIClassHierarchyDescriptor structure + * @param classNamespace the given class namespace + * @return the class inheritance flag + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws AddressOutOfBoundsException if try reading memory out of bounds + */ + private int getClassInheritanceFlag(Namespace classNamespace) + throws CancelledException, MemoryAccessException, AddressOutOfBoundsException { + + List symbols = extraUtils.getListOfSymbolsByNameInNamespace( + RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL, classNamespace, false); + + if (symbols.size() >= 1) { + + try { + return (extraUtils.getInt(symbols.get(0).getAddress().add(4))); + } + catch (MemoryAccessException e) { +// println("Could not get class inheritance flag at address " + +// symbols.get(0).getAddress().toString()); + return NONE; + } + catch (AddressOutOfBoundsException e) { +// println("Could not get class inheritance flag at address " + +// symbols.get(0).getAddress().toString()); + return NONE; + } + } + return NONE; + } + + /** + * Method to set the class inheritance type based on Class Hierarchy Descriptor inheritance + * attribute flag: + * bit 0: 0 = single inheritance/ 1 = multiple inheritance + * bit 1: 0 = non-virtual inheritance / 1 = virtual inheritance + * bit 2: 0 = non-ambiguous case / 1 = ambiguous (ie multiple inheritance with repeated base classes) + * @param recoveredClass the given class + * @param inheritanceType the inheritance type to set in the class object + */ + private void setClassInheritanceType(RecoveredClass recoveredClass, int inheritanceType) { + + // TODO: update the single virtual case from here too - may need to update class + // TODO: ?? add multi-repeate base inh flag? do I care other than to give info to user? + + if ((inheritanceType & CHD_MULTINH) == 0) { + recoveredClass.setHasSingleInheritance(true); + recoveredClass.setHasMultipleInheritance(false); + + if ((inheritanceType & CHD_VIRTINH) == 0) { + recoveredClass.setInheritsVirtualAncestor(false); + } +// else { +// // TODO: might have to update the single virt inh here +// println("Flag indicates single inheritance virtual ancestor for class " + +// recoveredClass.getName()); +// } + } + else { + recoveredClass.setHasSingleInheritance(false); + recoveredClass.setHasMultipleInheritance(true); + if ((inheritanceType & CHD_VIRTINH) == 0) { + recoveredClass.setHasMultipleVirtualInheritance(false); + } + else { + recoveredClass.setHasMultipleVirtualInheritance(true); +// println("Flag indicates multiple inheritance virtual ancestor for class " + +// recoveredClass.getName()); + } + } + //TODO: CHD_AMBIGUOUS = 0x00000004; + + } + + /** + * Method to call the various methods to determine whether the functions that make references to + * the vftables are constructors, destructors, deleting destructors, clones, or vbase functions + * @param recoveredClasses List of classes + * @throws CancelledException if cancelled + * @throws InvalidInputException if issues setting function return + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @Exception if issues making labels + */ + private void processConstructorAndDestructors(List recoveredClasses) + throws CancelledException, InvalidInputException, DuplicateNameException, Exception { + + // find deleting destructors using various mechanisms + findDeletingDestructors(recoveredClasses); + + // use atexit param list to find more destructors + findDestructorsUsingAtexitCalledFunctions(recoveredClasses); + + // figure out which are inlined and put on separate list to be processed later + separateInlinedConstructorDestructors(recoveredClasses); + + // figure out which member functions are constructors and which are destructors + // using the order their parents are called + processRegularConstructorsAndDestructorsUsingCallOrder(recoveredClasses); + + // determine which of the inlines are constructors and which are destructors + processInlinedConstructorsAndDestructors(recoveredClasses); + + findConstructorsAndDestructorsUsingAncestorClassFunctions(recoveredClasses); + + findInlineConstructorsAndDestructorsUsingRelatedClassFunctions(recoveredClasses); + + // use the load/store information from decompiler to figure out as many of the + // 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); + + // 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 + // first block, and call non-parent constructors and non operator new before the vftable ref + findMoreInlinedConstructors(recoveredClasses); + + findDestructorsWithNoParamsOrReturn(recoveredClasses); + + // use vftables with references to all the same function (except possibly one deleting + // destructor)to find the purecall function + identifyPureVirtualFunction(recoveredClasses); + + findRealVBaseFunctions(recoveredClasses); + + } + + + /** + * 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 + * @param recoveredClasses List of classes to process + * @throws Exception when cancelled + */ + private void determineParentClassInfoFromBaseClassArray(List recoveredClasses) + throws Exception { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + boolean hasVirtualAncestor = false; + int vbaseOffset = NONE; + + // iterate over base class array and for each parent class of the given recovered class + // get the mdisp, pdisp, vdisp info + List baseClassArray = extraUtils.getListOfSymbolsByNameInNamespace( + RTTI_BASE_CLASS_ARRAY_LABEL, recoveredClass.getClassNamespace(), false); + + // this should never happen + if (baseClassArray.size() != 1) { + throw new Exception( + recoveredClass.getName() + " has more than one base class array"); + } + + Address baseClassArrayAddress = baseClassArray.get(0).getAddress(); + Data baseClassArrayData = api.getDataAt(baseClassArrayAddress); + + if (!baseClassArrayData.isArray()) { + throw new Exception( + recoveredClass.getName() + " base class array is not an array data type"); + + } + + StructureDataType vbaseStructure = new StructureDataType(recoveredClass.getClassPath(), + recoveredClass.getName() + CLASS_VTABLE_STRUCT_NAME, 0, dataTypeManager); + + IntegerDataType integerDataType = new IntegerDataType(); + + int numPointers = baseClassArrayData.getNumComponents(); + + for (int i = 0; i < numPointers; ++i) { + monitor.checkCanceled(); + + // Get the address it is pointing to + Address pointerAddress = baseClassArrayData.getComponent(i).getAddress(); + + Address baseClassDescriptorAddress = + extraUtils.getReferencedAddress(pointerAddress, true); + if (baseClassArrayAddress == null) { + continue; + } + Symbol baseClassDescSymbol = + symbolTable.getPrimarySymbol(baseClassDescriptorAddress); + if (baseClassDescSymbol == null) { + continue; + } + Namespace namespace = baseClassDescSymbol.getParentNamespace(); + if (namespace.equals(globalNamespace)) { + continue; + } + RecoveredClass baseClass = getClass(namespace); + + // update parent map based on pdisp (-1 means not virtual base, otherwise it is a + // virtual base + // set the has vbtable if any of them are a virtual base + // update the vbstruct if any of them are a virtual base + int pdisp = api.getInt(baseClassDescriptorAddress.add(12)); + int vdisp = api.getInt(baseClassDescriptorAddress.add(16)); + + if (vbaseStructure.getComponentAt(vdisp) == null) { + String classFieldName = new String(); + if (USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS && + !baseClass.getShortenedTemplateName().isEmpty()) { + classFieldName = baseClass.getShortenedTemplateName(); + } + else { + classFieldName = baseClass.getName(); + } + vbaseStructure.insertAtOffset(vdisp, integerDataType, + integerDataType.getLength(), classFieldName + "_offset", null); + } + + // skip the rest for the given class + if (baseClass == recoveredClass) { + continue; + } + + if (pdisp == -1) { + recoveredClass.addParentToBaseTypeMapping(baseClass, false); + } + else { + + if (vbaseOffset == NONE) { + vbaseOffset = pdisp; + } + else if (vbaseOffset != pdisp) { + throw new Exception( + recoveredClass.getName() + " vbaseOffset values do not match"); + } + + hasVirtualAncestor = true; + recoveredClass.addParentToBaseTypeMapping(baseClass, true); + } + + // after the loop check if vbstruct/flag and if so figure out the vbaseTable address + if (hasVirtualAncestor) { + if (vbaseOffset != UNKNOWN) { + Address vbtableAddress = getVbaseTableAddress(recoveredClass, vbaseOffset); + if (vbtableAddress != null) { + recoveredClass.setVbtableAddress(vbtableAddress); + } + } + recoveredClass.setVbtableStructure(vbaseStructure); + recoveredClass.setInheritsVirtualAncestor(true); + recoveredClass.setVbtableOffset(vbaseOffset); + } + } + } + } + + /** + * Method to retrieve the address of the vbtable given the vbtableOffset from the baseClassDescriptor + * and the address referenced by the target address in the storedPcodeOp at the vbtableOffset + * @param recoveredClass the given class + * @param vbtableOffset the offset of the vbtable in the given class + * @return the address in the current program's memory of the given class's vbtable + * @throws CancelledException if cancelled + */ + private Address getVbaseTableAddress(RecoveredClass recoveredClass, int vbtableOffset) + throws CancelledException { + + List constructorList = recoveredClass.getConstructorList(); + if (constructorList.isEmpty()) { + constructorList.addAll(recoveredClass.getInlinedConstructorList()); + if (constructorList.isEmpty()) { + return null; + } + } + + Iterator constructorIterator = constructorList.iterator(); + while (constructorIterator.hasNext()) { + + monitor.checkCanceled(); + Function constructor = constructorIterator.next(); + + HighFunction highFunction = decompilerUtils.getHighFunction(constructor); + + if (highFunction == null) { + continue; + } + + FillOutStructureCmd fillCmd = + new FillOutStructureCmd(program, location, tool); + + Address vbtableAddress = getVbtableAddressFromDecompiledFunction(fillCmd, highFunction, + recoveredClass, constructor, vbtableOffset); + + if (vbtableAddress != null) { + return vbtableAddress; + } + } + + List indeterminateList = recoveredClass.getIndeterminateList(); + if (indeterminateList.isEmpty()) { + indeterminateList.addAll(recoveredClass.getIndeterminateInlineList()); + if (indeterminateList.isEmpty()) { + return null; + } + } + + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + + monitor.checkCanceled(); + Function constructor = indeterminateIterator.next(); + + HighFunction highFunction = decompilerUtils.getHighFunction(constructor); + + if (highFunction == null) { + continue; + } + + FillOutStructureCmd fillCmd = + new FillOutStructureCmd(program, location, tool); + + Address vbtableAddress = getVbtableAddressFromDecompiledFunction(fillCmd, highFunction, + recoveredClass, constructor, vbtableOffset); + + if (vbtableAddress != null) { + return vbtableAddress; + } + } + + return null; + + } + + /** + * Method to find the address of the vbtable referenced at the given offset in the given function + * @param fillCmd the decompiler's filled out structure for a particular variable + * @param highFunction the high function for the given function + * @param recoveredClass the given class + * @param function the given function + * @param offset the offset in the filled out structure where the vbtable address must be + * @return the address of the found vbtable or null if none is found + * @throws CancelledException if cancelled + */ + private Address getVbtableAddressFromDecompiledFunction(FillOutStructureCmd fillCmd, + HighFunction highFunction, RecoveredClass recoveredClass, Function function, int offset) + throws CancelledException { + + List highVariables = new ArrayList(); + + // if there are params add the first or the "this" param to the list to be checked first + // It is the most likely to store the vftablePtr + + int numParams = highFunction.getFunctionPrototype().getNumParams(); + if (numParams > 0) { + + for (int i = 0; i < numParams; i++) { + monitor.checkCanceled(); + HighVariable param = + highFunction.getFunctionPrototype().getParam(i).getHighVariable(); + if (param != null) { + highVariables.add(param); + } + } + } + + Iterator highVariableIterator = highVariables.iterator(); + + while (highVariableIterator.hasNext()) { + + HighVariable highVariable = highVariableIterator.next(); + monitor.checkCanceled(); + + fillCmd.processStructure(highVariable, function); + List stores = fillCmd.getStorePcodeOps(); + stores = removePcodeOpsNotInFunction(function, stores); + + // this method checks the storedPcodeOps to see if one is a vftable address + Iterator iterator = stores.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + OffsetPcodeOpPair offsetPcodeOpPair = iterator.next(); + int pcodeOffset = offsetPcodeOpPair.getOffset().intValue(); + if (pcodeOffset == offset) { + + Address listingAddress = + getTargetAddressFromPcodeOp(offsetPcodeOpPair.getPcodeOp()); + + Address vbtableAddress = + extraUtils.getSingleReferencedAddress(listingAddress); + + if (vbtableAddress == null) { + continue; + } + return vbtableAddress; + + } + } + + } + return null; + } + + /** + * Method to create vftable address and parent class map for each class object + * @param recoveredClasses list of class objects + * @throws Exception when cancelled + */ + private void assignParentClassToVftables(List recoveredClasses) + throws Exception { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + if (!recoveredClass.hasVftable()) { + continue; + } + + List
vftableAddresses = recoveredClass.getVftableAddresses(); + if (vftableAddresses.size() == 0) { + continue; + } + + List parentsWithVirtualFunctions = + getParentsWithVirtualFunctions(recoveredClass); + if (parentsWithVirtualFunctions.size() == 0) { + continue; + } + + List ancestorsAllowedToMap = new ArrayList(); + + List ancestorsWithoutVfunctions = + getAncestorsWithoutVfunctions(recoveredClass); + + // case where more than one parent with virtual functions and class has multiple + // virtual inheritance, ie the diamond case, need to remove parents with common + // ancestors from parent list and replace with the common ancestor + if (recoveredClass.hasMultipleVirtualInheritance()) { + // need to find common ancestor inherited in the diamond shape and replace + // the parents that use it with the ancestor. The resulting list should + // equal the number of vftables + ancestorsAllowedToMap = replaceParentsWithCommonAncestor(recoveredClass); + ancestorsAllowedToMap.removeAll(ancestorsWithoutVfunctions); + mapVftablesToParents(recoveredClass, ancestorsAllowedToMap); + continue; + } + + // case where class has multiple inheritance flag because an ancestor has mult inheritance but + // TODO: pull into separate method + if (recoveredClass.hasMultipleInheritance() && + recoveredClass.getClassHierarchyMap().size() == 1 && + recoveredClass.getVftableAddresses().size() > 1) { + + List parents = + new ArrayList(recoveredClass.getClassHierarchyMap().keySet()); + RecoveredClass singleParent = parents.get(0); + List grandParents = + getParentsWithVirtualFunctions(singleParent); + // check that they both have vftables + // get their order from the class hierarchy list + // first see if it has a parent order map and just make it the same one + + if (grandParents.size() == recoveredClass.getVftableAddresses().size()) { + // get the sorted order of vftables + Map orderToVftableMap = recoveredClass.getOrderToVftableMap(); + List sortedOrder = new ArrayList(orderToVftableMap.keySet()); + Collections.sort(sortedOrder); + + int order = 0; + // iterate over the hierarchy list and use it to get the order of the parentsParents and assign + // to correct vftable + List classHierarchy = recoveredClass.getClassHierarchy(); + Iterator classHierarchyIterator = classHierarchy.iterator(); + while (classHierarchyIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestor = classHierarchyIterator.next(); + if (grandParents.contains(ancestor)) { + Integer index = sortedOrder.get(order); + Address vftableAddress = orderToVftableMap.get(index); + recoveredClass.addVftableToBaseClassMapping(vftableAddress, ancestor); + order++; + } + } + + } + + continue; + } + + if (recoveredClass.hasSingleInheritance() && + recoveredClass.getParentList().size() == 1 && + recoveredClass.getVftableAddresses().size() == 2) { + + // case 1: class's direct parent is virtually inherited and has vtable + // first Vftable is mapped to null parent because it is used in class struct by current class + // second is mapped to first virtual ancestor with vftable + + // case 2: class's direct parent is non-virt with vtable, it has ancestor that is virtual with vftable + // use the mapping function to map correct parent to correct vftable + + // case multiple vftables and there is only one parent that is virtually inherited + // one vftable is used for current class and one for the virt in + RecoveredClass virtualAncestorWithVfunctions = + getFirstVirtuallyInheritedAncestorWithVfunctions(recoveredClass); + + if (virtualAncestorWithVfunctions != null) { + + //RecoveredClass parentClass = recoveredClass.getParentClass(); + + RecoveredClass parentClass = recoveredClass.getParentList().get(0); + + if (virtualAncestorWithVfunctions.equals(parentClass)) { + + // map the current class to the first vftable + recoveredClass.addVftableToBaseClassMapping( + recoveredClass.getVftableAddresses().get(0), recoveredClass); + // map the virtual parent to the second vftable + recoveredClass.addVftableToBaseClassMapping( + recoveredClass.getVftableAddresses().get(1), parentClass); + continue; + } + + // map the non-virtual parent to the first vftable + recoveredClass.addVftableToBaseClassMapping( + recoveredClass.getVftableAddresses().get(0), parentClass); + // map the first virtual ancestor to the second vftable + recoveredClass.addVftableToBaseClassMapping( + recoveredClass.getVftableAddresses().get(1), virtualAncestorWithVfunctions); + continue; + + } + + } + + // the rest should work for both single and regular multiple inheritance + ancestorsAllowedToMap = parentsWithVirtualFunctions; + + // when only one direct parent with virtual functions, map the vftable to that parent + if (ancestorsAllowedToMap.size() == 1 && vftableAddresses.size() == 1) { + recoveredClass.addVftableToBaseClassMapping( + recoveredClass.getVftableAddresses().get(0), ancestorsAllowedToMap.get(0)); + continue; + } + + // All other cases where the number of vftables should equal the number of + // parents (virtual or otherwise) + mapVftablesToParents(recoveredClass, ancestorsAllowedToMap); + + } + + } + + /** + * Method to determine if the given class inherits any ancestors virtually + * @param recoveredClass the given class + * @return true if any of the given class's ancestors are inherited virtually, false otherwise + * @throws CancelledException if cancelled + */ + private RecoveredClass getFirstVirtuallyInheritedAncestorWithVfunctions( + RecoveredClass recoveredClass) throws CancelledException { + + List classHierarchy = recoveredClass.getClassHierarchy(); + + Iterator hierarchyIterator = classHierarchy.iterator(); + + while (hierarchyIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestorClass = hierarchyIterator.next(); + + RecoveredClass firstVirtuallyInheritedAncestorWithVfunctions = + getVirtuallyInheritedParentWithVfunctions(ancestorClass); + if (firstVirtuallyInheritedAncestorWithVfunctions != null) { + return firstVirtuallyInheritedAncestorWithVfunctions; + + } + + } + + return null; + } + + /** + * Method to retrieve the virtually inherited parent that has vfunctions for the given class if there is one + * @param recoveredClass the given class + * @return the virtually inherited parent that has vfunctions for the given class if there is one, or null if there isn't + * @throws CancelledException if cancelled + */ + private RecoveredClass getVirtuallyInheritedParentWithVfunctions(RecoveredClass recoveredClass) + throws CancelledException { + + if (!recoveredClass.hasVftable()) { + return null; + } + + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + if (classHierarchyMap == null) { + return null; + } + + Map parentToBaseTypeMap = recoveredClass.getParentToBaseTypeMap(); + + List parents = new ArrayList(classHierarchyMap.keySet()); + Iterator parentIterator = parents.iterator(); + + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parent = parentIterator.next(); + Boolean isVirtuallyInherited = parentToBaseTypeMap.get(parent); + + if (isVirtuallyInherited != null && isVirtuallyInherited && parent.hasVftable()) { + return parent; + } + + } + return null; + + } + + /** + * Using their address order in constructor/destructor functions, map the given class's vftables to their + * respective parent (or in some cases, ancestor) classes + * @param recoveredClass the given class + * @param ancestorsAllowedToMap List of parent/ancestors allowed to map to vftables (ie, in multi-virt case, the parents won't get mapped but a common ancestor will) + * @throws CancelledException if cancelled + */ + private void mapVftablesToParents(RecoveredClass recoveredClass, + List ancestorsAllowedToMap) throws CancelledException { + + Map orderToVftableMap = recoveredClass.getOrderToVftableMap(); + List sortedOrder = new ArrayList(orderToVftableMap.keySet()); + Collections.sort(sortedOrder); + + Map parentOrderMap = + 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()); +// } + return; + } + + Iterator orderIterator = sortedOrder.iterator(); + while (orderIterator.hasNext()) { + monitor.checkCanceled(); + Integer order = orderIterator.next(); + Address vftableAddress = orderToVftableMap.get(order); + RecoveredClass parentClass = parentOrderMap.get(order); + recoveredClass.addVftableToBaseClassMapping(vftableAddress, parentClass); + } + + } + + /** + * Method to create a map containing order/parent mappings for the given class, using order they are used in constructor/destructors + * @param recoveredClass the given class + * @return a map containing order/parent mappings for the given class + * @throws CancelledException if cancelled + */ + private Map getParentOrderMap(RecoveredClass recoveredClass, + List parentsWithVfunctions) throws CancelledException { + + Map parentOrderMap = new HashMap(); + + if (recoveredClass.getConstructorOrDestructorFunctions().isEmpty()) { + return parentOrderMap; + } + + // try to get parent order map using constructors/inline constructors + parentOrderMap = getParentOrderMap(recoveredClass, parentsWithVfunctions, true); + if (!parentOrderMap.isEmpty()) { + return parentOrderMap; + } + + // otherwise try to get the map using destructors/inline destructors + parentOrderMap = getParentOrderMap(recoveredClass, parentsWithVfunctions, false); + if (!parentOrderMap.isEmpty()) { + return parentOrderMap; + } + + return parentOrderMap; + } + + /** + * Method to create an order/ancestor map for the given class (usually will be parents but in some cases will be ancestors) + * @param recoveredClass the given class + * @param allowedAncestors List of ancestors (usually parents) that can be added to the map + * @param useConstructors if true, use constructor functions to determine order, if false, use destructor functions + * @return the order/ancestor map of the same size as the number of vftables in the class or an empty map if the correctly sized map cannot be determined + * @throws CancelledException if cancelled + */ + private Map getParentOrderMap(RecoveredClass recoveredClass, + List allowedAncestors, boolean useConstructors) + throws CancelledException { + + Map parentOrderMap = new HashMap(); + + int numVftables = recoveredClass.getVftableAddresses().size(); + + List functionList = new ArrayList(); + if (useConstructors) { + functionList.addAll(recoveredClass.getConstructorList()); + functionList.addAll(recoveredClass.getInlinedConstructorList()); + } + else { + functionList.addAll(recoveredClass.getDestructorList()); + functionList.addAll(recoveredClass.getInlinedDestructorList()); + } + + // return empty map + if (functionList.isEmpty()) { + return parentOrderMap; + } + + Iterator functionIterator = functionList.iterator(); + while (functionIterator.hasNext()) { + + monitor.checkCanceled(); + + Function function = functionIterator.next(); + + parentOrderMap = new HashMap(); + + Map referenceToParentMap = + getReferenceToClassMap(recoveredClass, function); + + Map allowedReferncesToParentMap = + new HashMap(); + + List
classReferences = new ArrayList
(referenceToParentMap.keySet()); + Iterator
classReferenceIterator = classReferences.iterator(); + while (classReferenceIterator.hasNext()) { + + monitor.checkCanceled(); + Address classReferenceAddress = classReferenceIterator.next(); + + // if the address refers to a vftable and that vftable is in the current class then it is not a parent class so do not add to map + Address possibleVftable = getVftableAddress(classReferenceAddress); + + // if not a vftable then it is a function call + if (possibleVftable == null) { + + Function referencedFunction = + extraUtils.getReferencedFunction(classReferenceAddress, true); + if (referencedFunction == null) { + continue; + } + + } + if (possibleVftable != null && + recoveredClass.getVftableAddresses().contains(possibleVftable)) { + continue; + } + + RecoveredClass ancestorClass = referenceToParentMap.get(classReferenceAddress); + if (allowedAncestors.contains(ancestorClass)) { + allowedReferncesToParentMap.put(classReferenceAddress, ancestorClass); + } + } + + // now order the addresses in the map one direction for constructors and the other for destructors + int order = 0; + List
parentReferences = + new ArrayList
(allowedReferncesToParentMap.keySet()); + + if (useConstructors) { + Collections.sort(parentReferences); + } + else { + Collections.sort(parentReferences, Collections.reverseOrder()); + } + + // iterate over the ordered parents and add to the order to parent map + Iterator
parentRefIterator = parentReferences.iterator(); + while (parentRefIterator.hasNext()) { + monitor.checkCanceled(); + Address refAddress = parentRefIterator.next(); + RecoveredClass parentClass = referenceToParentMap.get(refAddress); + parentOrderMap.put(order, parentClass); + order++; + } + + // the size of the resulting ref to parent map should equal the number of vftables in the class + // if not, continue to iterate over more functions + // if so, return the map + if (parentOrderMap.size() == numVftables) { + return parentOrderMap; + } + } + + // return empty map if none of the construtor/destructor functions create the correctly sized map + return parentOrderMap; + } + + /** + * Method to find common inherited ancestors in the given class's parent list + * and replace the parents with that common ancestor with the ancestor + * @param recoveredClass the given class + * @return List containing lowest common ancestor and the removal of the parents with the common ancestor but leaving parents with no common ancestors + * @throws Exception if class has empty class hierarchy list + */ + private List replaceParentsWithCommonAncestor(RecoveredClass recoveredClass) + throws Exception { + + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + + Map> ancestorToCommonChild = + new HashMap>(); + + List parentClasses = + new ArrayList(classHierarchyMap.keySet()); + parentClasses = getClassesWithVFunctions(parentClasses); + + List updatedParentClasses = new ArrayList(parentClasses); + + // now iterate over the direct parents and map that parent to each ancestor on the ancestor with vfunction list + Iterator parentIterator = parentClasses.iterator(); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass parentClass = parentIterator.next(); + List ancestors = + new ArrayList(parentClass.getClassHierarchy()); + ancestors.remove(parentClass); + ancestors = getClassesWithVFunctions(ancestors); + + if (ancestors.isEmpty()) { + continue; + } + + Iterator ancestorIterator = ancestors.iterator(); + while (ancestorIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass ancestor = ancestorIterator.next(); + + List decendentList = ancestorToCommonChild.get(ancestor); + if (decendentList == null) { + List newDecendentList = new ArrayList(); + newDecendentList.add(parentClass); + ancestorToCommonChild.put(ancestor, newDecendentList); + } + else { + if (!decendentList.contains(parentClass)) { + decendentList.add(parentClass); + ancestorToCommonChild.replace(ancestor, decendentList); + } + } + } + + } + + // if the map is empty, return the updated list of parents which contains only + // parents with vfunctions + Set keySet = ancestorToCommonChild.keySet(); + if (keySet.isEmpty()) { + return updatedParentClasses; + } + + // now iterate over the ancestor map and update the parent list by adding any ancestor + // that has common parents and removing those parents from the list + Iterator ancestorsIterator = keySet.iterator(); + while (ancestorsIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestor = ancestorsIterator.next(); + List commonChildList = ancestorToCommonChild.get(ancestor); + if (commonChildList != null && commonChildList.size() >= 2) { + if (!updatedParentClasses.contains(ancestor)) { + + updatedParentClasses.add(ancestor); + + } + updatedParentClasses.removeAll(commonChildList); + } + } + + if (updatedParentClasses.isEmpty()) { + return updatedParentClasses; + } + + Iterator updatedParentsIterator = updatedParentClasses.iterator(); + while (updatedParentsIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestor = updatedParentsIterator.next(); + + // remove if ancestor is an ancestor of any of the others + if (isClassAnAncestorOfAnyOnList(updatedParentClasses, ancestor)) { + updatedParentsIterator.remove(); + } + } + + return updatedParentClasses; + + } + + + + + + + + + + /** + * Method to call create and apply class structures method starting with top parent classes + * and non-virtual classes then the children and their children until all classes are processed. + * @param recoveredClasses List of classes + * @throws CancelledException when cancelled + * @throws Exception if issue creating data + */ + private void createAndApplyClassStructures(List recoveredClasses) + throws CancelledException, Exception { + + List listOfClasses = new ArrayList(recoveredClasses); + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + // first process all the classes with no parents + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + if (recoveredClass.hasMultipleInheritance()) { + continue; + } + + if (recoveredClass.hasParentClass()) { + continue; + } + + if (!recoveredClass.hasVftable()) { + createClassStructureWhenNoParentOrVftable(recoveredClass); + listOfClasses.remove(recoveredClass); + continue; + } + + processDataTypes(recoveredClass); + listOfClasses.remove(recoveredClass); + + } + + // now process the classes that have all parents processed + // continue looping until all classes are processed + int numLoops = 0; + + while (!listOfClasses.isEmpty()) { + monitor.checkCanceled(); + + // put in stop gap measure in case some classes never get all + // parents processed for some reason + if (numLoops == 100) { + return; + } + numLoops++; + + recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + monitor.checkCanceled(); + if (!listOfClasses.contains(recoveredClass)) { + continue; + } + + if (!allAncestorDataHasBeenCreated(recoveredClass)) { + continue; + } + + processDataTypes(recoveredClass); + listOfClasses.remove(recoveredClass); + + } + } + } + + /** + * Method to create all the class data types for the current class, name all the class functions, and put them all into the class namespace + * @param recoveredClass current class + * @throws CancelledException when cancelled + * @throws Exception naming exception + */ + private void processDataTypes(RecoveredClass recoveredClass) + throws CancelledException, Exception { + + if (!recoveredClass.hasVftable()) { + createClassStructureUsingRTTI(recoveredClass, null); + // return in this case because if there is no vftable for a class the script cannot + // identify any member functions so there is no need to process the rest of this method + return; + } + + // create pointers to empty vftable structs so they can be added to the class data type + // then filled in later + Map vfPointerDataTypes = + createEmptyVfTableStructs(recoveredClass); + + // create current class structure and add pointer to vftable, all parent member data strutures, and class member data structure + Structure classStruct = null; + + classStruct = createClassStructureUsingRTTI(recoveredClass, vfPointerDataTypes); + + applyVbtableStructure(recoveredClass); + + // pdb already has good names so only name if no pdb + if (!isPDBLoaded) { + + // Now that we have a class data type + // name constructor and destructor functions and put into the class namespace + addConstructorsToClassNamespace(recoveredClass, classStruct); + addDestructorsToClassNamespace(recoveredClass); + addNonThisDestructorsToClassNamespace(recoveredClass); + addVbaseDestructorsToClassNamespace(recoveredClass); + addVbtableToClassNamespace(recoveredClass); + + // add secondary label on functions with inlined constructors or destructors + createInlinedConstructorComments(recoveredClass); + createInlinedDestructorComments(recoveredClass); + createIndeterminateInlineComments(recoveredClass); + + // add label on constructor destructor functions that could not be determined which were which + createIndeterminateLabels(recoveredClass); + } + + // This is done after the class structure is created and added to the dtmanager + // because if done before the class structures are created + // then empty classes will get auto-created in the wrong place + // when the vfunctions are put in the class + + fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes); + + } + + /** + * Method to create a class structure using information in program RTTI structures + * @param recoveredClass the given class + * @param vfPointerDataTypes map of address/vftable pointer structs + * @return the created class structure data type + * @throws Exception if invalid data creation + */ + + private Structure createClassStructureUsingRTTI(RecoveredClass recoveredClass, + Map vfPointerDataTypes) throws Exception { + + String className = recoveredClass.getName(); + + CategoryPath classPath = recoveredClass.getClassPath(); + + // get either existing structure if prog has a structure created by pdb or computed structure + // from decompiled construtor(s) info + Structure classStructure; + if (recoveredClass.hasExistingClassStructure()) { + classStructure = recoveredClass.getExistingClassStructure(); + } + else { + classStructure = recoveredClass.getComputedClassStructure(); + } + + int structLen = 0; + if (classStructure != null) { + structLen = addAlignment(classStructure.getLength()); + } + + Structure classStructureDataType = + new StructureDataType(classPath, className, structLen, dataTypeManager); + + Data baseClassArrayData = getBaseClassArray(recoveredClass); + + // if cannot recover the base class array return the existing or computed one instead + // so user will at least have some information like correct size and some members + if (baseClassArrayData == null) { + classStructureDataType = + createDefaultStructure(classStructure, classStructureDataType); + return classStructureDataType; + } + + // if lowest level multi-v (not just inherits a multi-v), need to add this first so it isn't overwritten by base class structures + // at same offset + if (recoveredClass.getClassHierarchyMap().size() > 1 && + recoveredClass.hasMultipleVirtualInheritance()) { + classStructureDataType = + addVbtableToClassStructure(recoveredClass, classStructureDataType); + } + + int baseClassOffset = 0; + int numPointers = baseClassArrayData.getNumComponents(); + + for (int i = 0; i < numPointers; ++i) { + monitor.checkCanceled(); + + // Get the base class it is pointing to + Address pointerAddress = baseClassArrayData.getComponent(i).getAddress(); + + Address baseClassDescriptorAddress = + extraUtils.getReferencedAddress(pointerAddress, true); + if (baseClassDescriptorAddress == null) { + continue; + } + + RecoveredClass baseClass = getClassFromBaseClassDescriptor(baseClassDescriptorAddress); + if (i > 0 && baseClass == null) { + continue; + } + + int mdisp = api.getInt(baseClassDescriptorAddress.add(8)); + int pdisp = api.getInt(baseClassDescriptorAddress.add(12)); + int vdisp = api.getInt(baseClassDescriptorAddress.add(16)); + + // skip main class - will fill in its vftable ptr later + if (pdisp == -1 && i == 0) { + continue; + } + + // get the baseClassStructure (ie self or ancestor class) and its displacement values + Structure baseClassStructure = + getClassStructureFromDataTypeManager(baseClass); + if (baseClassStructure == null) { +// println("****recovered Class = " + recoveredClass.getName() + "'s base Class " + +// baseClass.getName() + " class struct is null"); + continue; + } + + // Continue if the class has mult inh but base class is not on the parent list + if (recoveredClass.hasMultipleInheritance() && + !recoveredClass.getParentList().contains(baseClass)) { + continue; + } + + // process the non-virtually inherited ones + if (pdisp == -1) { + + baseClassOffset = mdisp; + + if (recoveredClass.hasMultipleInheritance() && + !recoveredClass.getParentList().contains(baseClass)) { + continue; + } + + // if it is a direct parent and the structure exists add it to the structure in the mdisp offset + // if class has virtual inheritance then copy individual members from the non-virtual parent struct + // into the struct + if (recoveredClass.hasSingleInheritance() && + recoveredClass.getVftableAddresses().size() > 1 && + recoveredClass.inheritsVirtualAncestor()) { + + int offsetOfVirtualParent = + getOffsetOfVirtualParent(recoveredClass, baseClassStructure); + + int dataLength; + if (offsetOfVirtualParent == NONE) { + dataLength = baseClassStructure.getLength(); + } + else { + int lengthOfVirtualParent = + baseClassStructure.getLength() - offsetOfVirtualParent; + dataLength = baseClassStructure.getLength() - lengthOfVirtualParent; + } + + if (structUtils.canAdd(classStructureDataType, baseClassOffset, + dataLength, monitor)) { + classStructureDataType = + addIndividualComponentsToStructure(classStructureDataType, + baseClassStructure, baseClassOffset, offsetOfVirtualParent); + } + continue; + } + + // else copy whole baseClass structure to the class Structure + if (structUtils.canAdd(classStructureDataType, baseClassOffset, + baseClassStructure.getLength(), monitor)) { + classStructureDataType = structUtils.addDataTypeToStructure( + classStructureDataType, baseClassOffset, baseClassStructure, + baseClassStructure.getName(), monitor); + + } + + } + else { + // else need to fill in the virtually inherited ones + // get the offset of this base class in the class using the vbtable + Address vbtableAddress = recoveredClass.getVbtableAddress(); + if (vbtableAddress == null) { + continue; + } + + baseClassOffset = api.getInt(recoveredClass.getVbtableAddress().add(vdisp)) + pdisp; + + if (structUtils.canAdd(classStructureDataType, baseClassOffset, + baseClassStructure.getLength(), monitor)) { + + classStructureDataType = structUtils.addDataTypeToStructure( + classStructureDataType, baseClassOffset, baseClassStructure, + baseClassStructure.getName(), monitor); + + } + } + + }// end of base class array + + if (vfPointerDataTypes != null) { + if (!isClassOffsetToVftableMapComplete(recoveredClass)) { +// println("class vftable offset map for " + recoveredClass.getName() + +// " is not complete"); + } + + // iterate over the set of offsets to vftables for the class and if nothing + // is already at the offset, add the vftables + Map classOffsetToVftableMap = + recoveredClass.getClassOffsetToVftableMap(); + Set classVftableOffsets = classOffsetToVftableMap.keySet(); + List sortedOffsets = new ArrayList(classVftableOffsets); + Collections.sort(sortedOffsets); + + Integer offset = sortedOffsets.get(0); + + Address vftableAddress = classOffsetToVftableMap.get(offset); + + DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress); + + if (structUtils.canAdd(classStructureDataType, offset.intValue(), + classVftablePointer.getLength(), monitor)) { + classStructureDataType = structUtils.addDataTypeToStructure( + classStructureDataType, offset.intValue(), classVftablePointer, + CLASS_VTABLE_PTR_FIELD_EXT, monitor); + + } + } + + // add the vbtable structure for single inheritance/virt parent case + if (recoveredClass.hasSingleInheritance() && recoveredClass.inheritsVirtualAncestor()) { + classStructureDataType = + addVbtableToClassStructure(recoveredClass, classStructureDataType); + } + + int dataOffset = getDataOffset(recoveredClass, classStructureDataType); + int dataLen = UNKNOWN; + if (dataOffset != NONE) { + dataLen = structUtils.getNumberOfUndefinedsStartingAtOffset( + classStructureDataType, dataOffset, monitor); + } + + if (dataLen != UNKNOWN && dataLen > 0) { + + Structure recoveredClassDataStruct = + createClassMemberDataStructure(recoveredClass, + classStructureDataType, dataLen, dataOffset); + + if (recoveredClassDataStruct != null) { + classStructureDataType = structUtils.addDataTypeToStructure( + classStructureDataType, dataOffset, recoveredClassDataStruct, "data", monitor); + } + + } + + if (classStructureDataType.getNumComponents() == classStructureDataType.getNumDefinedComponents()) { + classStructureDataType.setInternallyAligned(true); + } + + classStructureDataType.setDescription( + createParentStringBuffer(recoveredClass).toString()); + + classStructureDataType = (Structure) dataTypeManager.addDataType(classStructureDataType, + DataTypeConflictHandler.DEFAULT_HANDLER); + + return classStructureDataType; + } + + /** + * Method to retrieve the given class's base class array data type from the RTTI data + * @param recoveredClass the given class + * @return the base class array data type or null + * @throws CancelledException when cancelled + */ + private Data getBaseClassArray(RecoveredClass recoveredClass) throws CancelledException { + + List baseClassArray = extraUtils.getListOfSymbolsByNameInNamespace( + RTTI_BASE_CLASS_ARRAY_LABEL, recoveredClass.getClassNamespace(), false); + + if (baseClassArray.size() != 1) { + return null; + } + + Address baseClassArrayAddress = baseClassArray.get(0).getAddress(); + Data baseClassArrayData = api.getDataAt(baseClassArrayAddress); + if (!baseClassArrayData.isArray()) { + return null; + } + + return baseClassArrayData; + } + + /** + * Retrieve the RecoveredClass object that corresponds to the one in the same namespace as the given RTTIBaseClassDescriptor address + * @param baseClassDescriptorAddress the address of the pointer to the RTTIBaseClassDescriptor structure + * @return the corresponding RecoveredClass object or null if it cannot be retrieved + * @throws MemoryAccessException if memory cannot be read + */ + private RecoveredClass getClassFromBaseClassDescriptor(Address baseClassDescriptorAddress) + throws MemoryAccessException { + + Symbol baseClassDescSymbol = symbolTable.getPrimarySymbol(baseClassDescriptorAddress); + if (baseClassDescSymbol == null) { + return null; + } + Namespace namespace = baseClassDescSymbol.getParentNamespace(); + if (namespace.equals(globalNamespace)) { + return null; + } + RecoveredClass baseClass = getClass(namespace); + + return baseClass; + } + + /** + * Method to add a pointer to the class vbtable to the given class's class structure + * @param recoveredClass the given class + * @param classStructureDataType the given class's class structure data type + * @return the updated class structure + * @throws CancelledException if cancelled + */ + private Structure addVbtableToClassStructure(RecoveredClass recoveredClass, + Structure classStructureDataType) throws CancelledException { + + Structure vbtableStructure = recoveredClass.getVbtableStructure(); + + if (vbtableStructure != null) { + + int vbtableOffset = recoveredClass.getVbtableOffset(); + + DataType vbaseStructPointer = dataTypeManager.getPointer(vbtableStructure); + + int dataLength = vbaseStructPointer.getLength(); + if (structUtils.canAdd(classStructureDataType, vbtableOffset, dataLength, monitor)) { + + classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType, + vbtableOffset, vbaseStructPointer, "vbtablePtr", monitor); + + } + } + return classStructureDataType; + } + + + /** + * Method to apply the given class's vbtable structure + * @param recoveredClass the given RecoveredClass object which, if applicable, contains the address and structure to apply + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws CancelledException if cancelled + * @throws Exception if issue creating data + */ + private void applyVbtableStructure(RecoveredClass recoveredClass) + throws CancelledException, AddressOutOfBoundsException, Exception { + + Address vbtableAddress = recoveredClass.getVbtableAddress(); + if (vbtableAddress == null) { + return; + } + + Structure vbtableStructure = recoveredClass.getVbtableStructure(); + + api.clearListing(vbtableAddress, vbtableAddress.add(vbtableStructure.getLength())); + api.createData(vbtableAddress, vbtableStructure); + + api.setPlateComment(vbtableAddress, + recoveredClass.getClassNamespace().getName(true) + "::vbtable"); + } + + + +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java new file mode 100644 index 0000000000..ac9b0ef1f8 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -0,0 +1,1450 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// PROTOTYPE Script to recover class information using RTTI structures. +// Note: This script does not compile on versions of Ghidra prior to 9.2 or in 9.2.1. +// This script uses information found in RTTI structures to figure out +// class hierarchy, inheritance types, constructors and destructors, class data types, and more. +// If program has pdb information, it uses it to help fill in class structures with known names +// and types for class member data. +// If program does not have pdb, or incomplete pdb data types, it uses the decompiler pcode store +// information to fill in class structures with assumed data types for class member data. These +// data types are not always complete and may be filled in with undefined place holders of +// the same size as the associated data. +// There are many options that can be changed by editing flags at the top of the script. +// There are options to find missing functions that have not been disassembled, print or output +// class information in various formats, and to graph the class hierarchies. +// To best see the results of this script, look in the SymbolTree in the Classes folder. If a class +// has RTTI, you will see the class functions, vftable(s), and more there. Click on class functions +// and look at them in the decompiler to see how the class data types have improved the decompiler +// results. To see class data definitions, either hover on the this param in the decompiler or look +// in the DataTypeManager//ClassDataTypes/ folder. +// NOTE: As this is a prototype script, the location, names, and design of data types created by +// this script and default vfunctions named by this script are likely to change in the future +// once an official design for Object Oriented representation is determined. +// NOTE: Windows class recovery is more complete and tested than gcc class recovery, which is still +// in early stages of development. Gcc class data types have not been recovered yet but if the program +// has DWARF, there will be some amount of data recovered by the DWARF analyzer in the DWARF data folder. +//@category C++ + +import java.io.File; +import java.io.PrintWriter; +import java.util.*; +import java.util.stream.Collectors; + +import ghidra.app.decompiler.DecompInterface; +import ghidra.app.plugin.core.analysis.DecompilerFunctionAnalyzer; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.Analyzer; +import ghidra.app.services.GraphDisplayBroker; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.options.Options; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Parameter; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.service.graph.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.GraphException; +import ghidra.util.task.TaskMonitor; + +public class RecoverClassesFromRTTIScript extends GhidraScript { + + // print c-like class definitions to the console + private static final boolean PRINT_CLASS_DEFINITIONS = false; + + // print class information (parent class, children classes, vfunctions, member data) to console + private static final boolean PRINT_CLASS_INFO = false; + + // print class parent(s) and children classes to console + private static final boolean PRINT_CLASS_PARENTS_AND_CHILDREN = false; + + // print one line hierarchy for each class with no children (child : parent : grandparent: ...) + // if multiple inheritance print multiple parents on new line directly below end of their child + private static final boolean PRINT_CLASS_HIERARCHIES = false; + + // these do the same as above but print to a file + private static final boolean OUTPUT_CLASS_DEFINITIONS = false; + private static final boolean OUTPUT_CLASS_INFO = false; + private static final boolean OUTPUT_CLASS_PARENTS_AND_CHILDREN = false; + private static final boolean OUTPUT_SIMPLE_CLASS_HIERARCHIES = false; + + // print counts of found items + private static final boolean PRINT_COUNTS = true; + + // recommended to find missing RTTI structure info in older programs and to find any missing + // potential constructor destructor functions because analysis did not make them functions yet. + // They are either undefined bytes or code that is not in a function. + private static final boolean FIXUP_PROGRAM = true; + + // bookmark all constructor/destructor functions figured out by this script + private static final boolean BOOKMARK_FOUND_FUNCTIONS = true; + + // show a graph of class hierarchies after script is complete + // no parent = blue vertex + // single parent = green vertex + // multiple parents = red vertex + // edge between child and parent is orange if child inherits the parent virtually + // edge between child and parent is lime green if child inherits the parent non-virtually + private static final boolean GRAPH_CLASS_HIERARCHIES = false; + + // show shortened class template names in class structure field names + private static final boolean USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS = true; + + private static final String CLASS_DATA_STRUCT_NAME = "_data"; + + private static final String CONSTRUCTOR_BOOKMARK = "CONSTRUCTOR"; + private static final String DESTRUCTOR_BOOKMARK = "DESTRUCTOR"; + + private static final String INDETERMINATE_BOOKMARK = "INDETERMINATE"; + + boolean programHasRTTIApplied = false; + boolean isPDBLoaded; + boolean isGcc = false; + boolean isWindows = false; + String ghidraVersion = null; + + DecompilerScriptUtils decompilerUtils; + DataTypeManager dataTypeManager; + + int defaultPointerSize; + + RecoveredClassUtils classUtils; + + RTTIClassRecoverer recoverClassesFromRTTI; + + ExtraScriptUtils extraUtils; + + boolean nameVfunctions = false; + + @Override + public void run() throws Exception { + + String errorMsg = validate(); + if (!errorMsg.isEmpty()) { + println(errorMsg); + return; + } + + + if (isWindows()) { + + // TODO: check for typeinfo using the other way i had then pull the hasRTTI in and if first + // is true and second isn't then run the analyzer - move all this into a method + isPDBLoaded = isPDBLoadedInProgram(); + nameVfunctions = !isPDBLoaded; + recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram, + currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, + USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor); + + } + else if (isGcc()) { + + // for now assume gcc has named vfunctions + nameVfunctions = true; + recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation, + state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, + USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, monitor); + + + } + else { + println("This script will not work on this program type"); + return; + } + + if (!recoverClassesFromRTTI.containsRTTI()) { + println( + "This program does not appear to contain any processed RTTI information. Either it does not contain any or the RTTI Analyzer was not run."); + return; + } + + // possibly more picky subtype than just checking windows/gcc + if (!recoverClassesFromRTTI.isValidProgramType()) { + println("This script will not work on this program type"); + return; + } + + if (!recoverClassesFromRTTI.isValidProgramSize()) { + println("This program is not a valid program address size."); + return; + } + + + + + decompilerUtils = recoverClassesFromRTTI.getDecompilerUtils(); + DecompInterface decompInterface = decompilerUtils.getDecompilerInterface(); + + if (decompInterface.getProgram() == null) { + println("The decompiler interface cannot open the current program: " + + decompInterface.getLastMessage()); + return; + } + + defaultPointerSize = recoverClassesFromRTTI.getDefaultPointerSize(); + dataTypeManager = recoverClassesFromRTTI.getDataTypeManager(); + + PrintWriter out = null; + + boolean doOutput = shouldDoOutput(); + + if (doOutput) { + File outputFile = askFile("Choose Output File", "Please choose an output file"); + out = new PrintWriter(outputFile); + } + + if (FIXUP_PROGRAM) { + println( + "Checking for missing RTTI information and undefined constructor/destructor functions and creating if possible " + + "to find entry point..."); + AddressSetView beforeScriptChanges = currentProgram.getChanges().getAddressSet(); + recoverClassesFromRTTI.fixUpProgram(); + analyzeProgramChanges(beforeScriptChanges); + } + + println("Recovering classes using RTTI..."); + List recoveredClasses = recoverClassesFromRTTI.createRecoveredClasses(); + + if (recoveredClasses == null) { + println("Error recovering classes"); + return; + } + + if (recoveredClasses.isEmpty()) { + println("No new classes recovered."); + if (doOutput) { + out.close(); + } + return; + } + + println("Identified " + recoveredClasses.size() + " classes to process and " + + getNumberOfConstructorsOrDestructors(recoveredClasses) + + " class member functions to assign."); + + + if (!isPDBLoaded) { + + if (BOOKMARK_FOUND_FUNCTIONS) { + bookmarkFunctions(recoveredClasses); + println("See Bookmark Manager for a list of functions by type."); + } + } + + callOptionalOutputMethods(recoveredClasses, out); + + if (doOutput) { + out.close(); + } + + if (GRAPH_CLASS_HIERARCHIES) { + AttributedGraph graph = createGraph(recoveredClasses); + showGraph(graph); + } + + + decompilerUtils.disposeDecompilerInterface(); + } + + /** + * Method to determine if pdb info has been applied to the program + * @return true if pdb info has been applied to program + */ + private boolean isPDBLoadedInProgram() { + + Options options = currentProgram.getOptions("Program Information"); + isPDBLoaded = false; + Object isPDBLoadedObject = options.getObject("PDB Loaded", null); + if (isPDBLoadedObject != null) { + isPDBLoaded = (boolean) isPDBLoadedObject; + } + return isPDBLoaded; + } + + public String validate() { + + if (currentProgram == null) { + return ("There is no open program"); + } + + if (!checkGhidraVersion()) { + return ("This script only works with Ghidra version 9.2, 9.2.2 and later. It does not work on Ghidra 9.2.1 or on versions prior to 9.2"); + } + + if (!isGcc() && !isWindows()) { + return ("This script only handles Windows and gcc programs"); + + } + + defaultPointerSize = currentProgram.getDefaultPointerSize(); + if (defaultPointerSize != 4 && defaultPointerSize != 8) { + return ("This script only works on 32 or 64 bit programs"); + } + return new String(); + } + + private void analyzeProgramChanges(AddressSetView beforeChanges) throws Exception { + + AddressSetView addressSet = currentProgram.getChanges().getAddressSet(); + addressSet = addressSet.subtract(beforeChanges); + if (!addressSet.isEmpty()) { + println("analyzing program changes ..."); + setAnalysisOption(currentProgram, "Decompiler Parameter ID", "true"); + analyzeChanges(currentProgram); + addressSet = currentProgram.getChanges().getAddressSet(); + analyzeChangesWithDecompilerFunctionAnalyzer( + currentProgram.getChanges().getAddressSet()); + } + } + + @Override + public AnalysisMode getScriptAnalysisMode() { + + return AnalysisMode.SUSPENDED; + + } + + + /** + * Method to create a class hierarchy graph where the parents are at the top of the graph and + * the children at the bottom. Classes with no parents have blue nodes. Classes with a single + * parent have green nodes. Classes with multiple parents have red nodes. Classes with virtual + * inheritance have orange edges between parent and child. Classes with non-virtual inheritance + * have lime green edges between parent and child. + * @param recoveredClasses the list of classes + * @return a hierarchy graph for the given list of classes + * @throws CancelledException if cancelled + */ + private AttributedGraph createGraph(List recoveredClasses) + throws CancelledException { + + AttributedGraph g = new AttributedGraph(); + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + AttributedVertex classVertex = g.addVertex(recoveredClass.getName()); + + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + + // no parent = blue vertex + if (classHierarchyMap.isEmpty()) { + classVertex.setAttribute("Color", "Blue"); + continue; + } + + Set parents = classHierarchyMap.keySet(); + + // single parent = green vertex + if (parents.size() == 1) { + classVertex.setAttribute("Color", "Green"); + } + // multiple parents = red vertex + else { + classVertex.setAttribute("Color", "Red"); + } + + Map parentToBaseTypeMap = + recoveredClass.getParentToBaseTypeMap(); + + Iterator parentIterator = parents.iterator(); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parent = parentIterator.next(); + + AttributedVertex parentVertex = g.addVertex(parent.getName()); + + AttributedEdge edge = g.addEdge(parentVertex, classVertex); + + Boolean isVirtualParent = parentToBaseTypeMap.get(parent); + if (isVirtualParent == null) { + continue; + } + + // edge between child and parent is orange if child inherits the parent virtually + if (isVirtualParent) { + edge.setAttribute("Color", "Orange"); + } + // else edge between child and parent is lime green if child inherits the parent non-virtually + + } + } + + return g; + } + + /** + * Method to display the given graph + * @param graph the given graph + * @throws GraphException if the graph service cannot get the graph display + * @throws CancelledException if drawing the graph is cancelled + */ + private void showGraph(AttributedGraph graph) throws GraphException, CancelledException { + + GraphDisplay display; + PluginTool tool = state.getTool(); + GraphDisplayBroker broker = tool.getService(GraphDisplayBroker.class); + GraphDisplayProvider service = broker.getGraphDisplayProvider("Default Graph Display"); + display = service.getGraphDisplay(false, TaskMonitor.DUMMY); + display.setGraph(graph, "test graph", false, TaskMonitor.DUMMY); + } + + /** + * Method to determine, based on the script's output settings, if the script will do any output + * @return true if the script will do output, false otherwise + */ + private boolean shouldDoOutput() { + + boolean doOutput = OUTPUT_CLASS_DEFINITIONS || OUTPUT_CLASS_INFO || + OUTPUT_CLASS_PARENTS_AND_CHILDREN || OUTPUT_SIMPLE_CLASS_HIERARCHIES; + + return doOutput; + } + + + private void printClassHierarchyLists(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List classHierarchyList = recoveredClass.getClassHierarchy(); + Iterator classHierarchyIterator = classHierarchyList.iterator(); + while (classHierarchyIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass currentClass = classHierarchyIterator.next(); + println(currentClass.getName()); + } + + println("***"); + } + } + + + + + /** + * Script only works on versions of ghidra after 9.2, but not 9.2.1 because a method was + * accidentally removed from FillOutStructureCmd that is needed + * @return true if script will work and false otherwise + */ + private boolean checkGhidraVersion() { + + ghidraVersion = getVersionOfGhidra(); + + if (ghidraVersion.compareTo("9.3") <= 0 && !ghidraVersion.equals("9.2.1")) { + return true; + } + return false; + } + + /** + * Method to set the global variable isGcc + */ + private boolean isGcc() { + + isGcc = + currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase( + "gcc"); + return isGcc; + } + + /** + * Method to set the global variable isWindows + */ + private boolean isWindows() { + + String compilerID = + currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase(); + isWindows = compilerID.contains("windows"); + return isWindows; + } + + + + /** + * Method to determine if somehow the constructor list and destructor list for a class contain + * overlapping functions + * @param recoveredClass the given class + * @return true if there is a discrepancy in the constructor/destructor lists + */ + private boolean hasConstructorDestructorDiscrepancy(RecoveredClass recoveredClass) { + + List allClassConstructors = + recoverClassesFromRTTI.getAllClassConstructors(recoveredClass); + List allClassDestructors = + recoverClassesFromRTTI.getAllClassDestructors(recoveredClass); + + List commonFunctions1 = + allClassConstructors.stream().distinct().filter(allClassDestructors::contains).collect( + Collectors.toList()); + + List commonFunctions2 = + allClassDestructors.stream().distinct().filter(allClassConstructors::contains).collect( + Collectors.toList()); + + if (commonFunctions1.isEmpty() && commonFunctions2.isEmpty()) { + return false; + } + return true; + } + + + + //TODO: call this before create data in debug mode from script + private void findClassesWithErrors(List recoveredClasses) + throws CancelledException { + + Iterator iterator = recoveredClasses.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = iterator.next(); + if (hasConstructorDestructorDiscrepancy(recoveredClass)) { + println(recoveredClass.getName() + " has function on both c and d lists"); + } + } + } + + + + + /** + * Method to analyze the program changes with the decompiler parameter ID analyzer + * @param set the set of addresses to analyze + * @throws Exception if the analyzer throws an exception + */ + private void analyzeChangesWithDecompilerFunctionAnalyzer(AddressSetView set) throws Exception { + + Analyzer analyzer = new DecompilerFunctionAnalyzer(); + analyzer.added(currentProgram, set, monitor, new MessageLog()); + } + + + + /** + * Get the version of Ghidra that was used to analyze this program + * @return a string containing the version number of Ghidra used to analyze the current program + */ + private String getVersionOfGhidra() { + + Options options = currentProgram.getOptions("Program Information"); + Object ghidraVersionObject = options.getObject("Created With Ghidra Version", null); + + return ghidraVersionObject.toString(); + } + + + /** + * Method to bookmark all of the constructor/destructor/indeterminate functions + * @param recoveredClasses List of classes + * @throws CancelledException if cancelled + */ + private void bookmarkFunctions(List recoveredClasses) + throws CancelledException { + bookmarkConstructors(recoveredClasses); + bookmarkDestructors(recoveredClasses); + bookmarkRemainingIndeterminateConstructorsAndDestructors(recoveredClasses); + } + + + /** + * Method to print class hierarchy of the form child : parent: grandparent : etc... + * @param stringBuffer the buffer to add the newly created string to + * @param recoveredClass the current class to process + * @throws CancelledException when cancelled + */ + private StringBuffer getSimpleClassHierarchyString(StringBuffer stringBuffer, + RecoveredClass recoveredClass) throws CancelledException { + + // print class + stringBuffer.append(recoveredClass.getName()); + + if (!recoveredClass.hasParentClass()) { + return stringBuffer; + } + + // if class has parents, print parents in order + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + + List parents = new ArrayList(classHierarchyMap.keySet()); + + // if single inheritance - simple linear case + if (recoveredClass.hasSingleInheritance()) { + stringBuffer.append(" : "); + getSimpleClassHierarchyString(stringBuffer, parents.get(0)); + } + // otherwise have to split into various lines for the multiple parents + else { + Iterator parentIterator = classHierarchyMap.keySet().iterator(); + stringBuffer.append(" : "); + int lastColon = stringBuffer.lastIndexOf(":"); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parentClass = parentIterator.next(); + if (classHierarchyMap.size() == 1) { + //stringBuffer.append(" : "); + getSimpleClassHierarchyString(stringBuffer, parentClass); + continue; + } + + stringBuffer.append("\r\n"); + + //int lastColon = stringBuffer.lastIndexOf(":"); + for (int i = 0; i <= lastColon; i++) { + monitor.checkCanceled(); + stringBuffer.append(" "); + } + getSimpleClassHierarchyString(stringBuffer, parentClass); + } + + } + + return stringBuffer; + + } + + + + + /** + * Method to retrieve the AddressSet of the current program's initialized memory + * @return the AddressSet of the current program's initialized memory + * @throws CancelledException if cancelled + */ + private AddressSet getInitializedMemory() throws CancelledException { + + AddressSet dataAddresses = new AddressSet(); + MemoryBlock[] blocks = currentProgram.getMemory().getBlocks(); + + for (MemoryBlock block : blocks) { + monitor.checkCanceled(); + + if (block.isInitialized()) { + dataAddresses.add(block.getStart(), block.getEnd()); + } + } + return dataAddresses; + } + + + + /** + * Method to bookmark found constructor functions + * @param recoveredClasses List of classes + * @throws CancelledException if cancelled + */ + private void bookmarkConstructors(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassesIterator = recoveredClasses.iterator(); + + while (recoveredClassesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassesIterator.next(); + bookmarkFunctionsOnList(recoveredClass.getConstructorList(), CONSTRUCTOR_BOOKMARK); + } + } + + /** + * Method to bookmark found constructor functions + * @param recoveredClasses List of classes + * @throws CancelledException if cancelled + */ + private void bookmarkDestructors(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassesIterator = recoveredClasses.iterator(); + + while (recoveredClassesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassesIterator.next(); + bookmarkFunctionsOnList(recoveredClass.getDestructorList(), DESTRUCTOR_BOOKMARK); + bookmarkFunctionsOnList(recoveredClass.getNonThisDestructors(), DESTRUCTOR_BOOKMARK); + } + } + + /** + * Method to bookmark indeterminate constructor/destructor functions + * @param recoveredClasses List of classes + * @throws CancelledException if cancelled + */ + private void bookmarkRemainingIndeterminateConstructorsAndDestructors( + List recoveredClasses) throws CancelledException { + + Iterator recoveredClassesIterator = recoveredClasses.iterator(); + + while (recoveredClassesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassesIterator.next(); + + bookmarkFunctionsOnList(recoveredClass.getIndeterminateList(), INDETERMINATE_BOOKMARK); + } + } + + + /** + * Method to add/append analysis bookmarks with the given comment to the given list of functions + * @param functions List of functions + * @param comment the bookmark comment to add + * @throws CancelledException when script is cancelled + */ + private void bookmarkFunctionsOnList(List functions, String comment) + throws CancelledException { + + if (functions.size() == 0) { + return; + } + + Iterator functionIterator = functions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); + Function function = functionIterator.next(); + Address address = function.getEntryPoint(); + recoverClassesFromRTTI.bookmarkAddress(address, comment); + } + } + + + /** + * Method to optionally print to console or output to file various types of class information + * depending on the options set at top of script + * @param recoveredClasses List of classes + * @param out output mechanism + * @throws CancelledException if cancelled + */ + private void callOptionalOutputMethods(List recoveredClasses, PrintWriter out) + throws CancelledException { + + if (PRINT_COUNTS) { + printCounts(recoveredClasses); + } + + if (PRINT_CLASS_HIERARCHIES) { + printClassHierarchiesFromLowestChildren(recoveredClasses); + } + + if (PRINT_CLASS_DEFINITIONS) { + printClassDefinitions(recoveredClasses); + } + + if (PRINT_CLASS_PARENTS_AND_CHILDREN) { + printClassesParentsAndChilren(recoveredClasses); + } + + if (PRINT_CLASS_INFO) { + printClassInfo(recoveredClasses); + } + + if (OUTPUT_CLASS_DEFINITIONS) { + outputClassDefinitions(recoveredClasses, out); + } + if (OUTPUT_CLASS_INFO) { + outputClassInfo(recoveredClasses, out); + } + + if (OUTPUT_CLASS_PARENTS_AND_CHILDREN) { + outputClassParentsAndChildren(recoveredClasses, out); + } + if (OUTPUT_SIMPLE_CLASS_HIERARCHIES) { + outputSimpleClassHierarchies(recoveredClasses, out); + } + + } + + /** + * Method to print class definitions given information discovered about each class. + * Start with top parents and recurse over their children + * @param recoveredClasses List of classes + * @throws CancelledException when cancelled + */ + private void printClassDefinitions(List recoveredClasses) + throws CancelledException { + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + if (!recoveredClass.hasParentClass()) { + println(createClassDefinitionString(recoveredClass).toString()); + } + } + } + + + private void outputClassDefinitions(List recoveredClasses, PrintWriter out) + throws CancelledException { + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + if (!recoveredClass.hasParentClass()) { + out.append(createClassDefinitionString(recoveredClass)); + } + } + + + } + + /** + * Method to print class info for each of the classes on the given list, starting with classes with no parents then + * recursively printing infor for their child classes + * @param recoveredClasses the list of classes + * @throws CancelledException if cancelled + */ + private void printClassInfo(List recoveredClasses) throws CancelledException { + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + if (!recoveredClass.hasParentClass()) { + println(createClassInfoString(recoveredClass).toString()); + } + } + } + + private void printClassParents(List recoveredClasses) + throws CancelledException { + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + String printString = new String("\n" + recoveredClass.getName() + "\n"); + if (recoveredClass.hasParentClass()) { + List parentList = recoveredClass.getParentList(); + Iterator parentIterator = parentList.iterator(); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parent = parentIterator.next(); + printString = printString.concat("\t" + parent.getName() + "\n"); + } + } + println(printString); + } + } + + + /** + * Method to print class hierarchies for the given list of classes starting with the lowest child classes in each family of classes + * @param recoveredClasses the list of classes + * @throws CancelledException if cancelled + */ + private void printClassHierarchiesFromLowestChildren( + List recoveredClasses) throws CancelledException { + + + StringBuffer wholeBuffer = new StringBuffer(); + wholeBuffer.append("\r\n"); + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + if (!recoveredClass.hasChildClass()) { + + StringBuffer stringBuffer = new StringBuffer(); + + wholeBuffer.append( + getSimpleClassHierarchyString(stringBuffer, recoveredClass).toString() + + "\r\n\r\n"); + } + } + println(wholeBuffer.toString()); + + } + + /** + * Method to output simple class hierarchies for the given classes to the given output writer + * @param recoveredClasses the list of classes + * @param out the output writer + * @throws CancelledException if cancelled + */ + private void outputSimpleClassHierarchies(List recoveredClasses, + PrintWriter out) throws CancelledException { + + StringBuffer wholeBuffer = new StringBuffer(); + wholeBuffer.append("\r\n"); + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + if (!recoveredClass.hasChildClass()) { + StringBuffer stringBuffer = new StringBuffer(); + wholeBuffer.append( + getSimpleClassHierarchyString(stringBuffer, recoveredClass).toString() + + "\r\n"); + } + } + out.append(wholeBuffer); + } + + /** + * Method to output class info for the given list of classes + * @param recoveredClasses the list of classes + * @param out the output writer + * @throws CancelledException if cancelled + */ + private void outputClassInfo(List recoveredClasses, PrintWriter out) + throws CancelledException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + if (!recoveredClass.hasParentClass()) { + out.append(createClassInfoString(recoveredClass).toString()); + } + } + } + + + /** + * Method to print counts of various class items for the given classes, such as number of constructors, destructors, etc... + * @param recoveredClasses list of classes + * @throws CancelledException if cancelled + */ + private void printCounts(List recoveredClasses) throws CancelledException { + + + println("Total number of constructors: " + + recoverClassesFromRTTI.getNumberOfConstructors(recoveredClasses)); + println("Total number of inlined constructors: " + + getNumberOfInlinedConstructors(recoveredClasses)); + println( + "Total number of destructors: " + + recoverClassesFromRTTI.getNumberOfDestructors(recoveredClasses)); + println("Total number of inlined destructors: " + + recoverClassesFromRTTI.getNumberOfInlineDestructors(recoveredClasses)); + println( + "Total number of virtual functions: " + + recoverClassesFromRTTI.getNumberOfVirtualFunctions(recoveredClasses)); + println("Total number of virtual functions that are deleting destructors: " + + recoverClassesFromRTTI.getNumberOfDeletingDestructors(recoveredClasses)); + + println("Total number of virtual functions that are clone functions: " + + recoverClassesFromRTTI.getNumberOfCloneFunctions(recoveredClasses)); + + println("Total number of virtual functions that are vbase_destructors: " + + recoverClassesFromRTTI.getNumberOfVBaseFunctions(recoveredClasses)); + + List remainingIndeterminates = + recoverClassesFromRTTI.getRemainingIndeterminates(recoveredClasses); + println("Total number of indetermined constructor/destructors: " + + remainingIndeterminates.size()); + + //TODO: need to get from the new class +// println("Total fixed incorrect FID functions: " + badFIDFunctions.size()); +// println("Total resolved functions that had multiple FID possiblities: " + +// resolvedFIDFunctions.size()); +// println("Total fixed functions that had incorrect data types due to incorrect FID: " + +// fixedFIDFunctions.size()); + + } + + + /** + * Method to get the total number of + * @param recoveredClasses list of classes + * @return the total number of constructors and destructors in the given list of classes + * @throws CancelledException if cancelled + */ + private int getNumberOfConstructorsOrDestructors(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + List constructorList = recoveredClass.getConstructorOrDestructorFunctions(); + total += constructorList.size(); + } + return total; + } + + /** + * Method to get the total number of inlined constructors in the given list of classes + * @param recoveredClasses list of classes + * @return total number of inlined constructors in the given list of classes + * @throws CancelledException if cancelled + */ + private int getNumberOfInlinedConstructors(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + List inlineList = recoveredClass.getInlinedConstructorList(); + total += inlineList.size(); + } + return total; + } + + /** + * Method to print the given list of addresses + * @param addresses the list of addresses to print + * @throws CancelledException if cancelled + */ + private void printAddresses(List
addresses) throws CancelledException { + Iterator
iterator = addresses.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + println(iterator.next().toString()); + } + } + + + /** + * Method to output the class, it's parents and it's children for each of the listed classes + * @param recoveredClasses the given classes + * @param out the output writer + * @throws CancelledException if cancelled + */ + private void outputClassParentsAndChildren(List recoveredClasses, + PrintWriter out) throws CancelledException { + + Iterator iterator = recoveredClasses.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = iterator.next(); + out.append(printClassParentsandChildren(recoveredClass)); + } + } + + /** + * Method to print the class, it's parents and it's children for each of the listed classes + * @param recoveredClasses the given classes + * @throws CancelledException if cancelled + */ + private void printClassesParentsAndChilren(List recoveredClasses) + throws CancelledException { + + Iterator iterator = recoveredClasses.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = iterator.next(); + println(printClassParentsandChildren(recoveredClass).toString()); + } + } + + /** + * Method to get formatted string containing the given class, it's parents and it's children + * @param recoveredClass the given classes + * @return StringBuffer containing the formatted string containing the given class, it's parents and it's children + * @throws CancelledException if cancelled + */ + private StringBuffer printClassParentsandChildren(RecoveredClass recoveredClass) + throws CancelledException { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("\r\n"); + stringBuffer.append("\r\n"); + + stringBuffer.append("*** Recovered Class: " + recoveredClass.getName() + " ***\r\n"); + stringBuffer.append("\r\n"); + + // print parent classes + stringBuffer.append("parent class(es):\r\n"); + if (recoveredClass.hasParentClass()) { + Set keySet = recoveredClass.getClassHierarchyMap().keySet(); + Iterator parentIterator = keySet.iterator(); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parent = parentIterator.next(); + stringBuffer.append("\t" + parent.getName() + "\r\n"); + } + } + + // print child classes + stringBuffer.append("\r\n"); + stringBuffer.append("child class(es):\r\n"); + if (recoveredClass.hasChildClass()) { + List childClasses = recoveredClass.getChildClasses(); + Iterator childClassIterator = childClasses.iterator(); + while (childClassIterator.hasNext()) { + monitor.checkCanceled(); + stringBuffer.append("\t" + childClassIterator.next().getName() + "\r\n"); + } + + } + return stringBuffer; + + } + + /** + * Method to create a string containing class info for the given class including parents, children, constructors, destructors + * inlined constructors, inlined destructors, member functions, member data and the same info for each child class + * @param recoveredClass the given class + * @return string buffer containing class info for the given class + * @throws CancelledException if cancelled + */ + private StringBuffer createClassInfoString(RecoveredClass recoveredClass) + throws CancelledException { + + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("\r\n"); + stringBuffer.append("\r\n"); + + stringBuffer.append("*** Recovered Class: " + recoveredClass.getName() + " ***\r\n"); + stringBuffer.append("\r\n"); + + // print parent classes + stringBuffer.append("parent class(es):\r\n"); + if (recoveredClass.hasParentClass()) { + + // use this to get direct parents + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + Set directParents = classHierarchyMap.keySet(); + + // use this to get correct parent order and to get the type of parent + Map parentToBaseTypeMap = + recoveredClass.getParentToBaseTypeMap(); + Set ancestors = parentToBaseTypeMap.keySet(); + Iterator ancestorIterator = ancestors.iterator(); + while (ancestorIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestor = ancestorIterator.next(); + if (directParents.contains(ancestor)) { + + Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor); + if (isVirtualParent != null && isVirtualParent) { + stringBuffer.append( + "\t virtual " + ancestor.getName() + "\r\n"); + } + else { + stringBuffer.append( + "\t" + ancestor.getName() + "\r\n"); + } + } + } + } + else { + stringBuffer.append("\tNone\r\n"); + } + + + // print child classes + stringBuffer.append("\r\n"); + stringBuffer.append("child class(es):\r\n"); + if (recoveredClass.hasChildClass()) { + List childClasses = recoveredClass.getChildClasses(); + Iterator childClassIterator = childClasses.iterator(); + while (childClassIterator.hasNext()) { + monitor.checkCanceled(); + stringBuffer.append("\t" + childClassIterator.next().getName() + "\r\n"); + } + + } + stringBuffer.append("\r\n"); + + // print constructors + stringBuffer.append("constructor(s):\r\n"); + List constructorList = recoveredClass.getConstructorList(); + Iterator constructorIterator = constructorList.iterator(); + while (constructorIterator.hasNext()) { + monitor.checkCanceled(); + Function constructorFunction = constructorIterator.next(); + stringBuffer.append("\t" + constructorFunction.getName() + " " + + constructorFunction.getEntryPoint().toString() + "\r\n"); + } + stringBuffer.append("\r\n"); + + // print inlined constructors + List inlinedConstructorList = recoveredClass.getInlinedConstructorList(); + if (inlinedConstructorList.size() > 0) { + stringBuffer.append("inlined constructor(s):\r\n"); + Iterator inlinedConstructorIterator = inlinedConstructorList.iterator(); + while (inlinedConstructorIterator.hasNext()) { + monitor.checkCanceled(); + Function inlinedConstructorFunction = inlinedConstructorIterator.next(); + stringBuffer.append("\t" + inlinedConstructorFunction.getName() + " " + + inlinedConstructorFunction.getEntryPoint().toString() + "\r\n"); + } + } + stringBuffer.append("\r\n"); + + // print destructors + stringBuffer.append("destructor(s):\r\n"); + List destructorList = recoveredClass.getDestructorList(); + Iterator destructorIterator = destructorList.iterator(); + while (destructorIterator.hasNext()) { + monitor.checkCanceled(); + Function destructorFunction = destructorIterator.next(); + stringBuffer.append("\t" + destructorFunction.getName() + " " + + destructorFunction.getEntryPoint().toString() + "\r\n"); + } + stringBuffer.append("\r\n"); + + // print inlined destructors + List inlinedDestructorList = recoveredClass.getInlinedDestructorList(); + if (inlinedDestructorList.size() > 0) { + stringBuffer.append("inlined destructor(s):\r\n"); + Iterator inlinedDestructorIterator = inlinedDestructorList.iterator(); + while (inlinedDestructorIterator.hasNext()) { + monitor.checkCanceled(); + Function inlinedDestructorFunction = inlinedDestructorIterator.next(); + stringBuffer.append("\t" + inlinedDestructorFunction.getName() + " " + + inlinedDestructorFunction.getEntryPoint().toString() + "\r\n"); + } + } + + // print const/dest that couldn't be classified correctly + List indeterminateList = recoveredClass.getIndeterminateList(); + if (indeterminateList.size() > 0) { + stringBuffer.append("\r\nindeterminate constructor(s) or destructor(s):\r\n"); + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + stringBuffer.append("\t" + indeterminateFunction.getName() + " " + + indeterminateFunction.getEntryPoint().toString() + "\r\n"); + } + stringBuffer.append("\r\n"); + } + stringBuffer.append("\r\n"); + + // print virtual function signatures + stringBuffer.append("member function(s):\r\n"); + List virtualFunctions = recoveredClass.getAllVirtualFunctions(); + //List dedupedVirtualFunctions = removeDuplicateFunctions(virtualFunctions); + Iterator vfunctionIter = virtualFunctions.iterator(); + while (vfunctionIter.hasNext()) { + monitor.checkCanceled(); + Function vfunction = vfunctionIter.next(); + stringBuffer.append("\t" + vfunction.getName() + " " + + vfunction.getEntryPoint().toString() + "\r\n"); + } + stringBuffer.append("\r\n"); + + // print class member data + stringBuffer.append("member data:\r\n"); + + DataType classMemberDataType = dataTypeManager.getDataType(recoveredClass.getClassPath(), + recoveredClass.getName() + CLASS_DATA_STRUCT_NAME); + + if (classMemberDataType != null && classMemberDataType instanceof Structure) { + + Structure memberDataStructure = (Structure) classMemberDataType; + int numDefinedComponents = memberDataStructure.getNumDefinedComponents(); + + DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents(); + for (int i = 0; i < numDefinedComponents; i++) { + monitor.checkCanceled(); + + stringBuffer.append("\t" + definedComponents[i].getDataType() + " " + + definedComponents[i].getFieldName() + "\r\n"); + } + } + stringBuffer.append("\r\n"); + + // Then recursively process the child classes + if (recoveredClass.hasChildClass()) { + List childClasses = recoveredClass.getChildClasses(); + Iterator childClassIterator = childClasses.iterator(); + while (childClassIterator.hasNext()) { + monitor.checkCanceled(); + stringBuffer.append(createClassInfoString(childClassIterator.next())); + } + } + + return stringBuffer; + } + + + /** + * Method to get the function signature string, from the decompiler if possible, otherwise from + * the listing + * @param function the given function + * @param includeReturn if true, include the return type in the string + * @return the function signature string + * @throws CancelledException if cancelled + */ + private String getFunctionSignatureString(Function function, boolean includeReturn) + throws CancelledException { + + if (function == null) { + return ""; + } + + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("\t"); + + String functionSignatureString = + decompilerUtils.getFunctionSignatureString(function, includeReturn); + + if (functionSignatureString != null) { + stringBuffer = stringBuffer.append(functionSignatureString.toString()); + return stringBuffer.toString(); + } + + // if can't get it from decompiler then use the listing one + if (includeReturn) { + stringBuffer.append(function.getReturnType().getDisplayName() + " "); + } + + stringBuffer.append(function.getName() + "("); + int paramCount = function.getParameterCount(); + int autoParamCount = function.getAutoParameterCount(); + if (paramCount - autoParamCount <= 0) { + stringBuffer.append(");"); + } + else { + for (int i = autoParamCount - 1; i < paramCount; i++) { + monitor.checkCanceled(); + Parameter param = function.getParameter(i); + stringBuffer.append(param.getDataType().getDisplayName() + " " + param.getName()); + if (i == paramCount) { + stringBuffer.append(");"); + } + else { + stringBuffer.append(", "); + } + } + } + + return stringBuffer.toString(); + } + + + /** + * Method to create a string containing a C++-like representation of the given class + * @param recoveredClass the given class + * @return string containing a C++-like representation of the given class + * @throws CancelledException if cancelled + */ + private StringBuffer createClassDefinitionString(RecoveredClass recoveredClass) + throws CancelledException { + + + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("\r\n\r\n"); + + stringBuffer.append(recoverClassesFromRTTI.createParentStringBuffer(recoveredClass)); + + stringBuffer.append("\r\n{\r\n"); + + + // print constructor signature(s) + stringBuffer.append("constructor(s):\r\n"); + List constructorList = recoveredClass.getConstructorList(); + Iterator constructorIterator = constructorList.iterator(); + while (constructorIterator.hasNext()) { + monitor.checkCanceled(); + Function constructorFunction = constructorIterator.next(); + String functionSignatureString = + getFunctionSignatureString(constructorFunction, true); + + stringBuffer.append(functionSignatureString); + stringBuffer.append("\r\n"); + } + + // print destructor signature + stringBuffer.append("\r\ndestructor(s):\r\n"); + List destructorList = recoveredClass.getDestructorList(); + Iterator destructorIterator = destructorList.iterator(); + while (destructorIterator.hasNext()) { + monitor.checkCanceled(); + Function destructorFunction = destructorIterator.next(); + String functionSignatureString = + getFunctionSignatureString(destructorFunction, true); + stringBuffer.append(functionSignatureString); + stringBuffer.append("\r\n"); + } + + // print const/dest that couldn't be classified correctly + List indeterminateList = recoveredClass.getIndeterminateList(); + if (indeterminateList.size() > 0) { + stringBuffer.append("\r\nindeterminate constructor or destructor function(s):\r\n"); + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + String functionSignatureString = + getFunctionSignatureString(indeterminateFunction, true); + stringBuffer.append(functionSignatureString); + stringBuffer.append("\r\n"); + } + } + + // print virtual function signature(s) + stringBuffer.append("\r\nmember function(s):\r\n"); + List virtualFunctions = recoveredClass.getAllVirtualFunctions(); + Iterator vfunctionIter = virtualFunctions.iterator(); + while (vfunctionIter.hasNext()) { + monitor.checkCanceled(); + Function vfunction = vfunctionIter.next(); + String functionSignatureString = + getFunctionSignatureString(vfunction, true); + stringBuffer.append(functionSignatureString); + stringBuffer.append("\r\n"); + } + + stringBuffer.append("\r\n"); + stringBuffer.append("member data: \r\n"); + + // print class member data items + DataType classMemberDataType = dataTypeManager.getDataType(recoveredClass.getClassPath(), + recoveredClass.getName() + CLASS_DATA_STRUCT_NAME); + + if (classMemberDataType != null && classMemberDataType instanceof Structure) { + + Structure memberDataStructure = (Structure) classMemberDataType; + int numDefinedComponents = memberDataStructure.getNumDefinedComponents(); + + DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents(); + for (int i = 0; i < numDefinedComponents; i++) { + monitor.checkCanceled(); + stringBuffer.append("\t" + definedComponents[i].getDataType() + " " + + definedComponents[i].getFieldName() + "\r\n"); + } + } + stringBuffer.append("};\r\n"); + + + // Then recursively process the child classes + if (recoveredClass.hasChildClass()) { + List childClasses = recoveredClass.getChildClasses(); + Iterator childClassIterator = childClasses.iterator(); + while (childClassIterator.hasNext()) { + monitor.checkCanceled(); + stringBuffer.append(createClassDefinitionString(childClassIterator.next())); + } + } + + return stringBuffer; + } + + + +} diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClass.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClass.java new file mode 100644 index 0000000000..4a497a1807 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClass.java @@ -0,0 +1,665 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; +import java.util.Map.Entry; + +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.symbol.Namespace; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * The Class that describes object oriented classes that exist in the program + */ +public class RecoveredClass { + + private String name; + private CategoryPath classPath; + private Namespace classNamespace; + private DataTypeManager dataTypeManager; + + private List
vftableAddresses = new ArrayList
(); + private List allClassVirtualFunctions = new ArrayList(); + + private boolean hasParentClass = false; + private boolean hasChildClass = false; + private boolean hasVftable = true; + + private boolean hasSingleInheritance = false; + private boolean hasMultipleInheritance = false; + private boolean hasMultipleVirtualInheritance = false; + + private List constructorAndDestructorList = new ArrayList(); + private List constructorList = new ArrayList(); + private List inlinedConstructorList = new ArrayList(); + private List destructorList = new ArrayList(); + private List inlinedDestructorList = new ArrayList(); + private List deletingDestructors = new ArrayList(); + private List nonThisDestructors = new ArrayList(); + private List cloneFunctions = new ArrayList(); + private List indeterminateList = new ArrayList(); + private List indeterminateInlineList = new ArrayList(); + + private Map> vftableToVfunctionsMap = + new HashMap>(); + + private List classHierarchy = new ArrayList(); + private Map> classHierarchyMap = + new HashMap>(); + + private Map vftableToBaseClassMap = + new HashMap(); + private Map orderToVftableMap = new HashMap(); + + private Map classOffsetToVftableMap = new HashMap(); + + private List parentList = new ArrayList(); + private Map parentToBaseTypeMap = + new HashMap(); + + private List childClasses = new ArrayList(); + + private Address vbtableAddress = null; + private Structure vbtableStructure = null; + private int vbtableOffset = NONE; + + private boolean inheritsVirtualAncestor = false; + private boolean isPublicClass = false; + + private Structure existingClassStructure = null; + private Structure computedClassStructure = null; + private boolean hasExistingClassStructure = false; + private Function vbaseDestructor = null; + + private String shortenedTemplateName = new String(); + + private static final int NONE = -1; + + TaskMonitor monitor = TaskMonitor.DUMMY; + EditStructureUtils structUtils; + + + RecoveredClass(String name, CategoryPath classPath, Namespace classNamespace, + DataTypeManager dataTypeManager) { + this.name = name; + this.classPath = classPath; + this.classNamespace = classNamespace; + this.dataTypeManager = dataTypeManager; + + this.structUtils = new EditStructureUtils(); + } + + public String getName() { + return name; + } + + public List getVirtualFunctions(Address vftableAddress) { + return vftableToVfunctionsMap.get(vftableAddress); + } + + public List getAllVirtualFunctions() { + return allClassVirtualFunctions; + } + + public CategoryPath getClassPath() { + return classPath; + } + + public Namespace getClassNamespace() { + return classNamespace; + } + + public void setHasParentClass(boolean bool) { + hasParentClass = bool; + return; + } + + public boolean hasParentClass() { + return hasParentClass; + } + + public void setHasChildClass(boolean bool) { + hasChildClass = true; + } + + public boolean hasChildClass() { + return hasChildClass; + } + + public void addParent(RecoveredClass recoveredClass) { + if (!parentList.contains(recoveredClass)) { + parentList.add(recoveredClass); + } + } + + public List getParentList() { + return parentList; + } + + public void addVftableAddress(Address address) { + vftableAddresses.add(address); + setHasVftable(true); + } + + public List
getVftableAddresses() { + return vftableAddresses; + } + + public void addVftableVfunctionsMapping(Address vftAddress, List vfunctions) { + vftableToVfunctionsMap.put(vftAddress, vfunctions); + allClassVirtualFunctions.addAll(vfunctions); + } + + public void addVftableToBaseClassMapping(Address vftAddress, RecoveredClass recoveredClass) { + vftableToBaseClassMap.put(vftAddress, recoveredClass); + } + + public RecoveredClass getVftableBaseClass(Address address) { + return vftableToBaseClassMap.get(address); + } + + public void addOrderToVftableMapping(Integer order, Address address) { + orderToVftableMap.put(order, address); + } + + public Map getOrderToVftableMap() { + return orderToVftableMap; + } + + public void addClassOffsetToVftableMapping(int offset, Address vftableAddress) + throws Exception { + + // already have this mapping + if (classOffsetToVftableMap.get(offset) == vftableAddress) { + return; + } + + if (!classOffsetToVftableMap.keySet().contains(offset)) { + + // error if try to add same address to different offset + if (classOffsetToVftableMap.values().contains(vftableAddress)) { + throw new Exception(name + " trying to add same vftable address " + + vftableAddress.toString() + " to new offset " + offset); + } + + classOffsetToVftableMap.put(offset, vftableAddress); + return; + } + + // error if try to add different address to same offset + Address address = classOffsetToVftableMap.get(offset); + if (!address.equals(vftableAddress)) { + throw new Exception(name + " trying to add different vftable address (old: " + + vftableAddress.toString() + " new: " + address.toString() + ") to same offset " + + offset); + } + + } + + public Map getClassOffsetToVftableMap() { + return classOffsetToVftableMap; + } + + public void addParentToBaseTypeMapping(RecoveredClass recoveredClass, Boolean isVirtualBase) { + + if (!parentToBaseTypeMap.keySet().contains(recoveredClass)) { + parentToBaseTypeMap.put(recoveredClass, isVirtualBase); + } + } + + public Map getParentToBaseTypeMap() { + return parentToBaseTypeMap; + } + + public void setVbtableAddress(Address address) { + vbtableAddress = address; + } + + public Address getVbtableAddress() { + return vbtableAddress; + } + + public void setVbtableStructure(Structure structure) { + vbtableStructure = structure; + } + + public Structure getVbtableStructure() { + return vbtableStructure; + } + + public void setVbtableOffset(int offset) { + vbtableOffset = offset; + } + + public int getVbtableOffset() { + return vbtableOffset; + } + + public void setInheritsVirtualAncestor(boolean setting) { + inheritsVirtualAncestor = setting; + } + + public boolean inheritsVirtualAncestor() { + return inheritsVirtualAncestor; + } + + public void setIsPublicClass(boolean setting) { + isPublicClass = setting; + } + + public boolean isPublicClass() { + return isPublicClass; + } + + public void setHasVftable(boolean setting) { + hasVftable = setting; + } + + public boolean hasVftable() { + return hasVftable; + } + + public void setHasSingleInheritance(boolean hasSingleInheritanceSetting) { + hasSingleInheritance = hasSingleInheritanceSetting; + } + + public boolean hasSingleInheritance() { + return hasSingleInheritance; + } + + public void setHasMultipleInheritance(boolean hasMultipleInheritanceSetting) { + hasMultipleInheritance = hasMultipleInheritanceSetting; + } + + public boolean hasMultipleInheritance() { + return hasMultipleInheritance; + } + + public void setHasMultipleVirtualInheritance(boolean hasMultVirtSetting) { + hasMultipleVirtualInheritance = hasMultVirtSetting; + } + + public boolean hasMultipleVirtualInheritance() { + return hasMultipleVirtualInheritance; + } + + public void addConstructorDestructorList(List list) { + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + + Function function = iterator.next(); + if (!constructorAndDestructorList.contains(function)) { + constructorAndDestructorList.add(function); + } + } + return; + } + + public void removeFromConstructorDestructorList(Function function) { + if (constructorAndDestructorList.contains(function)) { + constructorAndDestructorList.remove(function); + } + } + + public void addConstructor(Function function) { + if (!constructorList.contains(function)) { + constructorList.add(function); + } + return; + } + + public void addDestructor(Function function) { + if (!destructorList.contains(function)) { + destructorList.add(function); + } + return; + } + + public void addInlinedConstructor(Function function) { + if (!inlinedConstructorList.contains(function)) { + inlinedConstructorList.add(function); + } + return; + } + + public void addInlinedDestructor(Function function) { + if (!inlinedDestructorList.contains(function)) { + inlinedDestructorList.add(function); + } + return; + } + + public void addNonThisDestructor(Function function) { + if (!nonThisDestructors.contains(function)) { + nonThisDestructors.add(function); + } + } + + public void addIndeterminateInline(Function function) { + if (!indeterminateInlineList.contains(function)) { + indeterminateInlineList.add(function); + } + return; + } + + public void removeIndeterminateInline(Function function) { + if (indeterminateInlineList.contains(function)) { + indeterminateInlineList.remove(function); + } + return; + } + + public void addIndeterminateConstructorOrDestructorList(List list) { + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + Function function = iterator.next(); + if (!indeterminateList.contains(function)) { + indeterminateList.add(function); + } + } + return; + } + + public void removeIndeterminateConstructorOrDestructor(Function function) { + if (indeterminateList.contains(function)) { + indeterminateList.remove(function); + } + return; + } + + public List getConstructorOrDestructorFunctions() { + return constructorAndDestructorList; + } + + public List getConstructorList() { + return constructorList; + } + + public List getInlinedConstructorList() { + return inlinedConstructorList; + } + + public List getDestructorList() { + return destructorList; + } + + public List getInlinedDestructorList() { + return inlinedDestructorList; + } + + public List getNonThisDestructors() { + return nonThisDestructors; + } + + public List getIndeterminateList() { + return indeterminateList; + } + + public List getIndeterminateInlineList() { + return indeterminateInlineList; + } + + public void addChildClass(RecoveredClass recoveredClass) { + if (!childClasses.contains(recoveredClass)) { + childClasses.add(recoveredClass); + } + return; + } + + public List getChildClasses() { + return childClasses; + } + + public void addExistingClassStructure(Structure classStructure) { + if (classStructure == null) { + return; + } + existingClassStructure = classStructure; + hasExistingClassStructure = true; + return; + } + + public Structure getExistingClassStructure() { + return existingClassStructure; + } + + public boolean hasExistingClassStructure() { + return hasExistingClassStructure; + } + + public void updateClassMemberStructure(Structure structure) throws CancelledException { + + // initialize by copying first structure + if (computedClassStructure == null) { + + computedClassStructure = + new StructureDataType(classPath, name, structure.getLength(), dataTypeManager); + + int numComponents = structure.getNumComponents(); + for (int i = 0; i < numComponents; i++) { + DataTypeComponent component = structure.getComponent(i); + int offset = component.getOffset(); + + computedClassStructure.replaceAtOffset(offset, component.getDataType(), + component.getDataType().getLength(), component.getFieldName(), + component.getComment()); + } + + return; + } + + // update initial structure using all further structures + if (structure.getLength() > computedClassStructure.getLength()) { + computedClassStructure.growStructure( + structure.getLength() - computedClassStructure.getLength()); + } + + DataTypeComponent[] definedComponents = structure.getDefinedComponents(); + for (DataTypeComponent newComponent : definedComponents) { + + DataType newComponentDataType = newComponent.getDataType(); + + int offset = newComponent.getOffset(); + + DataTypeComponent currentComponent = computedClassStructure.getComponentAt(offset); + DataType currentComponentDataType = currentComponent.getDataType(); + + if (currentComponentDataType.equals(newComponentDataType)) { + continue; + } + + int length = newComponentDataType.getLength(); + String fieldName = newComponent.getFieldName(); + String comment = newComponent.getComment(); + + // if it is any empty placeholder structure - replace with + // undefined1 dt + if (newComponentDataType instanceof Structure && + newComponentDataType.isNotYetDefined()) { + + computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1, + fieldName, comment); + continue; + } + + // replace pointers to existing class data type with void pointer of same size + if (newComponentDataType instanceof Pointer && + newComponentDataType.getName().equals(name + " *")) { + + DataType voidDT = new VoidDataType(); + + Pointer pointer = new PointerDataType(); + if (newComponentDataType.getLength() == 4) { + pointer = new Pointer32DataType(voidDT); + } + if (newComponentDataType.getLength() == 8) { + pointer = new Pointer64DataType(voidDT); + } + computedClassStructure.replaceAtOffset(offset, pointer, pointer.getLength(), + fieldName, comment); + continue; + } + + // if the new component is a non-empty structure, check to see if the current + // structure has undefined or equivalent components and replace with new struct if so + if (newComponentDataType instanceof Structure) { + if (structUtils.hasReplaceableComponentsAtOffset(computedClassStructure, + offset, (Structure) newComponentDataType, monitor)) { + + boolean successfulClear = + structUtils.clearLengthAtOffset(computedClassStructure, offset, + length, monitor); + + if (successfulClear) { + computedClassStructure.replaceAtOffset(offset, newComponentDataType, length, + fieldName, comment); + } + continue; + } + } + + // if current component is undefined size 1 and new component is not undefined size 1 + // then replace it + if (structUtils.isUndefined1(currentComponentDataType) && + !structUtils.isUndefined1(newComponentDataType)) { + if (structUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(computedClassStructure, + offset, length, monitor)) { + boolean successfulClear = + structUtils.clearLengthAtOffset(computedClassStructure, offset, + length, monitor); + + if (successfulClear) { + computedClassStructure.replaceAtOffset(offset, newComponentDataType, length, + fieldName, comment); + } + } + continue; + } + + // if new component is not an undefined data type and the current componenent(s) + // that make up new component length are all undefineds then clear and replace + // the current component(s) with the new one + if (structUtils.isUndefined(currentComponentDataType) && + !structUtils.isUndefined(newComponentDataType)) { + + if (structUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(computedClassStructure, + offset, length, monitor)) { + boolean successfulClear = + structUtils.clearLengthAtOffset(computedClassStructure, offset, + length, monitor); + + if (successfulClear) { + computedClassStructure.replaceAtOffset(offset, newComponentDataType, length, + fieldName, comment); + } + } + } + } + + return; + } + + // TODO: remove once FillOutStructCmd updates to put Undefined1 instead of undefined + // for saved offsets + public void updateClassMemberStructureUndefineds(NoisyStructureBuilder componentMap) { + + Iterator> componentMapIterator = componentMap.iterator(); + while (componentMapIterator.hasNext()) { + Entry next = componentMapIterator.next(); + Long offset = next.getKey(); + DataType dataType = next.getValue(); + + if (computedClassStructure.getLength() < (offset.intValue() + dataType.getLength())) { + continue; + } + + if (structUtils.isUndefined1(dataType)) { + dataType = new Undefined1DataType(); + DataTypeComponent component = + computedClassStructure.getComponentAt(offset.intValue()); + + if (!component.getDataType().equals(dataType)) { + computedClassStructure.replaceAtOffset(offset.intValue(), dataType, + dataType.getLength(), component.getFieldName(), component.getComment()); + } + } + } + } + + public Structure getComputedClassStructure() { + return computedClassStructure; + } + + public void addCloneFunction(Function function) { + if (!cloneFunctions.contains(function)) { + cloneFunctions.add(function); + } + } + + public List getCloneFunctions() { + return cloneFunctions; + } + + public void addDeletingDestructor(Function function) { + if (!deletingDestructors.contains(function)) { + deletingDestructors.add(function); + } + } + + public List getDeletingDestructors() { + return deletingDestructors; + } + + public void setVBaseDestructor(Function function) { + vbaseDestructor = function; + } + + public Function getVBaseDestructor() { + return vbaseDestructor; + } + + public void setClassHierarchy(List list) { + classHierarchy.addAll(list); + return; + } + + public List getClassHierarchy() { + return classHierarchy; + } + + public void addClassHierarchyMapping(RecoveredClass recoveredClass, + List recoveredClasses) { + + if (!classHierarchyMap.keySet().contains(recoveredClass)) { + classHierarchyMap.put(recoveredClass, recoveredClasses); + } + } + + public Map> getClassHierarchyMap() { + return classHierarchyMap; + } + + public void addShortenedTemplatedName(String shortenedName) { + shortenedTemplateName = shortenedName; + } + + public String getShortenedTemplateName() { + return shortenedTemplateName; + } +} + diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClassUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClassUtils.java new file mode 100644 index 0000000000..82d02d94f1 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClassUtils.java @@ -0,0 +1,6650 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.*; +import java.util.stream.Collectors; + +import ghidra.app.cmd.label.AddLabelCmd; +import ghidra.app.cmd.label.SetLabelPrimaryCmd; +import ghidra.app.plugin.core.analysis.ReferenceAddressPair; +import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd; +import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd.OffsetPcodeOpPair; +import ghidra.app.plugin.core.navigation.locationreferences.LocationReference; +import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.flatapi.FlatProgramAPI; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function.FunctionUpdateType; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.pcode.*; +import ghidra.program.model.symbol.*; +import ghidra.program.util.ProgramLocation; +import ghidra.program.util.ProgramMemoryUtil; +import ghidra.util.Msg; +import ghidra.util.bytesearch.*; +import ghidra.util.datastruct.ListAccumulator; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +public class RecoveredClassUtils { + + public static final String DTM_CLASS_DATA_FOLDER_NAME = "ClassDataTypes"; + private static final String CLASS_DATA_STRUCT_NAME = "_data"; + private static final String DEFAULT_VFUNCTION_PREFIX = "vfunction"; + private static final String CLASS_VFUNCTION_STRUCT_NAME = "_vftable"; + + public static final String VFTABLE_LABEL = "vftable"; + private static final String VBASE_DESTRUCTOR_LABEL = "vbase_destructor"; + private static final String VBTABLE_LABEL = "vbtable"; + private static final String CLONE_LABEL = "clone"; + private static final String DELETING_DESTRUCTOR_LABEL = "deleting_destructor"; + + private static final String BOOKMARK_CATEGORY = "RECOVERED CLASS"; + + private static final String INLINE_CONSTRUCTOR_BOOKMARK = "INLINED CONSTRUCTOR"; + private static final String INLINE_DESTRUCTOR_BOOKMARK = "INLINED DESTRUCTOR"; + + private static final String INDETERMINATE_INLINE_BOOKMARK = "INDETERMINATE INLINE"; + + private static final int NONE = -1; + + private static int MIN_OPERATOR_NEW_REFS = 10; + + private Map vftableToClassMap = new HashMap(); + + // map from vftable references to the vftables they point to + private Map vftableRefToVftableMap = new HashMap(); + + // map from function to its vftable references + private Map> functionToVftableRefsMap = + new HashMap>(); + + // map from function to class(es) it is in + private Map> functionToClassesMap = + new HashMap>(); + + // map from function to all called constructors/destructors ref/addr pairs + Map> functionToCalledConsDestRefAddrPairMap = + new HashMap>(); + + // map from class to list of possible parent classes + Map> possibleParentMap = + new HashMap>(); + + // map from namespace to class object + Map namespaceToClassMap = new HashMap(); + + Map> functionToStorePcodeOps = + new HashMap>(); + Map> functionToLoadPcodeOps = + new HashMap>(); + + List allConstructorsAndDestructors = new ArrayList(); + List allConstructors = new ArrayList(); + List allDestructors = new ArrayList(); + List allInlinedConstructors = new ArrayList(); + List allInlinedDestructors = new ArrayList(); + + List badFIDNamespaces = new ArrayList(); + List badFIDStructures = new ArrayList(); + + List badFIDFunctions = new ArrayList(); + List resolvedFIDFunctions = new ArrayList(); + List fixedFIDFunctions = new ArrayList(); + + private static Function operator_delete = null; + private static Function operator_new = null; + private static Function purecall = null; + + private static Function atexit = null; + List atexitCalledFunctions = new ArrayList(); + + List deletingDestructorsThatCallDestructor = new ArrayList(); + + GlobalNamespace globalNamespace; + DataTypeManager dataTypeManager; + int defaultPointerSize; + SymbolTable symbolTable; + + ExtraScriptUtils extraUtils; + EditStructureUtils structUtils; + + DecompilerScriptUtils decompilerUtils; + CategoryPath classDataTypesCategoryPath; + + private TaskMonitor monitor; + private Program program; + ProgramLocation location; + PluginTool tool; + FlatProgramAPI api; + boolean createBookmarks; + boolean useShortTemplates; + boolean nameVfunctions; + + RecoveredClassUtils(Program program, ProgramLocation location, PluginTool tool, + FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, + boolean nameVfunctions, TaskMonitor monitor) { + + this.monitor = monitor; + this.program = program; + this.location = location; + this.tool = tool; + this.api = api; + + extraUtils = new ExtraScriptUtils(program, monitor); + + this.classDataTypesCategoryPath = + extraUtils.createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME); + + this.createBookmarks = createBookmarks; + this.useShortTemplates = useShortTemplates; + this.nameVfunctions = nameVfunctions; + + globalNamespace = (GlobalNamespace) program.getGlobalNamespace(); + + decompilerUtils = new DecompilerScriptUtils(program, tool, monitor); + structUtils = new EditStructureUtils(); + + + dataTypeManager = program.getDataTypeManager(); + symbolTable = program.getSymbolTable(); + + defaultPointerSize = program.getDefaultPointerSize(); + } + + public void updateVftableToClassMap(Address vftableAddress, RecoveredClass recoveredClass) { + if (vftableToClassMap.get(vftableAddress) == null) { + vftableToClassMap.put(vftableAddress, recoveredClass); + } + } + + public RecoveredClass getVftableClass(Address vftableAddress) { + return vftableToClassMap.get(vftableAddress); + } + + /** + * Method to create a mappings between the list of vftable references to the vftables they reference + * @param referencesToVftable addresses that reference the given vftable address + * @param vftableAddress the given vftable address + * @throws CancelledException if cancelled + */ + public void addReferenceToVtableMapping(List
referencesToVftable, + Address vftableAddress) throws CancelledException { + + Iterator
referencesIterator = referencesToVftable.iterator(); + while (referencesIterator.hasNext()) { + Address vtableReference = referencesIterator.next(); + monitor.checkCanceled(); + vftableRefToVftableMap.put(vtableReference, vftableAddress); + } + } + + public Address getVftableAddress(Address vftableReference) { + return vftableRefToVftableMap.get(vftableReference); + } + + /** + * Method to update the functionToVftableListMap with the given input + * @param vftableRefToFunctionMapping a mapping of a vftable reference to the function it is in + * @throws CancelledException if cancelled + */ + public void addFunctionToVftableReferencesMapping( + Map vftableRefToFunctionMapping) throws CancelledException { + + Set
keySet = vftableRefToFunctionMapping.keySet(); + Iterator
referencesIterator = keySet.iterator(); + while (referencesIterator.hasNext()) { + monitor.checkCanceled(); + Address vtableReference = referencesIterator.next(); + Function function = vftableRefToFunctionMapping.get(vtableReference); + if (functionToVftableRefsMap.containsKey(function)) { + List
referenceList = functionToVftableRefsMap.get(function); + if (!referenceList.contains(vtableReference)) { + List
newReferenceList = new ArrayList
(referenceList); + newReferenceList.add(vtableReference); + functionToVftableRefsMap.replace(function, referenceList, newReferenceList); + } + } + else { + List
referenceList = new ArrayList
(); + referenceList.add(vtableReference); + functionToVftableRefsMap.put(function, referenceList); + } + } + } + + public List
getVftableReferences(Function function) { + return functionToVftableRefsMap.get(function); + } + + /** + * Method to add function to map of class it is contained in some functions are in + * multiple classes becuase they have references to multiple class vtables either + * because they have an inlined parent + * @param functions the given list of functions + * @param recoveredClass the given class + * @throws CancelledException if cancelled + */ + public void addFunctionsToClassMapping(List functions, RecoveredClass recoveredClass) + throws CancelledException { + + Iterator functionIterator = functions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); + Function function = functionIterator.next(); + // if the map already contains a mapping for function and if + // the associated class list doesn't contain the new class, then + // add the new class and update the mapping + if (functionToClassesMap.containsKey(function)) { + List classList = functionToClassesMap.get(function); + if (!classList.contains(recoveredClass)) { + List newClassList = new ArrayList(classList); + newClassList.add(recoveredClass); + functionToClassesMap.replace(function, classList, newClassList); + } + } + // if the map doesn't contain a mapping for function, then add it + else { + List classList = new ArrayList(); + classList.add(recoveredClass); + functionToClassesMap.put(function, classList); + } + } + + } + + public List getClasses(Function function) { + return functionToClassesMap.get(function); + } + + /** + * Method to find all the vftables in the program + * @return list of all vftable symbols + * @throws CancelledException when cancelled + */ + public List getListOfVftableSymbols() throws CancelledException { + + // Do with *'s to also get the PDB ones + SymbolIterator vftableSymbols = + program.getSymbolTable().getSymbolIterator("*vftable*", true); + + List vftableSymbolList = new ArrayList(); + while (vftableSymbols.hasNext()) { + monitor.checkCanceled(); + Symbol vftableSymbol = vftableSymbols.next(); + if (vftableSymbol.getName().equals("vftable")) { + vftableSymbolList.add(vftableSymbol); + } + // check for ones that are pdb that start with ' and may or may not end with ' + // can't just get all that contain vftable because that would get some strings + else { + String name = vftableSymbol.getName(); + name = name.substring(1, name.length()); + if (name.startsWith("vftable")) { + vftableSymbolList.add(vftableSymbol); + } + } + } + return vftableSymbolList; + } + + /** + * Method to return a symbol with the given name in the given namespace which is in the given + * parent namespace or null if one is not found + * @param parentNamespaceName name of parent namespace + * @param namespaceName name of symbol namespace + * @param symbolName name of symbol + * @return Symbol with given name, namespace and parent namespace or null if doesn't exist + * @throws CancelledException if cancelled + */ + public Symbol getSymbolInNamespaces(String parentNamespaceName, String namespaceName, + String symbolName) throws CancelledException { + + SymbolIterator symbols = program.getSymbolTable().getSymbols(symbolName); + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + if (symbol.getParentNamespace().getName().equals(namespaceName)) { + Namespace namespace = symbol.getParentNamespace(); + if (namespace.getParentNamespace().getName().equals(parentNamespaceName)) { + return symbol; + } + } + } + return null; + } + + public Address getSingleAddressOfSymbolContainingBothStrings(String string1, String string2) + throws CancelledException { + + List
symbolAddressList = new ArrayList
(); + + SymbolIterator symbols = + program.getSymbolTable().getSymbolIterator("*" + string1 + "*", true); + + while (symbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = symbols.next(); + Address symbolAddress = symbol.getAddress(); + + if (symbol.getName().contains(string2)) { + if (!symbolAddressList.contains(symbolAddress)) { + symbolAddressList.add(symbolAddress); + } + } + } + if (symbolAddressList.size() == 1) { + return symbolAddressList.get(0); + } + return null; + } + + /** + * Method to create a map with keyset containing all constructor/destructor functions in the + * given list of classes and associated mapping to list of refAddrPairs for called + * constructor/destructor functions + * @param recoveredClasses the list of classes + * @throws CancelledException if cancelled + */ + public void createCalledFunctionMap(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List constructorOrDestructorFunctions = + recoveredClass.getConstructorOrDestructorFunctions(); + Iterator functionIterator = constructorOrDestructorFunctions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); + Function function = functionIterator.next(); + createFunctionToCalledConstructorOrDestructorRefAddrPairMapping(function); + } + } + + } + + /** + * Method to determine and add the mapping of the given function to its called constructor or destructor functions + * @param function given function + * @throws CancelledException when script is canceled + */ + public void createFunctionToCalledConstructorOrDestructorRefAddrPairMapping(Function function) + throws CancelledException { + + List referenceAddressPairs = new ArrayList(); + Set calledFunctions = function.getCalledFunctions(monitor); + Iterator calledFunctionIterator = calledFunctions.iterator(); + while (calledFunctionIterator.hasNext()) { + monitor.checkCanceled(); + Function calledFunction = calledFunctionIterator.next(); + Function referencedFunction = calledFunction; + if (calledFunction.isThunk()) { + Function thunkFunction = calledFunction.getThunkedFunction(true); + calledFunction = thunkFunction; + } + // 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)) { + // get list of refs to this function from the calling function + List
referencesToFunctionBFromFunctionA = + extraUtils.getReferencesToFunctionBFromFunctionA(function, + referencedFunction); + // add them to list of ref address pairs + Iterator
iterator = referencesToFunctionBFromFunctionA.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Address sourceRefAddr = iterator.next(); + referenceAddressPairs.add( + new ReferenceAddressPair(sourceRefAddr, calledFunction.getEntryPoint())); + } + } + } + // add list to global map + functionToCalledConsDestRefAddrPairMap.put(function, referenceAddressPairs); + + } + + public List getCalledConstDestRefAddrPairs(Function function) { + return functionToCalledConsDestRefAddrPairMap.get(function); + } + + public void updateNamespaceToClassMap(Namespace namespace, RecoveredClass recoveredClass) { + namespaceToClassMap.put(namespace, recoveredClass); + } + + public RecoveredClass getClass(Namespace namespace) { + return namespaceToClassMap.get(namespace); + } + + public void updateFunctionToStorePcodeOpsMap(Function function, + List offsetPcodeOpPairs) { + if (functionToStorePcodeOps.get(function) == null) { + functionToStorePcodeOps.put(function, offsetPcodeOpPairs); + } + } + + public List getStorePcodeOpPairs(Function function) { + return functionToStorePcodeOps.get(function); + } + + public void updateFunctionToLoadPcodeOpsMap(Function function, + List offsetPcodeOpPairs) { + if (functionToLoadPcodeOps.get(function) == null) { + functionToLoadPcodeOps.put(function, offsetPcodeOpPairs); + } + } + + public List getLoadPcodeOpPairs(Function function) { + return functionToLoadPcodeOps.get(function); + } + + public void updateAllConstructorsAndDestructorsList(List functions) { + allConstructorsAndDestructors.addAll(functions); + } + + public List getAllConstructorsAndDestructors() { + return allConstructorsAndDestructors; + } + + public void addToAllConstructors(Function function) { + allConstructors.add(function); + } + + public void removeFromAllConstructors(Function function) { + allConstructors.remove(function); + } + + public List getAllConstructors() { + return allConstructors; + } + + public void addToAllDestructors(Function function) { + allDestructors.add(function); + } + + public void removeFromAllDestructors(Function function) { + allDestructors.remove(function); + } + + public List getAllDestructors() { + return allDestructors; + } + + public void addToAllInlinedConstructors(Function function) { + allInlinedConstructors.add(function); + } + + public void removeFromAllInlinedConstructors(Function function) { + allInlinedConstructors.remove(function); + } + + public List getAllInlinedConstructors() { + return allInlinedConstructors; + } + + public void addToAllInlinedDestructors(Function function) { + allInlinedDestructors.add(function); + } + + public void removeFromAllInlinedDestructors(Function function) { + allInlinedDestructors.remove(function); + } + + public List getAllInlinedDestructors() { + return allInlinedDestructors; + } + + + + /** + * Method to determine if referenced vftables are from the same class + * @param vftableReferences list of vftable references + * @return true if all listed vftable references refer to vftables from the same class, false otherwise + * @throws CancelledException when cancelled + */ + public boolean areVftablesInSameClass(List
vftableReferences) + throws CancelledException { + + List classes = new ArrayList(); + + Iterator
iterator = vftableReferences.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Address vftableReference = iterator.next(); + Address vftableAddress = vftableRefToVftableMap.get(vftableReference); + RecoveredClass recoveredClass = vftableToClassMap.get(vftableAddress); + if (!classes.contains(recoveredClass)) { + classes.add(recoveredClass); + } + } + if (classes.size() > 1) { + return false; + } + return true; + } + + /** + * Method to return reference to the class vftable in the given function + * @param recoveredClass the given class + * @param function the given function + * @return the reference to the class vftable in the given function or null if there isn't one + * @throws CancelledException if cancelled + */ + public Address getClassVftableReference(RecoveredClass recoveredClass, + Function function) throws CancelledException { + + List
vftableReferenceList = functionToVftableRefsMap.get(function); + + if (vftableReferenceList == null) { + return null; + } + + Iterator
vftableRefs = vftableReferenceList.iterator(); + while (vftableRefs.hasNext()) { + monitor.checkCanceled(); + Address vftableRef = vftableRefs.next(); + + Address vftableAddress = vftableRefToVftableMap.get(vftableRef); + if (vftableAddress == null) { + continue; + } + + RecoveredClass referencedClass = vftableToClassMap.get(vftableAddress); + if (referencedClass == null) { + continue; + } + + if (referencedClass.equals(recoveredClass)) { + return vftableRef; + } + } + return null; + + } + + /** + * Method to return the vftable reference in the given function that corresponds to the given class + * @param function the given function + * @param recoveredClass the given class + * @return the vftableRef address in the given function that corresponds to the given class + * @throws CancelledException if cancelled + */ + public Address getClassVftableRefInFunction(Function function, RecoveredClass recoveredClass) + throws CancelledException { + + List
listOfClassRefsInFunction = + getSortedListOfAncestorRefsInFunction(function, recoveredClass); + + Iterator
iterator = listOfClassRefsInFunction.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Address classRef = iterator.next(); + Address vftableAddress = vftableRefToVftableMap.get(classRef); + + // skip the ones that aren't vftable refs + if (vftableAddress == null) { + continue; + } + + // return the first one that is a vftable ref to the given class + RecoveredClass vftableClass = vftableToClassMap.get(vftableAddress); + if (vftableClass.equals(recoveredClass)) { + return classRef; + } + } + return null; + } + + /** + * Method to get a sorted list of both vftable and call refs to ancestor classes of the given + * class in the given function + * @param function the given function + * @param recoveredClass the given class + * @return a sorted list of both vftable and call refs to ancestor classes of the given + * class in the given function + * @throws CancelledException if cancelled + */ + public List
getSortedListOfAncestorRefsInFunction(Function function, + RecoveredClass recoveredClass) throws CancelledException { + + // get the map of all referenced vftables or constructor/desstructor calls in this function + Map referenceToClassMapForFunction = + getReferenceToClassMap(recoveredClass, function); + + // get a list of all ancestor classes referenced in the map + List classHierarchy = recoveredClass.getClassHierarchy(); + + // make a list of all related class references + List
listOfAncestorRefs = new ArrayList
(); + Set
ancestorRefs = referenceToClassMapForFunction.keySet(); + Iterator
ancestorRefIterator = ancestorRefs.iterator(); + while (ancestorRefIterator.hasNext()) { + monitor.checkCanceled(); + Address ancestorRef = ancestorRefIterator.next(); + RecoveredClass mappedClass = referenceToClassMapForFunction.get(ancestorRef); + if (classHierarchy.contains(mappedClass)) { + listOfAncestorRefs.add(ancestorRef); + } + } + + Collections.sort(listOfAncestorRefs); + return listOfAncestorRefs; + } + + /** + * Method to create a map of all references to classes in the given function. Classes are, for this purpose, referenced if they + * a vftable belonging to a class is referenced or if a constructor/destructor function from a class is called + * @param recoveredClass the given class + * @param function the given function + * @return Map of Address references to Class object for the given function + * @throws CancelledException when cancelled + */ + public Map getReferenceToClassMap(RecoveredClass recoveredClass, + Function function) throws CancelledException { + + Map referenceToParentMap = new HashMap(); + + List
vftableRefs = functionToVftableRefsMap.get(function); + + if (vftableRefs == null) { + return referenceToParentMap; + } + + // iterate through all vftable refs in the function and add it to ref/Parent map + Iterator
vftableRefIterator = vftableRefs.iterator(); + while (vftableRefIterator.hasNext()) { + + monitor.checkCanceled(); + + Address vftableRef = vftableRefIterator.next(); + Address vftableAddress = extraUtils.getSingleReferencedAddress(vftableRef); + + if (vftableAddress == null) { + continue; + } + + RecoveredClass parentClass = vftableToClassMap.get(vftableAddress); + referenceToParentMap.put(vftableRef, parentClass); + } + + // remove duplicate vftable refs (occasionally there are LEA then MOV of same vftable address + // a few intructions of each other. It confuses later processes to have both. + referenceToParentMap = dedupeMap(referenceToParentMap); + + // iterate through all the ref/addr pairs of called constructors/destructors in the function + // and get the class the constructor/destructor belongs to and add the ref/Parent pair to the + // same map as the vftable refs above + List refAddrPairsToCalledParents = + functionToCalledConsDestRefAddrPairMap.get(function); + + // if no calls then just return the ones found in the vftable section above + if (refAddrPairsToCalledParents == null) { + return referenceToParentMap; + } + + Iterator refAddPairIterator = refAddrPairsToCalledParents.iterator(); + + while (refAddPairIterator.hasNext()) { + + monitor.checkCanceled(); + + ReferenceAddressPair parentRefAddrPair = refAddPairIterator.next(); + + Address parentConstructorAddress = parentRefAddrPair.getDestination(); + Function parentConstructor = + program.getFunctionManager().getFunctionAt(parentConstructorAddress); + + if (parentConstructor.isThunk()) { + parentConstructor = parentConstructor.getThunkedFunction(true); + } + + RecoveredClass ancestorClass = + getAncestorClassWithGivenFunction(recoveredClass, parentConstructor); + + if (ancestorClass == null) { + continue; + } + + referenceToParentMap.put(parentRefAddrPair.getSource(), ancestorClass); + } + + return referenceToParentMap; + } + + private Map dedupeMap(Map map) + throws CancelledException { + + // Sort the vftable refs in the order they appear in the function + Set
vftableRefs = map.keySet(); + List
vftableRefList = new ArrayList
(vftableRefs); + Collections.sort(vftableRefList); + + Iterator
vftableRefIterator = vftableRefList.iterator(); + RecoveredClass lastClass = null; + Address lastVftableRef = null; + while (vftableRefIterator.hasNext()) { + monitor.checkCanceled(); + Address vftableRef = vftableRefIterator.next(); + RecoveredClass currentClass = map.get(vftableRef); + + if (lastClass != null && lastClass.equals(currentClass)) { + // if vftable refs are within a few instructions, dedupe map + if (numInstructionsApart(lastVftableRef, vftableRef) <= 3) { + map.remove(vftableRef); + } + } + + lastClass = currentClass; + lastVftableRef = vftableRef; + } + + return map; + } + + private int numInstructionsApart(Address ref1, Address ref2) throws CancelledException { + + Instruction instruction = program.getListing().getInstructionAfter(ref1); + int numApart = 0; + while (instruction != null && !instruction.contains(ref2)) { + monitor.checkCanceled(); + numApart++; + instruction = program.getListing().getInstructionAfter(instruction.getMaxAddress()); + } + return numApart; + } + + /** + * Method to figure out which of the multiple parents of a class contain the given function in their class + * @param recoveredClass the given class with multiple parents + * @param function the given function that is in one of the parent classes + * @return the parent class that the given function belongs to + * @throws CancelledException if cancelled + */ + private RecoveredClass getAncestorClassWithGivenFunction(RecoveredClass recoveredClass, + Function function) throws CancelledException { + + List classList = functionToClassesMap.get(function); + + if (classList == null) { + return null; + } + + if (classList.contains(recoveredClass)) { + classList.remove(recoveredClass); + } + + if (classList.size() == 0) { + return null; + } + + if (classList.size() == 1) { + return classList.get(0); + } + + List parentClasses = + new ArrayList(recoveredClass.getClassHierarchyMap().keySet()); + + // try direct parents first + Iterator parentsIterator = parentClasses.iterator(); + while (parentsIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parentClass = parentsIterator.next(); + List constructorDestructorList = + new ArrayList(parentClass.getConstructorList()); + constructorDestructorList.addAll(parentClass.getDestructorList()); + if (constructorDestructorList.contains(function)) { + return parentClass; + } + } + + // if not found in direct parents, try all ancestors + List ancestorClasses = recoveredClass.getClassHierarchy(); + + if (ancestorClasses.size() <= 1) { + return recoveredClass; + } + + ListIterator ancestorIterator = ancestorClasses.listIterator(1); + while (ancestorIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass ancestorClass = ancestorIterator.next(); + + // already checked the parents + if (parentClasses.contains(ancestorClass)) { + continue; + } + List constructorDestructorList = + new ArrayList(ancestorClass.getConstructorList()); + constructorDestructorList.addAll(ancestorClass.getDestructorList()); + if (constructorDestructorList.contains(function)) { + return ancestorClass; + } + } + + return null; + } + + /** + * Method to get a list of addresses that are references from the given address + * @param address the given address + * @return a list of addresses that are references from the given address + */ + private List
getReferenceFromAddresses(Address address) { + + Reference[] referencesFrom = program.getReferenceManager().getReferencesFrom(address); + + // get only the address references at the given address (ie no stack refs, ...) + List
refFromAddresses = new ArrayList
(); + for (Reference referenceFrom : referencesFrom) { + if (referenceFrom.isMemoryReference()) { + refFromAddresses.add(referenceFrom.getToAddress()); + } + } + + return refFromAddresses; + } + + /** + * Retrieve the first stored vftable from the pcodeOps in the list + * @param storedPcodeOps list of offset/PcodeOp pairs + * @return first referenced vftable address + * @throws CancelledException if cancelled + */ + public Address getStoredVftableAddress(List storedPcodeOps) + throws CancelledException { + + if (storedPcodeOps.size() > 0) { + Iterator iterator = storedPcodeOps.iterator(); + // figure out if vftable is referenced + while (iterator.hasNext()) { + monitor.checkCanceled(); + OffsetPcodeOpPair offsetPcodeOpPair = iterator.next(); + PcodeOp pcodeOp = offsetPcodeOpPair.getPcodeOp(); + Varnode storedValue = pcodeOp.getInput(2); + Address vftableAddress = decompilerUtils.getAssignedAddressFromPcode(storedValue); + if (vftableAddress != null && vftableToClassMap.containsKey(vftableAddress)) { + return vftableAddress; + } + } + } + return null; + } + + /** + * Method to get a list of addresses that reference the given vftable address + * @param vftableAddress the given vftable address + * @return list of addresses that reference the given vftable address + * @throws CancelledException if cancelled + */ + public List
getReferencesToVftable(Address vftableAddress) throws CancelledException { + + List
referencesToVftable = new ArrayList<>(); + + ReferenceIterator iterator = program.getReferenceManager().getReferencesTo(vftableAddress); + + while (iterator.hasNext()) { + + monitor.checkCanceled(); + + Reference reference = iterator.next(); + Address vtableReference = reference.getFromAddress(); + referencesToVftable.add(vtableReference); + } + + 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 + */ + 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; + } + + 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 + * @return true if the given function is an inlined function in any class, false if it is not an + * inlined function in any class + * @throws CancelledException if cancelled + */ + public boolean isInlineDestructorOrIndeterminateInAnyClass(Function function) + throws CancelledException { + + List functionClasses = getClasses(function); + if (functionClasses == null) { + Msg.debug(this, "no function to class map for " + function.getEntryPoint()); + return true; + } + Iterator functionClassesIterator = functionClasses.iterator(); + while (functionClassesIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = functionClassesIterator.next(); + + if (recoveredClass.getInlinedDestructorList().contains(function)) { + return true; + } + if (recoveredClass.getIndeterminateInlineList().contains(function)) { + return true; + } + } + return false; + } + + /** + * Method to create mappings for the current class and use the decompiler + * to figure out class member data information + * @param recoveredClass class that function belongs to + * @param function current function to process + * @throws CancelledException if cancelled + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if issues setting return type + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public void gatherClassMemberDataInfoForFunction(RecoveredClass recoveredClass, + Function function) throws CancelledException, DuplicateNameException, + InvalidInputException, CircularDependencyException { + + // save off param and return information + Parameter[] originalParameters = function.getParameters(); + DataType[] originalTypes = new DataType[originalParameters.length]; + SourceType[] originalSources = new SourceType[originalParameters.length]; + for (int i = 0; i < originalParameters.length; i++) { + monitor.checkCanceled(); + originalTypes[i] = originalParameters[i].getDataType(); + originalSources[i] = originalParameters[i].getSource(); + } + DataType originalReturnType = function.getReturnType(); + SourceType originalReturnSource = function.getReturn().getSource(); + + Namespace originalNamespace = function.getParentNamespace(); + + // temporarily remove class data type or other empty structures from return and params + // so they do not skew the structure contents from FillOutStructureCmd + temporarilyReplaceEmptyStructures(function, recoveredClass.getClassNamespace()); + + // create maps and updates class member data information using structure and pcode info returned + // from FillOutStructureCmd + updateMapsAndClassMemberDataInfo(function, recoveredClass); + + // replace namespace if it was removed + if (!function.getParentNamespace().equals(originalNamespace)) { + function.setParentNamespace(originalNamespace); + } + + // put back return and param data types that were temporarily removed + Parameter[] parameters = function.getParameters(); + for (int i = 0; i < parameters.length; i++) { + monitor.checkCanceled(); + if (parameters[i].getName().equals("this")) { + continue; + } + if (!parameters[i].getDataType().equals(originalTypes[i])) { + parameters[i].setDataType(originalTypes[i], originalSources[i]); + } + } + if (!function.getReturnType().equals(originalReturnType)) { + function.setReturnType(originalReturnType, originalReturnSource); + } + } + + /** + * Method to update the given class's maps and class member data using the given function info + * @param function the given function + * @param recoveredClass the given class + * @throws CancelledException if cancelled + */ + private void updateMapsAndClassMemberDataInfo(Function function, RecoveredClass recoveredClass) + throws CancelledException { + + List
vftableReferenceList = getVftableReferences(function); + if (vftableReferenceList == null) { + Msg.debug(this, "In update maps: function to class map doesn't exist for " + + function.getEntryPoint().toString()); + return; + } + Collections.sort(vftableReferenceList); + Address firstVftableReference = vftableReferenceList.get(0); + if (firstVftableReference == null) { + return; + } + + // make sure first vftable ref is in the given class (if inlined it might not be) + Address vftableAddress = getVftableAddress(firstVftableReference); + RecoveredClass vftableClass = getVftableClass(vftableAddress); + if (!vftableClass.equals(recoveredClass)) { + + Msg.debug(this, + "updating struct for " + recoveredClass.getName() + + " but first vftable in function " + function.getEntryPoint().toString() + + " is in class " + vftableClass.getName()); + + return; + } + + getStructureFromDecompilerPcode(recoveredClass, function, firstVftableReference); + + } + + /** + * Method to retrieve a filled-in structure associated with the given function's high variable + * that stores the given first vftable reference address in the given function. + * @param recoveredClass the given class + * @param function the given function + * @param firstVftableReference the first vftable reference address in the given function + * @throws CancelledException if cancelled + */ + public void getStructureFromDecompilerPcode(RecoveredClass recoveredClass, Function function, + Address firstVftableReference) throws CancelledException { + + // get the decompiler highFunction + HighFunction highFunction = decompilerUtils.getHighFunction(function); + + if (highFunction == null) { + return; + + } + + List highVariables = new ArrayList(); + + // if there are params add the first or the "this" param to the list to be checked first + // It is the most likely to store the vftablePtr + if (highFunction.getFunctionPrototype().getNumParams() > 0) { + + HighVariable thisParam = + highFunction.getFunctionPrototype().getParam(0).getHighVariable(); + if (thisParam != null) { + highVariables.add(thisParam); + } + } + + // add the other high variables that store vftable pointer + highVariables.addAll( + getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); + Iterator highVariableIterator = highVariables.iterator(); + + Address vftableAddress = null; + while (highVariableIterator.hasNext()) { + + HighVariable highVariable = highVariableIterator.next(); + monitor.checkCanceled(); + + FillOutStructureCmd fillCmd = new FillOutStructureCmd(program, location, tool); + + Structure structure = fillCmd.processStructure(highVariable, function); + + NoisyStructureBuilder componentMap = fillCmd.getComponentMap(); + + List stores = fillCmd.getStorePcodeOps(); + stores = removePcodeOpsNotInFunction(function, stores); + + // this method checks the storedPcodeOps to see if one is the desired vftable address + vftableAddress = getStoredVftableAddress(stores); + if (vftableAddress != null && + getVftableAddress(firstVftableReference).equals(vftableAddress)) { + + if (structure != null) { + recoveredClass.updateClassMemberStructure(structure); + recoveredClass.updateClassMemberStructureUndefineds(componentMap); + } + + List loads = fillCmd.getLoadPcodeOps(); + loads = removePcodeOpsNotInFunction(function, loads); + + //functionToStorePcodeOps.put(function, stores); + updateFunctionToStorePcodeOpsMap(function, stores); + updateFunctionToLoadPcodeOpsMap(function, loads); + return; + } + + Msg.debug(this, "Could not find variable pointing to vftable in " + + function.getEntryPoint().toString()); + + } + + } + + /** + * Method to determine which variable in the decompiler stores the vftable address + * @param highFunction the decompiler high function + * @param vftableReference the address that points to a vftable + * @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: Can I refactor to use the same methodology as getAssignedAddressFrompcode or getStoredVftableAddress + private List getVariableThatStoresVftablePointer(HighFunction highFunction, + Address vftableReference) throws CancelledException { + + List highVars = new ArrayList(); + + Iterator pcodeOps = highFunction.getPcodeOps(); + while (pcodeOps.hasNext()) { + monitor.checkCanceled(); + PcodeOp pcodeOp = pcodeOps.next(); + if (pcodeOp.getOpcode() == PcodeOp.STORE) { + + Address address = getTargetAddressFromPcodeOp(pcodeOp); + if (address.equals(vftableReference)) { + + Varnode[] inputs = pcodeOp.getInputs(); + for (Varnode input : inputs) { + monitor.checkCanceled(); + if (input.getHigh() != null) { + highVars.add(input.getHigh()); + } + } + } + } + } + return highVars; + + } + + /** + * temporarily change the function signature of the given constructor or destructor to replace + * any empty structure with same size undefined datatype and to also remove the functin from + * its namespace to remove the empty structure from the this param. This is so that the + * class member data calculations are made without bad info + * @param function the given function + * @param classNamespace the given class namespace + * @throws CancelledException if cancelled + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if issue making function thiscall + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + private void temporarilyReplaceEmptyStructures(Function function, Namespace classNamespace) + throws CancelledException, DuplicateNameException, InvalidInputException, + CircularDependencyException { + + int parameterCount = function.getParameterCount(); + for (int i = 0; i < parameterCount; i++) { + monitor.checkCanceled(); + + // if this call - temporarily put in global namespace to remove class structure + // in order to get unbiased pcode store information + if (function.getParameter(i).getName().equals("this")) { + function.setParentNamespace(globalNamespace); + continue; + } + + DataType dataType = function.getParameter(i).getDataType(); + + if (!extraUtils.isPointerToEmptyStructure(dataType)) { + continue; + } + + PointerDataType ptrUndefined = extraUtils.createPointerToUndefinedDataType(dataType); + if (ptrUndefined != null) { + function.getParameter(i).setDataType(ptrUndefined, SourceType.ANALYSIS); + } + + } + + // Next check the return type to see if it is the empty structure + DataType returnType = function.getReturnType(); + if (extraUtils.isPointerToEmptyStructure(returnType)) { + PointerDataType ptrUndefined = + extraUtils.createPointerToUndefinedDataType(returnType); + if (ptrUndefined != null) { + function.setReturnType(ptrUndefined, SourceType.ANALYSIS); + } + } + + } + + + + /** + * Method to determine if the given possible ancestor is an ancestor of any of the listed classes + * @param recoveredClasses List of classes + * @param possibleAncestor possible ancestor of one of the listed classes + * @return true if ancestor of one of the listed classes, false otherwise + * @throws Exception if one of the classes has empty class hierarchy + */ + // TODO: update with new exception once created + public boolean isClassAnAncestorOfAnyOnList(List recoveredClasses, + RecoveredClass possibleAncestor) throws Exception { + + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + if (isClassAnAncestor(recoveredClass, possibleAncestor)) { + return true; + } + } + return false; + } + + /** + * Method to determine if a class is an ancestor of another class + * @param recoveredClass the class with possible ancestor + * @param possibleAncestorClass the class that might be ancestor of recoveredClass + * @return true if possibleAncestorClass is an ancestor of recoveredClass, false otherwise + * @throws Exception if class hierarchy is empty + */ + // TODO: make my own exception here + private boolean isClassAnAncestor(RecoveredClass recoveredClass, + RecoveredClass possibleAncestorClass) throws Exception { + + List classHierarchy = + new ArrayList(recoveredClass.getClassHierarchy()); + + if (classHierarchy.isEmpty()) { + throw new Exception( + recoveredClass.getName() + " should not have an empty class hierarchy"); + } + + //remove self + classHierarchy.remove(recoveredClass); + + if (classHierarchy.contains(possibleAncestorClass)) { + return true; + } + return false; + } + + /** + * Method to return subList of offset/pcodeOp pairs from the given list that are contained in the current function + * @param function The given function + * @param pcodeOps The list of pcodeOps to filter + * @return List of pcodeOps from given list that are contained in given function + * @throws CancelledException when cancelled + */ + public List removePcodeOpsNotInFunction(Function function, + List pcodeOps) throws CancelledException { + + Iterator pcodeOpsIterator = pcodeOps.iterator(); + while (pcodeOpsIterator.hasNext()) { + monitor.checkCanceled(); + OffsetPcodeOpPair offsetPcodeOpPair = pcodeOpsIterator.next(); + PcodeOp pcodeOp = offsetPcodeOpPair.getPcodeOp(); + Address pcodeOpAddress = pcodeOp.getSeqnum().getTarget(); + if (!function.getBody().contains(pcodeOpAddress)) { + pcodeOpsIterator.remove(); + } + } + return pcodeOps; + } + + /** + * Method to get the listing address that the given PcodeOp is associated with + * @param pcodeOp the given PcodeOp + * @return the address the given PcodeOp is associated with + */ + public Address getTargetAddressFromPcodeOp(PcodeOp pcodeOp) { + return pcodeOp.getSeqnum().getTarget(); + } + + public Function getCalledFunctionFromCallPcodeOp(PcodeOp calledPcodeOp) { + + Function calledFunction = + api.getFunctionAt(calledPcodeOp.getInput(0).getAddress()); + if (calledFunction == null) { + return null; + } + + return calledFunction; + } + + /** + * method to update the two given classes with their related class hierarchy + * @param parentClass parent of childClass + * @param childClass child of parentClass + */ + public void updateClassWithParent(RecoveredClass parentClass, RecoveredClass childClass) { + + // return if they are already a known parent/child pair + if (parentClass.getChildClasses().contains(childClass) && + childClass.getParentList().contains(parentClass)) { + return; + } + + childClass.addParent(parentClass); + + childClass.setHasParentClass(true); + parentClass.setHasChildClass(true); + parentClass.addChildClass(childClass); + + return; + } + + /** + * Method to retrieve only the classes that have vfunctions from the given list of classes + * @param recoveredClasses the given list of classes + * @return a list of classes that have vfunctions + * @throws CancelledException if cancelled + */ + public List getClassesWithVFunctions(List recoveredClasses) + throws CancelledException { + + List classesWithVFunctions = new ArrayList(); + + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + if (recoveredClass.hasVftable()) { + classesWithVFunctions.add(recoveredClass); + } + } + + return classesWithVFunctions; + } + + /** + * Method that returns a list of all parents with virtual functions for the given class + * @param recoveredClass given class + * @return list of all parents with virtual functions for given class or empty list if none + * @throws CancelledException when cancelled + */ + public List getParentsWithVirtualFunctions(RecoveredClass recoveredClass) + throws CancelledException { + + List parentsWithVFunctions = new ArrayList(); + + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + + // no parents so return empty list + if (classHierarchyMap.isEmpty()) { + return parentsWithVFunctions; + } + + List parentClassList = + new ArrayList(classHierarchyMap.keySet()); + + Iterator parentIterator = parentClassList.iterator(); + while (parentIterator.hasNext()) { + + monitor.checkCanceled(); + + RecoveredClass parentClass = parentIterator.next(); + if (parentClass.hasVftable()) { + parentsWithVFunctions.add(parentClass); + } + } + + return parentsWithVFunctions; + } + + /** + * Method to get list of the given class's ancestors that have virtual functions or functions inherited virtual functions(ie a vftable) + * @param recoveredClass the given class + * @return list of the given class's ancestors that have virtual functions or functions inherited virtual functions(ie a vftable) + * @throws CancelledException if cancelled + */ + public List getAncestorsWithVirtualFunctions(RecoveredClass recoveredClass) + throws CancelledException { + + List ancestorsWithVFunctions = new ArrayList(); + + // no parents so return empty list + if (!recoveredClass.hasParentClass()) { + return ancestorsWithVFunctions; + } + + List classHierarchyList = recoveredClass.getClassHierarchy(); + + Iterator ancestorIterator = classHierarchyList.iterator(); + while (ancestorIterator.hasNext()) { + + monitor.checkCanceled(); + + RecoveredClass parentClass = ancestorIterator.next(); + if (parentClass.hasVftable()) { + ancestorsWithVFunctions.add(parentClass); + } + } + + return ancestorsWithVFunctions; + } + + /** + * Method to get ancestors that do not have vfunctions + * @param recoveredClass the given class + * @return true if any of the given class's ancestors are inherited virtually, false otherwise + * @throws CancelledException if cancelled + */ + public List getAncestorsWithoutVfunctions(RecoveredClass recoveredClass) + throws CancelledException { + + List ancestorsWithoutVfunctions = new ArrayList(); + + List classHierarchy = recoveredClass.getClassHierarchy(); + + Iterator hierarchyIterator = classHierarchy.iterator(); + + while (hierarchyIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestorClass = hierarchyIterator.next(); + + // skip self + if (ancestorClass.equals(recoveredClass)) { + continue; + } + if (!ancestorClass.hasVftable()) { + ancestorsWithoutVfunctions.add(ancestorClass); + } + } + + return ancestorsWithoutVfunctions; + } + + /** + * Method to test whether class has ancestor without virtual functions or not + * @param recoveredClass given class object + * @return true if class has an ancestor class without virtual functions + * @throws CancelledException if cancelled + */ + public boolean hasNonVirtualFunctionAncestor(RecoveredClass recoveredClass) + throws CancelledException { + List classHierarchy = recoveredClass.getClassHierarchy(); + Iterator recoveredClassIterator = classHierarchy.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass currentClass = recoveredClassIterator.next(); + if (!currentClass.hasVftable()) { + return true; + } + } + return false; + } + + /** + * Method to get all ancestor class constructors for the given class + * @param recoveredClass given class + * @return List of all functions that are constructors of an ancestor class of given class + * @throws CancelledException if script is cancelled + */ + public List getAllAncestorConstructors(RecoveredClass recoveredClass) + throws CancelledException { + + List allAncestorConstructors = new ArrayList(); + + List classHierarchy = recoveredClass.getClassHierarchy(); + ListIterator classHierarchyIterator = classHierarchy.listIterator(1); + + while (classHierarchyIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass currentClass = classHierarchyIterator.next(); + + List constructorList = + new ArrayList(currentClass.getConstructorList()); + constructorList.addAll(currentClass.getInlinedConstructorList()); + Iterator constructors = constructorList.iterator(); + while (constructors.hasNext()) { + monitor.checkCanceled(); + Function constructor = constructors.next(); + if (!allAncestorConstructors.contains(constructor)) { + allAncestorConstructors.add(constructor); + } + } + } + return allAncestorConstructors; + } + + /** + * Method to retrieve a list of destructors for the ancestors of the given class + * @param recoveredClass the given class + * @return a list of destructors for the ancestors of the given class + * @throws CancelledException if cancelled + */ + public List getAncestorDestructors(RecoveredClass recoveredClass) + throws CancelledException { + + List allAncestorDestructors = new ArrayList(); + + List classHierarchy = recoveredClass.getClassHierarchy(); + ListIterator classHierarchyIterator = classHierarchy.listIterator(1); + + while (classHierarchyIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parentClass = classHierarchyIterator.next(); + + List destructorList = + new ArrayList(parentClass.getDestructorList()); + destructorList.addAll(parentClass.getInlinedDestructorList()); + Iterator destructors = destructorList.iterator(); + while (destructors.hasNext()) { + monitor.checkCanceled(); + Function destructor = destructors.next(); + if (!allAncestorDestructors.contains(destructor)) { + allAncestorDestructors.add(destructor); + } + } + } + return allAncestorDestructors; + } + + /** + * Method to retrieve all constructors of descendants of the given class + * @param recoveredClass the given class + * @return a list of all constructors of descendants of the given class + * @throws CancelledException if cancelled + */ + public List getAllDescendantConstructors(RecoveredClass recoveredClass) + throws CancelledException { + + List allDescendantConstructors = new ArrayList(); + + List childClasses = recoveredClass.getChildClasses(); + Iterator childClassIterator = childClasses.iterator(); + while (childClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass childClass = childClassIterator.next(); + + List constructorList = + new ArrayList(childClass.getConstructorList()); + constructorList.addAll(childClass.getInlinedConstructorList()); + Iterator constructors = constructorList.iterator(); + while (constructors.hasNext()) { + monitor.checkCanceled(); + Function constructor = constructors.next(); + if (!allDescendantConstructors.contains(constructor)) { + allDescendantConstructors.add(constructor); + } + } + allDescendantConstructors.addAll(getAllDescendantConstructors(childClass)); + } + + return allDescendantConstructors; + } + + /** + * Method to retrieve all destructors of descendants of the given class + * @param recoveredClass the given class + * @return a list of all destructors of descendants of the given class + * @throws CancelledException if cancelled + */ + public List getAllDescendantDestructors(RecoveredClass recoveredClass) + throws CancelledException { + + List allDescendantDestructors = new ArrayList(); + + List childClasses = recoveredClass.getChildClasses(); + Iterator childClassIterator = childClasses.iterator(); + while (childClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass childClass = childClassIterator.next(); + + List destructorList = new ArrayList(childClass.getDestructorList()); + destructorList.addAll(childClass.getInlinedDestructorList()); + Iterator destructors = destructorList.iterator(); + while (destructors.hasNext()) { + monitor.checkCanceled(); + Function destructor = destructors.next(); + if (!allDescendantDestructors.contains(destructor)) { + allDescendantDestructors.add(destructor); + } + } + allDescendantDestructors.addAll(getAllDescendantDestructors(childClass)); + } + + return allDescendantDestructors; + } + + /** + * Method to retrieve a list of possible parent class constructors to the given function + * @param function the given function + * @return a list of possible parent class constructors to the given function + */ + public List getPossibleParentConstructors(Function function) + throws CancelledException { + + List possibleParentConstructors = new ArrayList(); + + List refAddrPairList = getCalledConstDestRefAddrPairs(function); + + List
vftableReferenceList = getVftableReferences(function); + if (vftableReferenceList == null) { + return possibleParentConstructors; + } + + Address minVftableReference = extraUtils.getMinimumAddressOnList(vftableReferenceList); + Iterator iterator = refAddrPairList.iterator(); + + while (iterator.hasNext()) { + monitor.checkCanceled(); + ReferenceAddressPair refAddrPair = iterator.next(); + Address sourceAddr = refAddrPair.getSource(); + if (sourceAddr.compareTo(minVftableReference) < 0) { + Function calledFunction = api.getFunctionAt(refAddrPair.getDestination()); + if (calledFunction != null) { + possibleParentConstructors.add(calledFunction); + } + } + } + return possibleParentConstructors; + } + + /** + * Method to retrieve a single common function on both lists + * @param list1 first list of functions + * @param list2 second list of functions + * @return single function if there is one function on both lists, null if there are none or + * more than one + */ + public Function getFunctionOnBothLists(List list1, List list2) { + + List commonFunctions = getFunctionsOnBothLists(list1, list2); + + if (commonFunctions.size() == 1) { + return commonFunctions.get(0); + } + // if none or more than one return null + return null; + } + + public List getFunctionsOnBothLists(List list1, List list2) { + List commonFunctions = + list1.stream().distinct().filter(list2::contains).collect(Collectors.toList()); + + return commonFunctions; + } + + /** + * Method to retrieve a set of functions contained in both of the given sets of functions + * @param set1 the first set of functions + * @param set2 the second set of functions + * @return a set of functions contained in both of the given sets of functions + */ + public Set getFunctionsContainedInBothSets(Set set1, Set set2) { + Set commonFunctions = + set1.stream().distinct().filter(set2::contains).collect(Collectors.toSet()); + + return commonFunctions; + + } + + /** + * Method to retrieve a list of possible parent class destructors to the given function + * @param function the given function + * @return a list of possible parent class destructors to the given function + * @throws CancelledException if cancelled + */ + public List getPossibleParentDestructors(Function function) + throws CancelledException { + + List possibleParentDestructors = new ArrayList(); + + List refAddrPairList = getCalledConstDestRefAddrPairs(function); + + List
vftableReferenceList = getVftableReferences(function); + if (vftableReferenceList == null) { + return possibleParentDestructors; + } + + Address maxVftableReference = extraUtils.getMaximumAddressOnList(vftableReferenceList); + Iterator iterator = refAddrPairList.iterator(); + + while (iterator.hasNext()) { + monitor.checkCanceled(); + ReferenceAddressPair refAddrPair = iterator.next(); + Address sourceAddr = refAddrPair.getSource(); + if (sourceAddr.compareTo(maxVftableReference) > 0) { + Function calledFunction = extraUtils.getFunctionAt(refAddrPair.getDestination()); + if (calledFunction != null) { + possibleParentDestructors.add(calledFunction); + } + } + } + return possibleParentDestructors; + } + + /** + * Method to determine the constructors/destructors using known parent + * @param recoveredClass RecoveredClass object + * @param parentClass possible parent class of the given RecoveredClass + * @return + * @throws CancelledException if cancelled + * @throws InvalidInputException if issue setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public boolean processConstructorsAndDestructorsUsingParent(RecoveredClass recoveredClass, + RecoveredClass parentClass) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + if (parentClass == null) { + return false; + } + + Map childParentConstructorMap = new HashMap(); + Map childParentDestructorMap = new HashMap(); + + List constDestFunctions = + new ArrayList(recoveredClass.getConstructorOrDestructorFunctions()); + constDestFunctions.removeAll(recoveredClass.getIndeterminateInlineList()); + + List parentConstDestFunctions = parentClass.getConstructorOrDestructorFunctions(); + + List parentConstructors = getAllClassConstructors(parentClass); + List parentDestructors = getAllClassDestructors(parentClass); + List childConstructors = getAllClassConstructors(recoveredClass); + List childDestructors = getAllClassDestructors(recoveredClass); + + Iterator constDestIterator = constDestFunctions.iterator(); + while (constDestIterator.hasNext()) { + monitor.checkCanceled(); + + Function constDestFunction = constDestIterator.next(); + + // based on call order get possible parent constructors for the given function + List possibleParentConstructors = + getPossibleParentConstructors(constDestFunction); + + // remove any known destructors since they can't also be constructors - rarely these + // show up on possible const list + possibleParentConstructors.removeAll(parentDestructors); + + Function parentConstructor = + getFunctionOnBothLists(possibleParentConstructors, parentConstDestFunctions); + + // another sanity check - make sure child function isn't a known destructor + if (parentConstructor != null && !childDestructors.contains(constDestFunction)) { + childParentConstructorMap.put(constDestFunction, parentConstructor); + continue; + } + + // based on call order get possible parent destructors for the given function + List possibleParentDestructors = + getPossibleParentDestructors(constDestFunction); + + // remove any known constructors since they can't also be destructors - rarely these + // show up on possible dest list + possibleParentDestructors.removeAll(parentConstructors); + + Function parentDestructor = + getFunctionOnBothLists(possibleParentDestructors, parentConstDestFunctions); + + // another sanity check - make sure child function isn't a known constructor + if (parentDestructor != null && !childConstructors.contains(constDestFunction)) { + childParentDestructorMap.put(constDestFunction, parentDestructor); + continue; + } + } + + // check to make sure there is no overlap in the poss c and poss d maps + Set constructorKeySet = childParentConstructorMap.keySet(); + Set destructorKeySet = childParentDestructorMap.keySet(); + Set functionsContainedInBothSets = + getFunctionsContainedInBothSets(constructorKeySet, destructorKeySet); + + if (functionsContainedInBothSets.size() > 0) { + constructorKeySet.removeAll(functionsContainedInBothSets); + destructorKeySet.removeAll(functionsContainedInBothSets); + if (constructorKeySet.isEmpty() && destructorKeySet.isEmpty()) { + return false; + } + } + + // once all checks pass, add both the child and parent constructors to their class + // constructor list and remove from the indeterminate lists + // the addConstructor method processes the offsets and types for the initialized class data + Iterator childConstructorIterator = constructorKeySet.iterator(); + while (childConstructorIterator.hasNext()) { + monitor.checkCanceled(); + Function childConstructor = childConstructorIterator.next(); + addConstructorToClass(recoveredClass, childConstructor); + recoveredClass.removeIndeterminateConstructorOrDestructor(childConstructor); + Function parentConstructor = childParentConstructorMap.get(childConstructor); + addConstructorToClass(parentClass, parentConstructor); + parentClass.removeIndeterminateConstructorOrDestructor(parentConstructor); + } + + // Do the same for the child/parent destructors + Iterator childDestructorIterator = destructorKeySet.iterator(); + while (childDestructorIterator.hasNext()) { + monitor.checkCanceled(); + Function childDestructor = childDestructorIterator.next(); + addDestructorToClass(recoveredClass, childDestructor); + recoveredClass.removeIndeterminateConstructorOrDestructor(childDestructor); + Function parentDestructor = childParentDestructorMap.get(childDestructor); + addDestructorToClass(parentClass, parentDestructor); + parentClass.removeIndeterminateConstructorOrDestructor(parentDestructor); + } + return true; + + } + + /** + * Method to retrieve all types of class destructors including normal destructors, non-this + * destructors and inline destructors(does not include deleting destructors since they are + * really a vfunction) + * @param recoveredClass the given class + * @return the list of all destructors for the given class + */ + public List getAllClassDestructors(RecoveredClass recoveredClass) { + + List allClassDestructors = new ArrayList(); + allClassDestructors.addAll(recoveredClass.getDestructorList()); + allClassDestructors.addAll(recoveredClass.getNonThisDestructors()); + allClassDestructors.addAll(recoveredClass.getInlinedDestructorList()); + + return allClassDestructors; + } + + /** + * Method to retrieve all types of class constructors including normal constructors and inline + * constructors + * @param recoveredClass the given class + * @return the list of all constructors for the given class + */ + public List getAllClassConstructors(RecoveredClass recoveredClass) { + + List allClassConstructors = new ArrayList(); + + allClassConstructors.addAll(recoveredClass.getConstructorList()); + allClassConstructors.addAll(recoveredClass.getInlinedConstructorList()); + + return allClassConstructors; + + } + + /** + * Method to add constructor function to the given class and collect member data information + * @param recoveredClass given class + * @param constructorFunction given function + * @throws CancelledException if cancelled + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public void addConstructorToClass(RecoveredClass recoveredClass, Function constructorFunction) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + // skip if already a constructor for this class + if (recoveredClass.getConstructorList().contains(constructorFunction)) { + return; + } + + // create vftable mapping for any class that didn't have a constructor when the + // original mappings were created + if (recoveredClass.getOrderToVftableMap().size() == 0) { + createVftableOrderMapping(recoveredClass); + } + + // If not already, make function a thiscall + // TODO: rethink doing this here -change to when creating constructor in class + // since some later get changed to inlines + extraUtils.makeFunctionThiscall(constructorFunction); + + recoveredClass.addConstructor(constructorFunction); + addToAllConstructors(constructorFunction); + } + + /** + * Method to add inlined constructor function to the given class + * @param recoveredClass given class + * @param inlinedConstructorFunction given function + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void addInlinedConstructorToClass(RecoveredClass recoveredClass, + Function inlinedConstructorFunction) + throws InvalidInputException, DuplicateNameException { + + //If not already, make function a thiscall + extraUtils.makeFunctionThiscall(inlinedConstructorFunction); + + recoveredClass.addInlinedConstructor(inlinedConstructorFunction); + addToAllInlinedConstructors(inlinedConstructorFunction); + } + + /** + * Method to add destructor to the given class + * @param recoveredClass given class + * @param destructorFunction given destructor function + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void addDestructorToClass(RecoveredClass recoveredClass, Function destructorFunction) + throws InvalidInputException, DuplicateNameException { + + //If not already, make function a thiscall + extraUtils.makeFunctionThiscall(destructorFunction); + + recoveredClass.addDestructor(destructorFunction); + addToAllDestructors(destructorFunction); + } + + /** + * Method to add inlined destructor to the given class + * @param recoveredClass given class + * @param inlinedDestructorFunction given function + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void addInlinedDestructorToClass(RecoveredClass recoveredClass, + Function inlinedDestructorFunction) + throws InvalidInputException, DuplicateNameException { + + //If not already, make function a thiscall + extraUtils.makeFunctionThiscall(inlinedDestructorFunction); + + recoveredClass.addInlinedDestructor(inlinedDestructorFunction); + addToAllInlinedDestructors(inlinedDestructorFunction); + } + + /** + * Method to create a mapping between the order it appears and the vftable for the given class + * @param recoveredClass the given class + * @throws CancelledException if cancelled + */ + public void createVftableOrderMapping(RecoveredClass recoveredClass) throws CancelledException { + + if (!recoveredClass.hasVftable()) { + return; + } + + List
vftableAddresses = recoveredClass.getVftableAddresses(); + + Map classOffsetToVftableMap = recoveredClass.getClassOffsetToVftableMap(); + + if (classOffsetToVftableMap.size() == 0) { + return; + } + + if (vftableAddresses.size() != classOffsetToVftableMap.size()) { + Msg.debug(this, recoveredClass.getName() + " has " + vftableAddresses.size() + + " vftables but " + classOffsetToVftableMap.size() + " offset to vftable maps"); + } + + List offsetList = new ArrayList(classOffsetToVftableMap.keySet()); + + Collections.sort(offsetList); + + int order = 0; + Iterator offsetIterator = offsetList.iterator(); + while (offsetIterator.hasNext()) { + monitor.checkCanceled(); + Integer offset = offsetIterator.next(); + Address vftableAddress = classOffsetToVftableMap.get(offset); + recoveredClass.addOrderToVftableMapping(order, vftableAddress); + order++; + } + } + + /** + * Method to create a map for each class between the order a vftable is seen in a class and the vftable itself + * @param recoveredClasses list of classes to processes + * @throws CancelledException if cancelled + */ + public void createVftableOrderMap(List recoveredClasses) + throws CancelledException { + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + // create a mapping of the order of the vftable to the vftable address and save to class + createVftableOrderMapping(recoveredClass); + } + } + + /** + * + * @param referenceToClassMap map of references to the class that contains the referenced function + * @param referencesToConstructors list of addresses referring to constructors + * @throws CancelledException if cancelled + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public void createListedConstructorFunctions(Map referenceToClassMap, + List
referencesToConstructors) throws CancelledException, + InvalidInputException, DuplicateNameException, CircularDependencyException { + + Iterator
constructorIterator = referencesToConstructors.iterator(); + while (constructorIterator.hasNext()) { + monitor.checkCanceled(); + + Address constructorReference = constructorIterator.next(); + RecoveredClass recoveredClass = referenceToClassMap.get(constructorReference); + + Function constructor = extraUtils.getReferencedFunction(constructorReference, true); + + if (recoveredClass.getIndeterminateList().contains(constructor)) { + addConstructorToClass(recoveredClass, constructor); + recoveredClass.removeIndeterminateConstructorOrDestructor(constructor); + continue; + } + + if (recoveredClass.getIndeterminateInlineList().contains(constructor)) { + processInlineConstructor(recoveredClass, constructor, referenceToClassMap); + } + } + } + + /** + * Method to process a found inlined constructor + * @param recoveredClass the class being processed + * @param inlinedConstructorFunction the function containing an inlined ancestor constructor + * @throws CancelledException if cancelled + * @throws InvalidInputException if issue setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + + public void processInlineConstructor(RecoveredClass recoveredClass, + Function inlinedConstructorFunction, Map referenceToClassMap) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + if (referenceToClassMap.isEmpty()) { + return; + } + + List
referencesToVftables = new ArrayList
(); + + List
referenceAddresses = new ArrayList
(referenceToClassMap.keySet()); + Iterator
referenceIterator = referenceAddresses.iterator(); + while (referenceIterator.hasNext()) { + monitor.checkCanceled(); + Address reference = referenceIterator.next(); + Address vftableAddress = getVftableAddress(reference); + if (vftableAddress != null) { + referencesToVftables.add(reference); + } + } + + if (referencesToVftables.isEmpty()) { + return; + } + + Collections.sort(referencesToVftables); + + int numRefs = referencesToVftables.size(); + Address lastRef = referencesToVftables.get(numRefs - 1); + + Iterator
refToVtablesIterator = referencesToVftables.iterator(); + while (refToVtablesIterator.hasNext()) { + monitor.checkCanceled(); + Address refToVftable = refToVtablesIterator.next(); + RecoveredClass referencedClass = referenceToClassMap.get(refToVftable); + + // last reference is the constructor + if (refToVftable.equals(lastRef)) { + addConstructorToClass(referencedClass, inlinedConstructorFunction); + } + // the rest are inlined constructors + else { + addInlinedConstructorToClass(referencedClass, inlinedConstructorFunction); + + } + referencedClass.removeIndeterminateInline(inlinedConstructorFunction); + } + + return; + + } + + /** + * Method to process an inlinedDestructor function + * @param recoveredClass the class the inlinedDestructor is in + * @param inlinedDestructorFunction the inlined function + * @param referenceToClassMap + * @throws CancelledException if cancelled + * @throws InvalidInputException if issue setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void processInlineDestructor(RecoveredClass recoveredClass, + Function inlinedDestructorFunction, Map referenceToClassMap) + throws CancelledException, InvalidInputException, DuplicateNameException { + + if (referenceToClassMap.isEmpty()) { + return; + } + + List
referencesToVftables = new ArrayList
(); + + List
referenceAddresses = new ArrayList
(referenceToClassMap.keySet()); + Iterator
referenceIterator = referenceAddresses.iterator(); + while (referenceIterator.hasNext()) { + monitor.checkCanceled(); + Address reference = referenceIterator.next(); + Address vftableAddress = getVftableAddress(reference); + if (vftableAddress != null) { + referencesToVftables.add(reference); + } + } + + if (referencesToVftables.isEmpty()) { + return; + } + + // reverse sort + Collections.sort(referencesToVftables, Collections.reverseOrder()); + + int numRefs = referencesToVftables.size(); + Address lastRef = referencesToVftables.get(numRefs - 1); + + Iterator
refToVtablesIterator = referencesToVftables.iterator(); + while (refToVtablesIterator.hasNext()) { + monitor.checkCanceled(); + Address refToVftable = refToVtablesIterator.next(); + RecoveredClass referencedClass = referenceToClassMap.get(refToVftable); + + // last reference is the constructor + if (refToVftable.equals(lastRef)) { + addDestructorToClass(referencedClass, inlinedDestructorFunction); + } + // the rest are inlined constructors + else { + addInlinedDestructorToClass(referencedClass, inlinedDestructorFunction); + + } + referencedClass.removeIndeterminateInline(inlinedDestructorFunction); + } + + return; + + } + + /** + * Method to get the address that references the first vftable in the given function + * @param function the given function + * @return the address in the given function that references the first referenced vftable or + * null if no vftable is referenced in the given function + */ + public Address getFirstVftableReferenceInFunction(Function function) { + + List
vftableReferenceList = getVftableReferences(function); + + Collections.sort(vftableReferenceList); + + return vftableReferenceList.get(0); + + } + + /** + * Method to make the given function a thiscall + * @param function the given function + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void makeFunctionThiscall(Function function) + throws InvalidInputException, DuplicateNameException { + + if (function.getCallingConventionName().equals("__thiscall")) { + return; + } + + ReturnParameterImpl returnType = + new ReturnParameterImpl(function.getSignature().getReturnType(), program); + + function.updateFunction("__thiscall", returnType, + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, function.getSignatureSource(), + function.getParameters()); + } + + /** + * Method to determine if the given function calls a known constructor or inlined constructor + * @param function the given function + * @return true if function calls a known constructor or inlined constructor, false otherwise + * @throws CancelledException if cancelled + */ + public boolean callsKnownConstructor(Function function) throws CancelledException { + + List calledFunctionRefAddrPairs = + getCalledConstDestRefAddrPairs(function); + + Iterator calledFunctionIterator = + calledFunctionRefAddrPairs.iterator(); + while (calledFunctionIterator.hasNext()) { + + monitor.checkCanceled(); + + ReferenceAddressPair referenceAddressPair = calledFunctionIterator.next(); + + Address calledFunctionAddress = referenceAddressPair.getDestination(); + Function calledFunction = extraUtils.getFunctionAt(calledFunctionAddress); + + if (calledFunction.isThunk()) { + calledFunction = calledFunction.getThunkedFunction(true); + } + + if (getAllConstructors().contains(calledFunction) || + getAllInlinedConstructors().contains(calledFunction)) { + return true; + } + } + return false; + } + + /** + * Method to determine if the given function calls a known constructor or inlined constructor + * @param function the given function + * @return true if function calls a known constructor or inlined constructor, false otherwise + * @throws CancelledException if cancelled + */ + public boolean callsKnownDestructor(Function function) throws CancelledException { + + List calledFunctionRefAddrPairs = + getCalledConstDestRefAddrPairs(function); + + Iterator calledFunctionIterator = + calledFunctionRefAddrPairs.iterator(); + while (calledFunctionIterator.hasNext()) { + + monitor.checkCanceled(); + + ReferenceAddressPair referenceAddressPair = calledFunctionIterator.next(); + + Address calledFunctionAddress = referenceAddressPair.getDestination(); + Function calledFunction = extraUtils.getFunctionAt(calledFunctionAddress); + + if (calledFunction.isThunk()) { + calledFunction = calledFunction.getThunkedFunction(true); + } + + if (getAllDestructors().contains(calledFunction) || + getAllInlinedDestructors().contains(calledFunction)) { + return true; + } + } + return false; + } + + /** + * Method to get the total number of constructors in the given list of classes + * @param recoveredClasses list of classes to process + * @return number of constructor functions in all classes + * @throws CancelledException if cancelled + */ + public int getNumberOfConstructors(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + List constructorList = recoveredClass.getConstructorList(); + total += constructorList.size(); + } + return total; + } + + /** + * Method to return the total number of destructors in the given list of classes + * @param recoveredClasses the list of classes + * @return the total number of destructors in the given list of classes + * @throws CancelledException if cancelled + */ + public int getNumberOfDestructors(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + int numDestructors = recoveredClass.getDestructorList().size(); + total += numDestructors; + } + return total; + } + + /** + * Method to return the total number of inlined destructors in the given list of classes + * @param recoveredClasses the list of classes + * @return the total number of inlined destructors in the given list of classes + * @throws CancelledException if cancelled + */ + public int getNumberOfInlineDestructors(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + int numInlinedDestructors = recoveredClass.getInlinedDestructorList().size(); + total += numInlinedDestructors; + } + return total; + } + + /** + * Method to get the total number of deleting destructors in the given list of classes + * @param recoveredClasses the list of classes + * @return the total number of deleting destructors in the given list of classes + * @throws CancelledException if cancelled + */ + public int getNumberOfDeletingDestructors(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + List deletingDestructors = recoveredClass.getDeletingDestructors(); + total += deletingDestructors.size(); + } + return total; + } + + /** + * Method to retrieve the total number of clone functions assigned to all the classes + * @param recoveredClasses List of classes + * @return total number of clone functions assigned to classes + * @throws CancelledException if cancelled + */ + public int getNumberOfCloneFunctions(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + List cloneFunctions = recoveredClass.getCloneFunctions(); + total += cloneFunctions.size(); + } + return total; + } + + /** + * Method to return the total number of vbase destructors in the given list of classes + * @param recoveredClasses the list of classes + * @return the the total number of vbase destructors in the given list of classes + * @throws CancelledException if cancelled + */ + public int getNumberOfVBaseFunctions(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + Function cloneFunction = recoveredClass.getVBaseDestructor(); + if (cloneFunction != null) { + total++; + } + } + return total; + } + + /** + * Method to get the total number of virtual functions (or functions that inherit from virtual functions) in the given list of classes + * @param recoveredClasses the list of classes + * @return the total number of virtual functions (or functions that inherit from virtual functions) in the given list of classes + * @throws CancelledException if cancelled + */ + public int getNumberOfVirtualFunctions(List recoveredClasses) + throws CancelledException { + + int total = 0; + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + List vfunctionList = recoveredClass.getAllVirtualFunctions(); + if (vfunctionList == null) { + continue; + } + + total += vfunctionList.size(); + + } + return total; + } + + /** + * Method to get a list of functions from the list of classes that could not be determined whether they + * were constructors or destructors + * @param recoveredClasses the list of classes + * @return list of functions from the list of classes that could not be determined whether they + * were constructors or destructors + * @throws CancelledException if cancelled + */ + public List getRemainingIndeterminates(List recoveredClasses) + throws CancelledException { + + List remainingIndeterminates = new ArrayList(); + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + List indeterminateConstructorOrDestructorList = + recoveredClass.getIndeterminateList(); + remainingIndeterminates.addAll(indeterminateConstructorOrDestructorList); + + List indeterminateInlines = recoveredClass.getIndeterminateInlineList(); + remainingIndeterminates.addAll(indeterminateInlines); + + } + return remainingIndeterminates; + } + + /** + * + * @param referenceToClassMap map from reference to class the referenced function is in + * @param referencesToDestructors list of addresses referring to destructors + * @throws CancelledException if cancelled + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void createListedDestructorFunctions(Map referenceToClassMap, + List
referencesToDestructors) + throws CancelledException, InvalidInputException, DuplicateNameException { + + Iterator
destructorIterator = referencesToDestructors.iterator(); + while (destructorIterator.hasNext()) { + monitor.checkCanceled(); + + Address destructorReference = destructorIterator.next(); + RecoveredClass recoveredClass = referenceToClassMap.get(destructorReference); + + Function destructor = extraUtils.getReferencedFunction(destructorReference, true); + + if (recoveredClass.getIndeterminateList().contains(destructor)) { + addDestructorToClass(recoveredClass, destructor); + recoveredClass.removeIndeterminateConstructorOrDestructor(destructor); + continue; + } + + if (recoveredClass.getIndeterminateInlineList().contains(destructor)) { + processInlineDestructor(recoveredClass, destructor, referenceToClassMap); + } + } + } + + /** + * Method to use existing pdb names to assign class constructors and destructors + * @param recoveredClasses List of classes + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if error setting return type + * @throws CancelledException if cancelled + */ + public void assignConstructorsAndDestructorsUsingExistingName( + List recoveredClasses) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List indeterminateFunctions = recoveredClass.getIndeterminateList(); + Iterator functionIterator = indeterminateFunctions.iterator(); + while (functionIterator.hasNext()) { + + monitor.checkCanceled(); + + Function function = functionIterator.next(); + Namespace namespace = function.getParentNamespace(); + if (!namespace.equals(recoveredClass.getClassNamespace())) { + continue; + } + String name = function.getName(); + if (name.equals(recoveredClass.getName())) { + addConstructorToClass(recoveredClass, function); + functionIterator.remove(); + continue; + } + if (name.equals("~" + recoveredClass.getName())) { + addDestructorToClass(recoveredClass, function); + functionIterator.remove(); + continue; + } + } + + } + } + + /** + * Method to determine if a class's identified vbase_destructor is valid or not + * If the class has both a vbase destructor and a regular destructor and the class has + * a non-virtual ancestor, and either the class is the lowest child or has a child with a + * vbase_destructor then it is a valid vbase_destructor. Otherwise, it isn't. + * @param recoveredClass the given class object + * @return true if class has a vbase destructor, false if not + */ + private boolean hasVbaseDestructor(RecoveredClass recoveredClass) throws CancelledException { + Function vBaseDestructor = recoveredClass.getVBaseDestructor(); + + StringBuffer string = new StringBuffer(); + string.append(recoveredClass.getName()); + + if (vBaseDestructor == null) { + return false; + } + if (recoveredClass.getDestructorList().size() != 1) { + return false; + } + + if (!hasNonVirtualFunctionAncestor(recoveredClass)) { + return false; + } + + if (!recoveredClass.hasChildClass() || hasChildWithVBaseAndDestructor(recoveredClass)) { + return true; + } + return false; + } + + /** + * Method to determine if the given class has a child class with both a vbase destructor and a + * regular destructor + * @param recoveredClass the given class + * @return true if the given class has a child class with both a vbase destructor and a regular + * destructor, false otherwise + * @throws CancelledException if cancelled + */ + public boolean hasChildWithVBaseAndDestructor(RecoveredClass recoveredClass) + throws CancelledException { + if (recoveredClass.hasChildClass()) { + List childClasses = recoveredClass.getChildClasses(); + Iterator childIterator = childClasses.iterator(); + while (childIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass childClass = childIterator.next(); + if (childClass.getDestructorList().size() == 1 && + childClass.getVBaseDestructor() != null) { + return true; + } + } + } + return false; + } + + /** + * Method to add class with no vftable to the namespace map + * @param namespace the namespace to put the new class in + * @return the recovered class\ + * @throws CancelledException if cancelled + */ + public RecoveredClass addNoVftableClass(Namespace namespace) throws CancelledException { + + String className = namespace.getName(); + String classNameWithNamespace = namespace.getName(true); + CategoryPath classPath = + extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, + classNameWithNamespace); + + RecoveredClass nonVftableClass = + new RecoveredClass(className, classPath, namespace, dataTypeManager); + nonVftableClass.setHasVftable(false); + + updateNamespaceToClassMap(namespace, nonVftableClass); + return nonVftableClass; + + } + + /** + * Method to recover the class information for each vftable symbol on the list + * * For each virtual function table: + * 1. get vftable's existing class + * 2. create matching data type category folder in dt manager + * 3. get list of virtual functions + * 4. create RecoveredClass object for the vftable class + * 5. add mapping from vftableAddress to class + * 6. add list of const/dest functions to RecoveredClass object + * 7. update list of all const/dest functions in currenetProgram + * 8. set RecoveredClass indeterminate list to const/dest list + * 9. update list of all indeterminate const/dest + * @param vftableSymbolList List of vftable symbols + * @param allowNullFunctionPtrs if true, allow existance of null pointers in vftable + * @param allowDefaultRefsInMiddle if true, allow existance of default refs into the middle of a vftable + * @return List of RecoveredClass objects created corresponding to the vftable symbols + * @throws CancelledException if cancelled + * @throws Exception if issues getting data + */ + List recoverClassesFromVftables(List vftableSymbolList, + boolean allowNullFunctionPtrs, boolean allowDefaultRefsInMiddle) + throws CancelledException, Exception { + + List recoveredClasses = new ArrayList(); + + Iterator vftableSymbolsIterator = vftableSymbolList.iterator(); + while (vftableSymbolsIterator.hasNext()) { + monitor.checkCanceled(); + Symbol vftableSymbol = vftableSymbolsIterator.next(); + Address vftableAddress = vftableSymbol.getAddress(); + + // Get class name from class vftable is in + Namespace vftableNamespace = vftableSymbol.getParentNamespace(); + if (vftableNamespace.equals(globalNamespace)) { + Msg.debug(this, + "vftable is in the global namespace, ie not in a class namespace, so cannot process"); + continue; + } + + SymbolType namespaceType = vftableNamespace.getSymbol().getSymbolType(); + if (namespaceType != SymbolType.CLASS) { + // if it is a namespace but not a class we need to promote it to a class namespace + if (namespaceType == SymbolType.NAMESPACE) { + + //vftableNamespace = promoteToClassNamespace(vftableNamespace); + + // else just leave the old one as a namepace + + } + } + + String className = vftableNamespace.getName(); + String classNameWithNamespace = vftableNamespace.getName(true); + + // Create Data Type Manager Category for given class + CategoryPath classPath = + extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, + classNameWithNamespace); + + // get only the functions from the ones that are not already processed structures + // return null if not an unprocessed table + List virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol, + allowNullFunctionPtrs, allowDefaultRefsInMiddle); + + // the vftable has already been processed - skip it + if (virtualFunctions == null) { + continue; + } + + // Check to see if already have an existing RecoveredClass object for the + // class associated with the current vftable. If so, it indicates multi-inheritance + RecoveredClass recoveredClass = getClass(vftableNamespace); + + if (recoveredClass == null) { + // Create a RecoveredClass object for the current class + recoveredClass = + new RecoveredClass(className, classPath, vftableNamespace, dataTypeManager); + recoveredClass.addVftableAddress(vftableAddress); + recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); + + // add recovered class to map + updateNamespaceToClassMap(vftableNamespace, recoveredClass); + // add it to the running list of RecoveredClass objects + recoveredClasses.add(recoveredClass); + } + else { + recoveredClass.addVftableAddress(vftableAddress); + recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); + + } + + // add it to the vftableAddress to Class map + //vftableToClassMap.put(vftableAddress, recoveredClass); + updateVftableToClassMap(vftableAddress, recoveredClass); + + List
referencesToVftable = getReferencesToVftable(vftableAddress); + addReferenceToVtableMapping(referencesToVftable, vftableAddress); + + Map vftableReferenceToFunctionMapping = + createVftableReferenceToFunctionMapping(referencesToVftable); + + // add this smaller mapping set to the global map + //vftableRefToFunctionMap.putAll(vftableReferenceToFunctionMapping); + + //vftableReferenceToFunctionMapping + List possibleConstructorDestructorsForThisClass = + findPossibleConstructorDestructors(vftableReferenceToFunctionMapping); + + addFunctionsToClassMapping(possibleConstructorDestructorsForThisClass, recoveredClass); + + // add the vftable reference to function mapping to the global list + addFunctionToVftableReferencesMapping(vftableReferenceToFunctionMapping); + + // add the possible constructor/destructor list to the class + recoveredClass.addConstructorDestructorList(possibleConstructorDestructorsForThisClass); + recoveredClass.addIndeterminateConstructorOrDestructorList( + possibleConstructorDestructorsForThisClass); + + // Add them to the list of all constructors and destructors in program + updateAllConstructorsAndDestructorsList(possibleConstructorDestructorsForThisClass); + + } // end of looping over vfTables + return recoveredClasses; + } + + /** + * Method to create mapping to possible constructor/destructor functions + * @param referencesToVftable list of references to a particular vftable + * @return Map of reference to vftable to the function it is in + * @throws CancelledException if cancelled + */ + private Map createVftableReferenceToFunctionMapping( + List
referencesToVftable) throws CancelledException { + + Map vftableRefToFunctionMapping = new HashMap(); + Iterator
referencesIterator = referencesToVftable.iterator(); + while (referencesIterator.hasNext()) { + monitor.checkCanceled(); + Address vftableReference = referencesIterator.next(); + Function functionContaining = extraUtils.getFunctionContaining(vftableReference); + if (functionContaining != null) { + vftableRefToFunctionMapping.put(vftableReference, functionContaining); + } + } + return vftableRefToFunctionMapping; + } + + /** + * Method to generate a list of constructors and destructors given the mapping of the + * vftable to the functions that reference it + * @param vftableReferenceToFunctionMapping the mapping of vftable to the functions that reference the vftable + * @return a list of possible constructors or destructors using the given mapping + * @throws CancelledException if cancelled + */ + private List findPossibleConstructorDestructors( + Map vftableReferenceToFunctionMapping) throws CancelledException { + + List cdFunctions = new ArrayList(); + Set
keySet = vftableReferenceToFunctionMapping.keySet(); + Iterator
referencesIterator = keySet.iterator(); + while (referencesIterator.hasNext()) { + monitor.checkCanceled(); + Address vtableReference = referencesIterator.next(); + Function function = vftableReferenceToFunctionMapping.get(vtableReference); + if (!cdFunctions.contains(function)) { + cdFunctions.add(function); + } + } + return cdFunctions; + } + + + + /** + * Method to get functions from vftable + * @param vftableAddress the address of the vftable + * @param vftableSymbol the name of the vftable + * @param allowNullFunctionPtrs if true, allow null pointers in table + * @param allowDefaultRefsInMiddle if true, allow references in middle of table + * @return the list of functions in the vftable + * @throws CancelledException if cancelled + * @throws Exception if issues getting data + */ + public List getFunctionsFromVftable(Address vftableAddress, Symbol vftableSymbol, + boolean allowNullFunctionPtrs, boolean allowDefaultRefsInMiddle) + throws CancelledException, Exception { + + List virtualFunctionList = new ArrayList(); + Data vftableData = program.getListing().getDefinedDataAt(vftableAddress); + + // now make sure the array or the structure is all pointers + if (!extraUtils.isArrayOrStructureOfAllPointers(vftableData)) { + // if it isn't an array of pointers then we don't know the size of the vftable + // If undefined or pointers not in array or struct then see if what they are + // pointing to are in the class already to determine size of array + + // create vtable + int numFunctionPointers = + createVftable(vftableAddress, allowNullFunctionPtrs, allowDefaultRefsInMiddle); + if (numFunctionPointers == 0) { + return null; + } + // make it an array + vftableData = createVftableArray(vftableAddress, numFunctionPointers); + if (vftableData == null) { + return null; + } + } + + // if there is already a structure created there and it is + // contained in the ClassDataTypes folder then it has already been processed so skip it + // TODO: can this be checked using the folderpath not the folder name? + if (vftableData.isStructure()) { + String[] pathElements = vftableData.getDataType().getCategoryPath().getPathElements(); + if ((pathElements.length > 0) && (pathElements[0].equals(DTM_CLASS_DATA_FOLDER_NAME))) { + return null; + } + } + + // Loop over the pointers in the vftable and add the pointed to functions to the list + int numPointers = vftableData.getNumComponents(); + + for (int i = 0; i < numPointers; ++i) { + monitor.checkCanceled(); + + Address functionPointerAddress = vftableData.getComponent(i).getAddress(); + if (allowNullFunctionPtrs && extraUtils.isNullPointer(functionPointerAddress)) { + virtualFunctionList.add(null); + continue; + } + + Function function = extraUtils.getPointedToFunction(functionPointerAddress); + + if (function != null) { + virtualFunctionList.add(function); + } + + } + return virtualFunctionList; + + } + + private Data createVftableArray(Address vftableAddress, int numFunctionPointers) + throws CancelledException, AddressOutOfBoundsException { + + api.clearListing(vftableAddress, + vftableAddress.add((numFunctionPointers * defaultPointerSize - 1))); + + DataType pointerDataType = dataTypeManager.getPointer(null); + ArrayDataType vftableArrayDataType = + new ArrayDataType(pointerDataType, numFunctionPointers, defaultPointerSize); + try { + Data vftableArrayData = api.createData(vftableAddress, vftableArrayDataType); + return vftableArrayData; + } + catch (Exception e) { + return null; + } + + } + + /** + * Method to create an array of pointers at the given vftable address + * @param vftableAddress the vftable address + * @param allowNullFunctionPtrs if true allow vftables to have null pointers + * @param allowDefaultRefsInMiddle if true allow default references into the middle of the table + * @return the created array of pointers Data or null + * @throws CancelledException if cancelled + */ + // TODO: put in classutils? + private int createVftable(Address vftableAddress, boolean allowNullFunctionPtrs, + boolean allowDefaultRefsInMiddle) throws CancelledException { + + int numFunctionPointers = 0; + Address address = vftableAddress; + MemoryBlock currentBlock = program.getMemory().getBlock(vftableAddress); + + boolean stillInCurrentTable = true; + while (address != null && currentBlock.contains(address) && stillInCurrentTable && + extraUtils.isFunctionPointer(address, allowNullFunctionPtrs)) { + numFunctionPointers++; + address = address.add(defaultPointerSize); + Symbol symbol = program.getSymbolTable().getPrimarySymbol(address); + if (symbol == null) { + continue; + } + // never let non-default refs + if (symbol.getSource() != SourceType.DEFAULT) { + stillInCurrentTable = false; + } + + // if it gets here it is default + if (!allowDefaultRefsInMiddle) { + stillInCurrentTable = false; + } + } + + return numFunctionPointers; + + } + + /** + * Method to find references to vftables that are not in functions, either in undefined areas or + * instructions that are not in functions. + * @param vftableSymbols List of vftable symbols + * @return List of addresses where vftables are referenced but are not in a function + * @throws CancelledException when cancelled + */ + public List
findVftableReferencesNotInFunction(List vftableSymbols) + throws CancelledException { + + MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Vftable References"); + + AddressSet searchSet = new AddressSet(); + AddressSetView executeSet = program.getMemory().getExecuteSet(); + AddressRangeIterator addressRanges = executeSet.getAddressRanges(); + while (addressRanges.hasNext()) { + monitor.checkCanceled(); + AddressRange addressRange = addressRanges.next(); + searchSet.add(addressRange.getMinAddress(), addressRange.getMaxAddress()); + } + + List
vftableAddresses = new ArrayList
(); + + List
notInFunctionVftableRefs = new ArrayList
(); + List
newFunctions = new ArrayList
(); + + Iterator vftableSymbolIterator = vftableSymbols.iterator(); + while (vftableSymbolIterator.hasNext()) { + monitor.checkCanceled(); + Symbol vftableSymbol = vftableSymbolIterator.next(); + Address vftableAddress = vftableSymbol.getAddress(); + vftableAddresses.add(vftableAddress); + + // check direct refs to see if they are in undefined area or not in function + byte[] bytes = ProgramMemoryUtil.getDirectAddressBytes(program, vftableAddress); + + addByteSearchPattern(searcher, notInFunctionVftableRefs, newFunctions, vftableAddress, + bytes, monitor); + + } + + searcher.search(program, searchSet, monitor); + + // check existing refs to see if in instruction but not in function + Iterator
vftableAddressIterator = vftableAddresses.iterator(); + while (vftableAddressIterator.hasNext()) { + monitor.checkCanceled(); + + Address vftableAddress = vftableAddressIterator.next(); + + ReferenceIterator referencesIterator = + program.getReferenceManager().getReferencesTo(vftableAddress); + + while (referencesIterator.hasNext()) { + monitor.checkCanceled(); + + Reference reference = referencesIterator.next(); + Address vftableReference = reference.getFromAddress(); + Function functionContaining = + program.getListing().getFunctionContaining(vftableReference); + + if (functionContaining == null) { + + Instruction instructionContaining = + program.getListing().getInstructionContaining(vftableReference); + if (instructionContaining != null) { + boolean functionCreated = + extraUtils.createFunction(program, vftableReference, monitor); + + if (!functionCreated) { + notInFunctionVftableRefs.add(vftableReference); + + } + + } + } + } + } + + return notInFunctionVftableRefs; + } + + /** + * Method to add a search pattern, to the searcher, for the set of bytes representing a vftable + * address + * @param searcher the MemoryBytePatternSearcher + * @param notInFunctionVftableRefs a list addresses of vftable references that are not contained + * in a function + * @param newFunctions a list of newly created functions that reference the given vftable address + * @param vftableAddress the given vftable address + * @param bytes the bytes to search for + * @param taskMonitor a cancellable monitor + */ + private void addByteSearchPattern(MemoryBytePatternSearcher searcher, + List
notInFunctionVftableRefs, List
newFunctions, + Address vftableAddress, byte[] bytes, TaskMonitor taskMonitor) { + + // no pattern bytes. + if (bytes == null) { + return; + } + + // Each time a match for this byte pattern ... + GenericMatchAction
action = new GenericMatchAction
(vftableAddress) { + @Override + public void apply(Program prog, Address addr, Match match) { + + Function functionContainingVftable = prog.getListing().getFunctionContaining(addr); + + Data dataAt = prog.getListing().getDefinedDataContaining(addr); + + Instruction instructionContainingAddr = + prog.getListing().getInstructionContaining(addr); + + // check the direct references found with the searcher + // if not in function but is an instruction then create the function + // otherwise, add to the list to report to user + if (functionContainingVftable == null && dataAt == null) { + if (instructionContainingAddr == null) { + notInFunctionVftableRefs.add(addr); + } + else { + boolean functionCreated = + extraUtils.createFunction(prog, addr, taskMonitor); + if (!functionCreated) { + notInFunctionVftableRefs.add(addr); + } + } + } + } + + }; + + // create a Pattern of the bytes and the MatchAction to perform upon a match + GenericByteSequencePattern
genericByteMatchPattern = + new GenericByteSequencePattern<>(bytes, action); + + searcher.addPattern(genericByteMatchPattern); + + } + + /** + * Method to create a string buffer containing class parents in the corrector order + * @param recoveredClass the given class + * @return StringBuffer containing class parents + * @throws CancelledException if cancelled + */ + public StringBuffer createParentStringBuffer(RecoveredClass recoveredClass) + throws CancelledException { + + StringBuffer parentStringBuffer = new StringBuffer(); + String classString = recoveredClass.getName(); + + if (recoveredClass.hasParentClass()) { + + // use this to get direct parents + Map> classHierarchyMap = + recoveredClass.getClassHierarchyMap(); + Set directParents = classHierarchyMap.keySet(); + + // use this to get correct parent order and to get the type of parent + Map parentToBaseTypeMap = + recoveredClass.getParentToBaseTypeMap(); + Set ancestors = parentToBaseTypeMap.keySet(); + Iterator ancestorIterator = ancestors.iterator(); + while (ancestorIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass ancestor = ancestorIterator.next(); + if (directParents.contains(ancestor)) { + + Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor); + if (isVirtualParent != null && isVirtualParent) { + classString = classString.concat(" : virtual " + ancestor.getName()); + } + else { + classString = classString.concat(" : " + ancestor.getName()); + } + } + } + } + parentStringBuffer.append("class " + classString); + return parentStringBuffer; + } + + /** + * Method to determine if all data for the ancestors of the given class have been created + * @param recoveredClass the given class + * @return true if all data for the ancestors of the given class have been created, false otherwise + * @throws CancelledException if cancelled + * @throws Exception if class hierarchy list has not been populated + */ + public boolean allAncestorDataHasBeenCreated(RecoveredClass recoveredClass) + throws CancelledException, Exception { + + List parentClasses = recoveredClass.getClassHierarchy(); + + //TODO: make my own exception type + if (parentClasses.isEmpty()) { + throw new Exception( + recoveredClass.getName() + " should not have an empty class hierarchy"); + } + + // if size one it only includes self + if (parentClasses.size() == 1) { + return true; + } + + Iterator parentIterator = parentClasses.listIterator(1); + while (parentIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass parentClass = parentIterator.next(); + + if (getClassStructureFromDataTypeManager(parentClass) == null) { + return false; + } + + } + return true; + + } + + /** + * Method to retrieve the given class's class structure from the data type manager + * @param recoveredClass the given class + * @return the given class's class structure from the data type manager + */ + public Structure getClassStructureFromDataTypeManager(RecoveredClass recoveredClass) { + + DataType classDataType = + dataTypeManager.getDataType(recoveredClass.getClassPath(), recoveredClass.getName()); + + if (classDataType != null && classDataType instanceof Structure) { + Structure classStructure = (Structure) classDataType; + return classStructure; + } + + return null; + + } + + /** + * Method to name class constructors and add them to class namespace + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void addConstructorsToClassNamespace(RecoveredClass recoveredClass, + Structure classStruct) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + String className = recoveredClass.getName(); + + List constructorList = recoveredClass.getConstructorList(); + Iterator constructorsIterator = constructorList.iterator(); + + while (constructorsIterator.hasNext()) { + monitor.checkCanceled(); + Function constructorFunction = constructorsIterator.next(); + + createNewSymbolAtFunction(constructorFunction, className, classNamespace, true, true); + + // check to see if the "this" data type is an empty placeholder for the class + // structure and replace it with the one that was just created by the script + //deleteEmptyClassStructure(constructorFunction, className); + replaceEmptyClassStructure(constructorFunction, className, classStruct); + + // if current non-FID decompiler function return type is a pointer then set the return type + // to a pointer to the class structure, otherwise if it is a void, make it a void so the + // listing has void too, otherwise, leave it as is, probably a void + // boolean isFidFunction = isFidFunction(constructorFunction); + + // if (!isFidFunction) { + String returnType = getReturnTypeFromDecompiler(constructorFunction); + + if (returnType.equals("void")) { + DataType voidDataType = new VoidDataType(); + constructorFunction.setReturnType(voidDataType, SourceType.ANALYSIS); + } + else if (returnType.contains("*")) { + DataType classPointerDataType = dataTypeManager.getPointer(classStruct); + constructorFunction.setReturnType(classPointerDataType, SourceType.ANALYSIS); + } + + //} + + //TODO: temporary until someone fixes thunk display of namespace + putThunksInClass(constructorFunction, classNamespace); + } + } + + /** + * Get the return value from the decompiler signature for the given function + * @param function the given function + * @return the decompiler return value for the given function + */ + private String getReturnTypeFromDecompiler(Function function) { + + DataType decompilerReturnType = decompilerUtils.getDecompilerReturnType(function); + + if (decompilerReturnType == null) { + return null; + } + + return decompilerReturnType.getDisplayName(); + } + + /** + * Method to name class destructors and add them to class namespace + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void addDestructorsToClassNamespace(RecoveredClass recoveredClass) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + String className = recoveredClass.getName(); + + List destructorList = recoveredClass.getDestructorList(); + Iterator destructorIterator = destructorList.iterator(); + while (destructorIterator.hasNext()) { + monitor.checkCanceled(); + Function destructorFunction = destructorIterator.next(); + String destructorName = "~" + className; + + createNewSymbolAtFunction(destructorFunction, destructorName, classNamespace, true, + true); + + destructorFunction.setReturnType(DataType.VOID, SourceType.ANALYSIS); + putThunksInClass(destructorFunction, classNamespace); + } + } + + /** + * Method to put all thunk functions for current function in given namespace + * @param function the given function + * @param namespace the given namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if invalid input + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws CancelledException if cancelled + */ + private void putThunksInClass(Function function, Namespace namespace) + throws DuplicateNameException, InvalidInputException, CircularDependencyException, + CancelledException { + Address[] functionThunkAddresses = function.getFunctionThunkAddresses(); + + if (functionThunkAddresses == null) { + return; + } + + for (Address functionThunkAddress : functionThunkAddresses) { + monitor.checkCanceled(); + + Function thunkFunction = api.getFunctionAt(functionThunkAddress); + if (thunkFunction != null && !thunkFunction.isExternal()) { + thunkFunction.setParentNamespace(namespace); + } + } + + } + + /** + * Method to name non-this destructors and add them to class namespace + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void addNonThisDestructorsToClassNamespace(RecoveredClass recoveredClass) + throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + String className = recoveredClass.getName(); + + List nonThisDestructorList = recoveredClass.getNonThisDestructors(); + Iterator destructorIterator = nonThisDestructorList.iterator(); + while (destructorIterator.hasNext()) { + monitor.checkCanceled(); + Function destructorFunction = destructorIterator.next(); + String destructorName = "~" + className; + + createNewSymbolAtFunction(destructorFunction, destructorName, classNamespace, false, + false); + + putThunksInClass(destructorFunction, classNamespace); + } + } + + /** + * Method to name class vbase destructors and add them to class namespace + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void addVbaseDestructorsToClassNamespace(RecoveredClass recoveredClass) + throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + + Function vbaseDestructorFunction = recoveredClass.getVBaseDestructor(); + if (vbaseDestructorFunction != null) { + String destructorName = VBASE_DESTRUCTOR_LABEL; + + createNewSymbolAtFunction(vbaseDestructorFunction, destructorName, classNamespace, true, + true); + + vbaseDestructorFunction.setReturnType(DataType.VOID, SourceType.ANALYSIS); + putThunksInClass(vbaseDestructorFunction, classNamespace); + } + + } + + /** + * Method to name the class vbtable, if one exists, and add it to the class namespace + * @param recoveredClass the given class + */ + public void addVbtableToClassNamespace(RecoveredClass recoveredClass) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + + Address vbtableAddress = recoveredClass.getVbtableAddress(); + + if (vbtableAddress == null) { + return; + } + + createNewSymbolAtAddress(vbtableAddress, VBTABLE_LABEL, classNamespace); + + } + + /** + * Method to create a new symbol at an address + * @param address the given address + * @param name the name to give the new symbol + * @param namespace the namespace to put the new symbol in + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CancelledException if cancelled + */ + public void createNewSymbolAtAddress(Address address, String name, Namespace namespace) + throws DuplicateNameException, InvalidInputException, CircularDependencyException, + CancelledException { + + Symbol symbol = symbolTable.getSymbol(name, address, namespace); + + // already exists + if (symbol != null) { + return; + } + + // check to see if symbol is same name but in global namespace + List symbolsByNameAtAddress = getSymbolsByNameAtAddress(address, name); + + // if no same name symbol, add new symbol + if (symbolsByNameAtAddress.size() == 0) { + AddLabelCmd lcmd = new AddLabelCmd(address, name, namespace, SourceType.ANALYSIS); + if (!lcmd.applyTo(program)) { + Msg.debug(this, + "ERROR: Could not add new symbol " + name + " to " + address.toString()); + } + } + //put the same name one in the namespace + else { + Iterator iterator = symbolsByNameAtAddress.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Symbol sameNameSymbol = iterator.next(); + sameNameSymbol.setNamespace(namespace); + } + } + + return; + } + + /** + * Method to replace the program's current class structure, only if an empty placeholder structure, + * with the one generated by this script + * @param function a class method with current class structure applied + * @param className the given class name + * @param newClassStructure the new structure to replace the old with + * @throws DataTypeDependencyException if there is a data dependency exception when replacing + */ + public void replaceEmptyClassStructure(Function function, String className, + Structure newClassStructure) throws DataTypeDependencyException { + + Parameter thisParam = function.getParameter(0); + if (thisParam == null) { + return; + } + + DataType dataType = thisParam.getDataType(); + if (dataType instanceof Pointer) { + Pointer ptr = (Pointer) dataType; + DataType baseDataType = ptr.getDataType(); + if (baseDataType.getName().equals(className) && baseDataType.isNotYetDefined()) { + + dataTypeManager.replaceDataType(baseDataType, newClassStructure, false); + + // remove original folder if it is empty after the replace + CategoryPath originalPath = baseDataType.getCategoryPath(); + Category category = dataTypeManager.getCategory(originalPath); + Category parentCategory = category.getParent(); + if (parentCategory != null) { + parentCategory.removeEmptyCategory(category.getName(), monitor); + } + + } + } + } + + /** + * Method to create a new symbol at the given function + * @param function the given function + * @param name the name for the new symbol + * @param namespace the namespace to put the new symbol in + * @param setPrimary if true, set the new symbol primary, if false do not make the new symbol primary + * @param removeBadFID if true, check for and remove any incorrect FID symbols, if false leave them there + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CancelledException if cancelled + */ + private void createNewSymbolAtFunction(Function function, String name, Namespace namespace, + boolean setPrimary, boolean removeBadFID) throws DuplicateNameException, + InvalidInputException, CircularDependencyException, CancelledException { + + // check for bad FID or FID that needs fix up and remove those bad symbols + if (removeBadFID) { + removeBadFIDSymbols(namespace, name, function); + } + + if (function.equals(purecall)) { + return; + } + + Symbol symbol = symbolTable.getSymbol(name, function.getEntryPoint(), namespace); + + // already exists + if (symbol != null) { + return; + } + + // check to see if symbol is same name but in global namespace + List symbolsByNameAtAddress = + getSymbolsByNameAtAddress(function.getEntryPoint(), name); + + // if no same name symbol, add new symbol + if (symbolsByNameAtAddress.size() == 0) { + AddLabelCmd lcmd = + new AddLabelCmd(function.getEntryPoint(), name, namespace, SourceType.ANALYSIS); + if (!lcmd.applyTo(program)) { + Msg.debug(this, "ERROR: Could not add new function label " + name + " to " + + function.getEntryPoint().toString()); + return; + } + + symbol = lcmd.getSymbol(); + if (setPrimary && !symbol.isPrimary()) { + SetLabelPrimaryCmd scmd = + new SetLabelPrimaryCmd(function.getEntryPoint(), name, namespace); + if (!scmd.applyTo(program)) { + Msg.debug(this, "ERROR: Could not make function label " + name + + " primary at " + function.getEntryPoint().toString()); + } + } + } + //put the same name one in the namespace + else { + Iterator iterator = symbolsByNameAtAddress.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Symbol sameNameSymbol = iterator.next(); + sameNameSymbol.setNamespace(namespace); + } + } + + return; + } + + /** + * Method to remove the primary label, applied by FID analyzer, at the given address if it does not match the given name + * leave the secondary labels alone, ie the mangled name, so people can regenerate the old symbol + * if they want to + * @param namespace the given namespace + * @param name the given name + * @param function the given function + * @throws CancelledException if cancelled + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if issues setting return type + */ + private void removeBadFIDSymbols(Namespace namespace, String name, Function function) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + Address functionAddress = function.getEntryPoint(); + + BookmarkManager bm = program.getBookmarkManager(); + + Bookmark bookmark = + bm.getBookmark(functionAddress, BookmarkType.ANALYSIS, "Function ID Analyzer"); + + if (bookmark == null) { + return; + } + + String bookmarkComment = bookmark.getComment(); + + // just get primary symbol and check it - if no match then remove all symbols and replace with good one + if (bookmarkComment.contains("Single Match")) { + + Symbol symbol = symbolTable.getPrimarySymbol(functionAddress); + if (symbol != null && symbol.getSource() == SourceType.ANALYSIS && + !symbol.getName().equals(name) && !symbol.getParentNamespace().equals(namespace)) { + // add to list of bad namespaces to be cleaned up later + if (!badFIDNamespaces.contains(symbol.getParentNamespace())) { + badFIDNamespaces.add(symbol.getParentNamespace()); + } + extraUtils.addUniqueStringToPlateComment(functionAddress, + "***** Removed Bad FID Symbol *****"); + + if (!badFIDFunctions.contains(function)) { + badFIDFunctions.add(function); + } + + findAndRemoveBadStructuresFromFunction(function, namespace); + extraUtils.removeAllSymbolsAtAddress(functionAddress); + + } + return; + } + // FID with multiple matches - either all FID_conflicts or one common name + // since no good namespace all need to be removed but if there is a good base name + // add FID + if (bookmarkComment.contains("Multiple Matches")) { + // See if any contain the class name and if so add "resolved" and if not + if (doAnySymbolsHaveMatchingName(functionAddress, name)) { + extraUtils.addUniqueStringToPlateComment(functionAddress, + "***** Resolved FID Conflict *****"); + + if (!resolvedFIDFunctions.contains(function)) { + resolvedFIDFunctions.add(function); + } + + findAndRemoveBadStructuresFromFunction(function, namespace); + } + else { + extraUtils.addUniqueStringToPlateComment(functionAddress, + "***** Removed Bad FID Symbol(s) *****"); + + if (!badFIDFunctions.contains(function)) { + badFIDFunctions.add(function); + } + + findAndRemoveBadStructuresFromFunction(function, namespace); + + } + extraUtils.removeAllSymbolsAtAddress(functionAddress); + return; + } + } + + /** + * Method to find empty structures that were created when incorrect FID function + * signatures were applied and remove them from the given function. Incorrect structure + * data types are added to a global list so that if nothing remains that references the data + * type, it can be removed after all functions have been processed. + * @param function the function with incorrect FID signature + * @param namespace the correct namespace of the function + * @throws CancelledException if cancelled + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + private void findAndRemoveBadStructuresFromFunction(Function function, Namespace namespace) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + // find bad structure parameter data types + List badStructureDataTypes = findBadParameterDataTypes(function, namespace); + + // find bad structure return data types + Structure badReturnType = findBadReturnType(function, namespace); + if (badReturnType != null && !badStructureDataTypes.contains(badReturnType)) { + badStructureDataTypes.add(badReturnType); + } + + // if bad structures were found delete and recreate the function and all calling functions + // in order to remove the bad data types from the function signature + if (badStructureDataTypes.size() > 0) { + // find all functions that call this function and do the same + fixBadSignatures(function, badStructureDataTypes); + // add all the new bad dts to the list of bad ones + Iterator badStructuresIterator = badStructureDataTypes.iterator(); + while (badStructuresIterator.hasNext()) { + monitor.checkCanceled(); + Structure structure = badStructuresIterator.next(); + if (!badFIDStructures.contains(structure)) { + badFIDStructures.add(structure); + } + } + } + } + + /** + * Method to remove incorrect data types from the given function's signature and from + * all calling functions + * @param function function with bad signature + * @throws CancelledException if cancelled + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if invalid data input + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + private void fixBadSignatures(Function function, List badStructureDataTypes) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + List allFunctionsToFix = new ArrayList(); + allFunctionsToFix.add(function); + Set callingFunctions = function.getCallingFunctions(monitor); + + while (callingFunctions != null && !callingFunctions.isEmpty()) { + monitor.checkCanceled(); + List moreCallingFunctions = new ArrayList(); + Iterator callingFunctionsIterator = callingFunctions.iterator(); + while (callingFunctionsIterator.hasNext()) { + monitor.checkCanceled(); + Function callingFunction = callingFunctionsIterator.next(); + if (!allFunctionsToFix.contains(callingFunction)) { + allFunctionsToFix.add(callingFunction); + moreCallingFunctions.addAll(callingFunction.getCallingFunctions(monitor)); + } + callingFunctionsIterator.remove(); + } + callingFunctions.addAll(moreCallingFunctions); + } + + Iterator functionsToFixIterator = allFunctionsToFix.iterator(); + while (functionsToFixIterator.hasNext()) { + monitor.checkCanceled(); + Function functionToFix = functionsToFixIterator.next(); + if (!functionToFix.isThunk()) { + + removeBadReturnType(functionToFix, badStructureDataTypes); + removeBadParameterDataTypes(functionToFix, badStructureDataTypes); + + if (!fixedFIDFunctions.contains(functionToFix)) { + fixedFIDFunctions.add(functionToFix); + } + } + } + + } + + /** + * Method to find and add to permanent removal list any incorrect empty structure params + * @param function the function to check for bad params + * @param namespace the correct parent namespace of function + * @throws CancelledException when cancelled + */ + private List findBadParameterDataTypes(Function function, Namespace namespace) + throws CancelledException { + + List badStructureDataTypes = new ArrayList(); + + int parameterCount = function.getParameterCount(); + for (int i = 0; i < parameterCount; i++) { + monitor.checkCanceled(); + DataType dataType = function.getParameter(i).getDataType(); + if (!dataType.getName().equals(namespace.getName()) && + extraUtils.isPointerToEmptyStructure(dataType)) { + Pointer ptr = (Pointer) dataType; + Structure structure = (Structure) ptr.getDataType(); + + if (!badStructureDataTypes.contains(structure)) { + badStructureDataTypes.add(structure); + } + } + } + return badStructureDataTypes; + } + + /** + * Method to replace the given bad structure data types with undefined data types of same size + * for the given functions parameters + * @param function the function to fix + * @param badStructureDataTypes the list of bad structure data types to replace if found + * @throws CancelledException if cancelled + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if invalid data input + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + private void removeBadParameterDataTypes(Function function, + List badStructureDataTypes) throws CancelledException, + DuplicateNameException, InvalidInputException, CircularDependencyException { + + int parameterCount = function.getParameterCount(); + for (int i = 0; i < parameterCount; i++) { + monitor.checkCanceled(); + DataType paramDataType = function.getParameter(i).getDataType(); + Structure baseDataType = extraUtils.getBaseStructureDataType(paramDataType); + if (baseDataType != null && badStructureDataTypes.contains(baseDataType)) { + + // To remove from this param we have to remove the function from its namespace + if (function.getParameter(i).getName().equals("this")) { + function.setParentNamespace(globalNamespace); + + } + else { + PointerDataType ptrUndefined = + extraUtils.createPointerToUndefinedDataType(paramDataType); + if (ptrUndefined != null) { + function.getParameter(i).setDataType(ptrUndefined, SourceType.ANALYSIS); + } + + else { + Msg.debug(this, "ERROR: " + function.getEntryPoint().toString() + + " Could not replace parameter " + i + " with undefined pointer."); + } + } + } + } + } + + /** + * Method to find incorrect empty structure return type + * @param function the function to check + * @param namespace the parent namespace of the function + */ + private Structure findBadReturnType(Function function, Namespace namespace) { + + DataType returnType = function.getReturnType(); + if (!returnType.getName().equals(namespace.getName()) && + extraUtils.isPointerToEmptyStructure(returnType)) { + Pointer ptr = (Pointer) returnType; + Structure structure = (Structure) ptr.getDataType(); + + return structure; + + } + return null; + } + + /** + * Method to fix a bad return type if it is one of the bad structure data types on the given + * list. The list was previously generated from functions that had incorrect FID signatures + * placed on them that this script recognized and corrected. + * @param function the given function + * @param badStructureDataTypes a list of bad structure data types + * @throws InvalidInputException if issue setting return type + */ + private void removeBadReturnType(Function function, List badStructureDataTypes) + throws InvalidInputException { + + DataType returnType = function.getReturnType(); + Structure baseDataType = extraUtils.getBaseStructureDataType(returnType); + if (baseDataType != null && badStructureDataTypes.contains(baseDataType)) { + PointerDataType ptrUndefined = + extraUtils.createPointerToUndefinedDataType(returnType); + if (ptrUndefined != null) { + function.setReturnType(ptrUndefined, SourceType.ANALYSIS); + } + } + } + + /** + * Method to determine if any symbols at the given address have matching names + * as the given name after removing template, pdb quotes, or FID_conflict characters + * added by other analyzers. + * @param address the given address + * @param name the name to match + * @return true if any symbols at the given address "match" the given name, false otherwise + * @throws CancelledException when canceled + */ + private boolean doAnySymbolsHaveMatchingName(Address address, String name) + throws CancelledException { + + String simpleName = extraUtils.removeTemplate(name); + + Symbol[] symbols = symbolTable.getSymbols(address); + for (Symbol symbol : symbols) { + monitor.checkCanceled(); + + String simpleSymbolName = extraUtils.removeTemplate(symbol.getName()); + simpleSymbolName = removeSingleQuotes(simpleSymbolName); + simpleSymbolName = removeFIDConflict(simpleSymbolName); + simpleSymbolName = removeSingleQuotes(simpleSymbolName); + + if (simpleName.equals(simpleSymbolName)) { + return true; + } + } + return false; + } + + /** + * Method to remove single quotes from beginning and end of given string + * @param string string to process + * @return string without leading or trailing single quotes + */ + private String removeSingleQuotes(String string) { + if (string.startsWith("`")) { + string = string.substring(1); + } + if (string.endsWith("'")) { + string = string.substring(0, string.length() - 1); + } + return string; + + } + + /** + * Method to remove "FID_conflict:" prefix from the given string + * @param string string to process + * @return string without "FID_conflict:" prefix + */ + private String removeFIDConflict(String string) { + if (string.startsWith("FID_conflict:")) { + string = string.substring(13); + } + return string; + + } + + /** + * Method to get symbols with the given name at the given address + * @param address the given address + * @param name the given name to match + * @return a list of symbols with the given name at the given address + * @throws CancelledException if cancelled + */ + List getSymbolsByNameAtAddress(Address address, String name) throws CancelledException { + + List sameNameSymbols = new ArrayList(); + + Symbol[] symbols = symbolTable.getSymbols(address); + for (Symbol symbol : symbols) { + monitor.checkCanceled(); + Namespace namespace = symbol.getParentNamespace(); + + if (namespace.isGlobal() && symbol.getName().equals(name)) { + sameNameSymbols.add(symbol); + } + else if (namespace.isGlobal() && name.equals(DELETING_DESTRUCTOR_LABEL) && + symbol.getName().contains(DELETING_DESTRUCTOR_LABEL)) { + sameNameSymbols.add(symbol); + } + } + return sameNameSymbols; + } + + /** + * Returns a new address with the specified offset in the default address space. + * @param offset the offset for the new address + * @return a new address with the specified offset in the default address space + */ + public final Address toAddr(long offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } + + /** + * Method to determine if the given constructor function calls any non-parent 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 + * vftable refererence, false otherwise + * @throws CancelledException if cancelled + */ + public boolean doesFunctionCallAnyNonParentConstructorsBeforeVtableReference( + RecoveredClass recoveredClass, Function constructor, Address vftableReference) + throws CancelledException { + + List orderedReferenceAddressPairsFromCallingFunction = + extraUtils.getOrderedReferenceAddressPairsFromCallingFunction(constructor); + + // if there are no calls from the function then return false + if (orderedReferenceAddressPairsFromCallingFunction.size() == 0) { + return false; + } + + Iterator iterator = + orderedReferenceAddressPairsFromCallingFunction.iterator(); + while (iterator.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) { + return false; + } + + // 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); + } + + if (calledFunction.equals(operator_new)) { + continue; + } + + if (calledFunction.getName().contains("prolog")) { + continue; + } + + 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 + */ + public void findCloneFunctions(List recoveredClasses) + throws CancelledException, Exception { + + Map cloneToClassMap = new HashMap(); + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List allOtherConstructors = + new ArrayList(getAllConstructors()); + allOtherConstructors.removeAll(recoveredClass.getConstructorList()); + + // iterate through the vtable functions + List virtualFunctions = recoveredClass.getAllVirtualFunctions(); + if (virtualFunctions == null) { + continue; + } + Iterator vfunctionIterator = virtualFunctions.iterator(); + while (vfunctionIterator.hasNext()) { + monitor.checkCanceled(); + + Function vfunction = vfunctionIterator.next(); + if (extraUtils.doesFunctionACallAnyListedFunction(vfunction, + recoveredClass.getConstructorList()) && + !extraUtils.doesFunctionACallAnyListedFunction(vfunction, + allOtherConstructors)) { + cloneToClassMap.put(vfunction, recoveredClass); + } + } + + } + + // 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); + } + } + } + + } + + /** + * 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 (!extraUtils.getCalledFunctionByCallOrder(caller, 1).equals(firstCalled)) { + return false; + } + RecoveredClass recoveredClass = cloneFunctionToClassMap.get(caller); + List constructorList = recoveredClass.getConstructorList(); + + Function secondFunction = extraUtils.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 = + extraUtils.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; + } + + /** + * Method to remove the empty namespaces and unreferenced empty class structures that + * that were incorrectly applied by FID + * @throws CancelledException when script is cancelled + */ + public void removeEmptyClassesAndStructures() throws CancelledException { + + Iterator badNamespaceIterator = badFIDNamespaces.iterator(); + while (badNamespaceIterator.hasNext()) { + monitor.checkCanceled(); + Namespace badNamespace = badNamespaceIterator.next(); + + // delete empty namespace and parent namespaces + if (!extraUtils.hasSymbolsInNamespace(badNamespace)) { + removeEmptyNamespaces(badNamespace); + } + } + + // remove unused empty structures + removeEmptyStructures(); + + } + + /** + * Method to remove the given namespace if it is empty and its parent namepaces if they are empty + * @param namespace the given namespace + * @throws CancelledException if cancelled + */ + private void removeEmptyNamespaces(Namespace namespace) throws CancelledException { + + // delete empty namespace and parent namespaces + Namespace parentNamespace = namespace.getParentNamespace(); + + namespace.getSymbol().delete(); + while (parentNamespace != null && !extraUtils.hasSymbolsInNamespace(parentNamespace)) { + monitor.checkCanceled(); + + namespace = parentNamespace; + parentNamespace = parentNamespace.getParentNamespace(); + namespace.getSymbol().delete(); + } + } + + /** + * Method to remove the incorrectly applied and unreferenced empty structures that are not used + * @throws CancelledException when script is cancelled + */ + private void removeEmptyStructures() throws CancelledException { + + Iterator badStructureIterator = badFIDStructures.iterator(); + while (badStructureIterator.hasNext()) { + + monitor.checkCanceled(); + + Structure badStructure = badStructureIterator.next(); + // if not used by anything remove it + ListAccumulator accumulator = new ListAccumulator<>(); + ReferenceUtils.findDataTypeReferences(accumulator, badStructure, null, program, true, + monitor); + + List referenceList = accumulator.asList(); + if (referenceList.isEmpty()) { + // delete empty class data type and empty parent folders + removeEmptyStructure(badStructure.getDataTypePath().getCategoryPath(), + badStructure.getName()); + } + } + } + + /** + * Method to remove the structure with the given folder path and name if it is empty + * @param folderPath the given folder path in the data type manager + * @param structureName the given structure name + * @throws CancelledException if cancelled + */ + private void removeEmptyStructure(CategoryPath folderPath, String structureName) + throws CancelledException { + + DataType dataType = dataTypeManager.getDataType(folderPath, structureName); + if (extraUtils.isEmptyStructure(dataType)) { + + dataTypeManager.remove(dataType, monitor); + Category classCategory = dataTypeManager.getCategory(folderPath); + Category parentCategory = classCategory.getParent(); + boolean tryToRemove = true; + while (parentCategory != null && tryToRemove) { + monitor.checkCanceled(); + + tryToRemove = parentCategory.removeEmptyCategory(classCategory.getName(), monitor); + classCategory = parentCategory; + parentCategory = parentCategory.getParent(); + } + + } + + } + + /** + * Method to create empty vftable structures before class struct is created so that + * they can be added to the class structure. Afterwords, they are filled in with pointers + * to vftable functions + * @param recoveredClass the given class + * @return Map of address/vftable structure pointers + * @throws Exception when invalid data creation + */ + public Map createEmptyVfTableStructs(RecoveredClass recoveredClass) + throws Exception { + + Map vftableToStructureMap = new HashMap(); + + String className = recoveredClass.getName(); + + CategoryPath classPath = recoveredClass.getClassPath(); + + Structure vftableStruct = null; + + Map orderToVftableMap = recoveredClass.getOrderToVftableMap(); + + for (int index = 0; index < orderToVftableMap.size(); index++) { + + monitor.checkCanceled(); + + Address vftableAddress = orderToVftableMap.get(index); + + // if only one vftable name the structure _vftable + if (orderToVftableMap.size() == 1) { + vftableStruct = new StructureDataType(classPath, + className + CLASS_VFUNCTION_STRUCT_NAME, 0, dataTypeManager); + } + // if more than one, name it _vftable_for_ if + // can associate them or if can't assoc parent + else { + RecoveredClass vftableParentClass = + recoveredClass.getVftableBaseClass(vftableAddress); + // should never happen but just in case + if (vftableParentClass == null) { + vftableStruct = new StructureDataType(classPath, + className + CLASS_VFUNCTION_STRUCT_NAME + index, 0, dataTypeManager); + } + else { + vftableStruct = + new StructureDataType(classPath, className + CLASS_VFUNCTION_STRUCT_NAME + + "_for_" + vftableParentClass.getName(), 0, dataTypeManager); + } + } + + // align the structure then add it to the data type manager + vftableStruct.setInternallyAligned(true); + vftableStruct = (Structure) dataTypeManager.addDataType(vftableStruct, + DataTypeConflictHandler.DEFAULT_HANDLER); + + DataType vfPointerDataType = dataTypeManager.getPointer(vftableStruct); + + vftableToStructureMap.put(vftableAddress, vfPointerDataType); + } + return vftableToStructureMap; + } + + /** + * Method to create class structure for single inheritance, no parent, non-vftable classes + * @param recoveredClass the given class + * @throws CancelledException when cancelled + */ + public void createClassStructureWhenNoParentOrVftable(RecoveredClass recoveredClass) + throws CancelledException { + + Structure classStruct; + if (recoveredClass.hasExistingClassStructure()) { + Structure computedClassDataStructure = recoveredClass.getExistingClassStructure(); + int structLen = 0; + if (computedClassDataStructure != null) { + structLen = computedClassDataStructure.getLength(); + int mod = structLen % defaultPointerSize; + int alignment = 0; + if (mod != 0) { + alignment = defaultPointerSize - mod; + structLen += alignment; + } + + classStruct = new StructureDataType(recoveredClass.getClassPath(), + recoveredClass.getName(), structLen, dataTypeManager); + + int numComponents = computedClassDataStructure.getNumDefinedComponents(); + for (int i = 1; i < numComponents; i++) { + monitor.checkCanceled(); + DataTypeComponent component = computedClassDataStructure.getComponent(i); + int offset = component.getOffset(); + classStruct.replaceAtOffset(offset, component.getDataType(), + component.getDataType().getLength(), component.getFieldName(), + component.getComment()); + } + } + else { + classStruct = new StructureDataType(recoveredClass.getClassPath(), + recoveredClass.getName(), defaultPointerSize, dataTypeManager); + } + + } + // make it default ptr size so it aligns inside child class correctly + else { + classStruct = new StructureDataType(recoveredClass.getClassPath(), + recoveredClass.getName(), defaultPointerSize, dataTypeManager); + } + + classStruct.setDescription(createParentStringBuffer(recoveredClass).toString()); + //TODO: do I still need to add it since I changed to pass in dtman in new Struct + classStruct = (Structure) dataTypeManager.addDataType(classStruct, + DataTypeConflictHandler.DEFAULT_HANDLER); + + } + + /** + * Method to fill in the vftable structure with pointers to virtual function signature data types + * @param recoveredClass the current class to be processed + * @param vftableToStructureMap the map from the class's vftables to the correct vftable structure data type + * @throws CancelledException when cancelled + */ + public void fillInAndApplyVftableStructAndNameVfunctions(RecoveredClass recoveredClass, + Map vftableToStructureMap) throws CancelledException, Exception { + + //create function definition for each virtual function and put in vftable structure and + // data subfolder + CategoryPath classPath = recoveredClass.getClassPath(); + + List
vftableAddresses = recoveredClass.getVftableAddresses(); + Iterator
vftableAddressIterator = vftableAddresses.iterator(); + + while (vftableAddressIterator.hasNext()) { + monitor.checkCanceled(); + Address vftableAddress = vftableAddressIterator.next(); + + PointerDataType vftablePointerDataType = + (PointerDataType) vftableToStructureMap.get(vftableAddress); + + DataType vftableDataType = vftablePointerDataType.getDataType(); + + vftableDataType = dataTypeManager.getDataType(vftableDataType.getCategoryPath(), + vftableDataType.getName()); + + Structure vftableStruct = (Structure) vftableDataType; + + if (nameVfunctions) { + // if no pdb info, name all the vfunctions for this vftable and put in class namespace + nameVfunctions(recoveredClass, vftableAddress, vftableDataType.getName()); + } + + List vFunctions = recoveredClass.getVirtualFunctions(vftableAddress); + Iterator vfIterator = vFunctions.iterator(); + + while (vfIterator.hasNext()) { + + monitor.checkCanceled(); + Function vfunction = vfIterator.next(); + + if (vfunction == null) { + Pointer nullPointer = dataTypeManager.getPointer(DataType.DEFAULT); + vftableStruct.add(nullPointer, "null pointer", null); + continue; + } + + // get the classPath of highest level parent with vfAddress in their vftable + classPath = + getCategoryPathForFunctionSignature(vfunction, recoveredClass, vftableAddress); + + Symbol vfunctionSymbol = symbolTable.getPrimarySymbol(vfunction.getEntryPoint()); + Namespace parentNamespace = vfunctionSymbol.getParentNamespace(); + + String classFieldName = new String(); + if (!parentNamespace.equals(globalNamespace)) { + RecoveredClass vfunctionClass = getClass(parentNamespace); + + // this is null when there is a class from somewhere other than RTTI so it is + // not stored in the map. Just use the parent namespace name in this case + // TODO: can check against other program namespace names to see if I cna shorten + if (vfunctionClass == null) { + classFieldName = parentNamespace.getName(); + } + else if (vfunctionClass.getShortenedTemplateName() != null && + useShortTemplates && !vfunctionClass.getShortenedTemplateName().isEmpty()) { + classFieldName = vfunctionClass.getShortenedTemplateName(); + } + else { + classFieldName = vfunctionClass.getName(); + } + + } + + PointerDataType functionPointerDataType = + createFunctionSignaturePointerDataType(vfunction, classPath); + vftableStruct.add(functionPointerDataType, vfunction.getName(), + classFieldName + " " + vfunction.getName()); + } + + // align the structure then add it to the data type manager + vftableStruct.setInternallyAligned(true); + vftableStruct = (Structure) dataTypeManager.addDataType(vftableStruct, + DataTypeConflictHandler.DEFAULT_HANDLER); + + // clear the array or unprocessed structure at the current vftable location and + // apply the structure. It has to be one or the other and the correct length + // because of the check at the beginning of the script that checked for either + // array or structure of pointers and got size from them initially + + api.clearListing(vftableAddress); + api.createData(vftableAddress, vftableStruct); + + } + } + + /** + * Method to give default names to the vfunctions in the given vftable if they don't have a name already. If they are a clone or deleting destructor name them accordingly. + * @param recoveredClass the given class + * @param vftableAddress the address of the vftable + * @param vftableStructureName the name of the vftable structure to be used as a prefix for the vfunctions in the given vftable + * @throws CancelledException if cancelled + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + private void nameVfunctions(RecoveredClass recoveredClass, Address vftableAddress, + String vftableStructureName) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + + List deletingDestructors = recoveredClass.getDeletingDestructors(); + List cloneFunctions = recoveredClass.getCloneFunctions(); + + Iterator vfIterator = + recoveredClass.getVirtualFunctions(vftableAddress).iterator(); + + String vfunctionName; + int tableEntry = 1; + while (vfIterator.hasNext()) { + monitor.checkCanceled(); + Function vfunction = vfIterator.next(); + + // create a one-up number for the next virtual function + int entryNumber = tableEntry++; + + boolean setPrimary = false; + boolean removeBadFID = false; + boolean isDeletingDestructor = false; + + if (deletingDestructors.contains(vfunction)) { + vfunctionName = DELETING_DESTRUCTOR_LABEL; + setPrimary = true; + removeBadFID = true; + isDeletingDestructor = true; + } + + else if (cloneFunctions.contains(vfunction)) { + vfunctionName = CLONE_LABEL; + setPrimary = true; + removeBadFID = true; + } + + else { + vfunctionName = DEFAULT_VFUNCTION_PREFIX + entryNumber; + } + + // can't put external functions into a namespace from this program + if (!vfunction.isExternal()) { + + // if not already, make it a this call + makeFunctionThiscall(vfunction); + + // put symbol on the virtual function + Symbol vfunctionSymbol = vfunction.getSymbol(); + Namespace vfunctionNamespace = vfunctionSymbol.getParentNamespace(); + + // if the name already contains deleting_destructor for this namespace don't add + // another dd symbol + if (hasDeletingDestructorInNamespace(vfunction.getEntryPoint(), classNamespace)) { + continue; + } + + if (!isDeletingDestructor && + isParentNamespace(vfunctionNamespace, classNamespace)) { + String plateComment = api.getPlateComment(vfunction.getEntryPoint()); + String newComment = vfunctionNamespace.getName(true) + + " member function inherited by " + classNamespace.getName(true); + if (plateComment != null) { + newComment = plateComment + "\n" + newComment; + } + api.setPlateComment(vfunction.getEntryPoint(), newComment); + continue; + } + + SourceType originalSourceType = vfunctionSymbol.getSource(); + if (originalSourceType == SourceType.DEFAULT || setPrimary) { + createNewSymbolAtFunction(vfunction, vfunctionName, classNamespace, setPrimary, + removeBadFID); + + //TODO: remove this once the thunk namespace issue is fixed + putThunksInClass(vfunction, classNamespace); + + } + } + } + } + + private boolean hasDeletingDestructorInNamespace(Address address, Namespace namespace) + throws CancelledException { + + Symbol[] symbols = symbolTable.getSymbols(address); + for (Symbol symbol : symbols) { + monitor.checkCanceled(); + + if (symbol.getName().contains("deleting_destructor") && + symbol.getParentNamespace().equals(namespace)) { + return true; + } + } + return false; + } + + private boolean isParentNamespace(Namespace namespace, Namespace childNamespace) { + + RecoveredClass possibleParentClass = getClass(namespace); + if (possibleParentClass == null) { + return false; + } + + RecoveredClass childClass = getClass(childNamespace); + if (childClass == null) { + return false; + } + + if (childClass.equals(possibleParentClass)) { + return false; + } + + List classHierarchy = childClass.getClassHierarchy(); + if (classHierarchy.contains(possibleParentClass)) { + return true; + } + return false; + } + + // may skip a parent so continue through all parents + // for multi-inheritance, get the correct parent class for the given vftable + /** + * Method to retrieve the class path of the highest ancestor with matching vfunction in its vftable + * @param vfunction the given virtual function + * @param recoveredClass the given class + * @param vftableAddress the given virtual function table from the given class + * @return the class path for the highest ancestor with matching virtual function in its vftable + * @throws CancelledException when cancelled + */ + CategoryPath getCategoryPathForFunctionSignature(Function vfunction, + RecoveredClass recoveredClass, Address vftableAddress) throws CancelledException { + + // if class has no parent, return its own classPath + CategoryPath classPath = recoveredClass.getClassPath(); + if (!recoveredClass.hasParentClass()) { + return classPath; + } + + // if no ancestor has virtual functions then return the given class's class path + List ancestorsWithVirtualFunctions = + getAncestorsWithVirtualFunctions(recoveredClass); + if (ancestorsWithVirtualFunctions.size() == 0) { + return classPath; + } + + Iterator classHierarchyIterator; + if (recoveredClass.hasSingleInheritance()) { + List classHierarchy = recoveredClass.getClassHierarchy(); + classHierarchyIterator = classHierarchy.listIterator(1); + } + else { + // get the parent that goes with the given vftableAddress + // if there is no parent associated with the vftable then return the current + // class's class path + RecoveredClass parentClass = recoveredClass.getVftableBaseClass(vftableAddress); + if (parentClass == null) { + return classPath; + } + + // get the class hierarchy for the parent + List classHierarchy = + recoveredClass.getClassHierarchyMap().get(parentClass); + if (classHierarchy == null) { + return classPath; + } + + classHierarchyIterator = classHierarchy.iterator(); + } + + while (classHierarchyIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass currentClass = classHierarchyIterator.next(); + List virtualFunctions = currentClass.getAllVirtualFunctions(); + if (virtualFunctions.contains(vfunction)) { + classPath = currentClass.getClassPath(); + } + } + return classPath; + + } + + /** + * + * @param vfunction the given function + * @param classPath the given data type manager classPath + * @return pointer to function signature data type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + private PointerDataType createFunctionSignaturePointerDataType(Function vfunction, + CategoryPath classPath) throws DuplicateNameException { + + FunctionDefinition functionDataType = (FunctionDefinitionDataType) vfunction.getSignature(); + + DataType returnType = vfunction.getReturnType(); + + functionDataType.setReturnType(returnType); + + // If this data type doesn't exist in this folder make a new one + // otherwise use the existing one + DataType existingDataType = + dataTypeManager.getDataType(classPath, functionDataType.getName()); + + PointerDataType functionPointerDataType; + + if (existingDataType == null) { + functionDataType.setCategoryPath(classPath); + functionDataType = (FunctionDefinition) dataTypeManager.addDataType(functionDataType, + DataTypeConflictHandler.DEFAULT_HANDLER); + functionPointerDataType = new PointerDataType(functionDataType); + } + else { + functionPointerDataType = new PointerDataType(existingDataType); + } + return functionPointerDataType; + + } + + /** + * Method to add precomment inside functions containing inlined constructors at approximate + * address of start of inlined function + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void createInlinedConstructorComments(RecoveredClass recoveredClass) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + String className = recoveredClass.getName(); + + List inlinedConstructorList = recoveredClass.getInlinedConstructorList(); + Iterator inlinedConstructorsIterator = inlinedConstructorList.iterator(); + + while (inlinedConstructorsIterator.hasNext()) { + monitor.checkCanceled(); + Function inlinedFunction = inlinedConstructorsIterator.next(); + + List
listOfClassRefsInFunction = + getSortedListOfAncestorRefsInFunction(inlinedFunction, recoveredClass); + + if (!listOfClassRefsInFunction.isEmpty()) { + + Address markupAddress = listOfClassRefsInFunction.get(0); + String markupString = classNamespace.getName(true) + "::" + className; + //TODO: might need to append to existing comment + String existingComment = api.getPreComment(markupAddress); + if (existingComment != null) { + existingComment = existingComment + "\n"; + } + else { + existingComment = ""; + } + api.setPreComment(markupAddress, + existingComment + "inlined constructor: " + markupString); + bookmarkAddress(markupAddress, INLINE_CONSTRUCTOR_BOOKMARK + " " + markupString); + } + } + } + + /** + * Method to add precomment inside functions containing inlined destructors at approximate + * address of start of inlined function + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void createInlinedDestructorComments(RecoveredClass recoveredClass) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + String className = recoveredClass.getName(); + + List inlinedDestructorList = recoveredClass.getInlinedDestructorList(); + Iterator inlinedDestructorIterator = inlinedDestructorList.iterator(); + while (inlinedDestructorIterator.hasNext()) { + monitor.checkCanceled(); + Function destructorFunction = inlinedDestructorIterator.next(); + Address classVftableRef = + getClassVftableRefInFunction(destructorFunction, recoveredClass); + + //TODO: use this one instead if testing pans out + Address otherWayRef = getClassVftableReference(recoveredClass, destructorFunction); + + if (classVftableRef == null) { + continue; + } + + //TODO: remove after testing + if (!classVftableRef.equals(otherWayRef)) { + Msg.debug(this, recoveredClass.getName() + " function " + + destructorFunction.getEntryPoint().toString() + " first ref: " + + classVftableRef.toString() + " other way ref: " + otherWayRef.toString()); + } + + String markupString = classNamespace.getName(true) + "::~" + className; + api.setPreComment(classVftableRef, "inlined destructor: " + markupString); + + bookmarkAddress(classVftableRef, INLINE_DESTRUCTOR_BOOKMARK + " " + markupString); + } + } + + /** + * Method to add label on functions with inlined constructor or destructors but couldn't tell which + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void createIndeterminateInlineComments(RecoveredClass recoveredClass) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + + List functionsContainingInlineList = recoveredClass.getIndeterminateInlineList(); + Iterator functionsContainingInlineIterator = + functionsContainingInlineList.iterator(); + while (functionsContainingInlineIterator.hasNext()) { + monitor.checkCanceled(); + Function functionContainingInline = functionsContainingInlineIterator.next(); + + Address classVftableRef = + getClassVftableRefInFunction(functionContainingInline, recoveredClass); + //TODO: use this one if testing more progs gives same results + Address otherWayRef = + getClassVftableReference(recoveredClass, functionContainingInline); + + if (classVftableRef == null) { + continue; + } + //TODO: remove after testing + if (!classVftableRef.equals(otherWayRef)) { + Msg.debug(this, + recoveredClass.getName() + " function " + + functionContainingInline.getEntryPoint().toString() + " first ref: " + + classVftableRef.toString() + " other way ref: " + otherWayRef.toString()); + } + + String markupString = "inlined constructor or destructor (approx location) for " + + classNamespace.getName(true); + api.setPreComment(classVftableRef, markupString); + + bookmarkAddress(classVftableRef, INDETERMINATE_INLINE_BOOKMARK + " " + markupString); + } + } + + /** + * Method to add label on constructor or destructors but couldn't tell which + * @param recoveredClass current class + * @throws Exception when cancelled + */ + public void createIndeterminateLabels(RecoveredClass recoveredClass) throws Exception { + + Namespace classNamespace = recoveredClass.getClassNamespace(); + String className = recoveredClass.getName(); + + List unknownIfConstructorOrDestructorLIst = recoveredClass.getIndeterminateList(); + Iterator unknownsIterator = unknownIfConstructorOrDestructorLIst.iterator(); + while (unknownsIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = unknownsIterator.next(); + createNewSymbolAtFunction(indeterminateFunction, + className + "_Constructor_or_Destructor", classNamespace, false, false); + } + } + + /** + * Method to create an ANALYSIS bookmark at the given address with the given comment + * @param address the given address + * @param comment the given comment + */ + public void bookmarkAddress(Address address, String comment) { + + BookmarkManager bookmarkMgr = program.getBookmarkManager(); + + Bookmark bookmark = + bookmarkMgr.getBookmark(address, BookmarkType.ANALYSIS, BOOKMARK_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, BOOKMARK_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. + * @param bookmarkComment the bookmark comment string + * @param string the string to search for within the comment + * @return true if string is contained exactly within the +'s + */ + private boolean containsString(String bookmarkComment, String string) { + + // first split comment into pieces between the +'s + String[] commentPieces = bookmarkComment.split("\\+"); + for (String piece : commentPieces) { + // remove leading and trailing spaces from each piece + int len = piece.length(); + + if (piece.charAt(len - 1) == ' ') { + piece = piece.substring(0, len - 2); + } + + if (piece.charAt(0) == ' ') { + piece = piece.substring(1); + } + + // return true if any piece exactly equals the new string + if (piece.equals(string)) { + return true; + } + } + + // return false if string does not match any of the pieces + return false; + + } + + /** + * 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 + */ + private Function findOperatorDeleteUsingKnownDeletingDestructors( + List recoveredClasses) throws CancelledException, Exception { + + Function possibleOperatorDelete = null; + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List deletingDestructors = recoveredClass.getDeletingDestructors(); + Iterator deletingDestructorIterator = deletingDestructors.iterator(); + while (deletingDestructorIterator.hasNext()) { + 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 = + extraUtils.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 = + extraUtils.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 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; + + } + + /** + * + * @param recoveredClass the given class + * @param virtualFunction the given virtual function + * @param operatorDeleteFunction the operator delete function + * @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 { + + // don't continue checking if it doesn't call operator_delete + if (!extraUtils.doesFunctionACallFunctionB(virtualFunction, operatorDeleteFunction)) { + return; + } + + List ownConstructorOrDestructorFunctions = + new ArrayList(recoveredClass.getConstructorOrDestructorFunctions()); + + ownConstructorOrDestructorFunctions.removeAll(recoveredClass.getConstructorList()); + ownConstructorOrDestructorFunctions.removeAll(recoveredClass.getInlinedConstructorList()); + + Iterator functionIterator = ownConstructorOrDestructorFunctions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); + Function function = functionIterator.next(); + + //Type 4 - class c/d called from other than first vfunction + if (extraUtils.doesFunctionACallFunctionB(virtualFunction, function)) { + recoveredClass.addDeletingDestructor(virtualFunction); + addDestructorToClass(recoveredClass, function); + recoveredClass.removeIndeterminateConstructorOrDestructor(function); + return; + } + } + + // 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 = getClassVftableReference(recoveredClass, virtualFunction); + + //TODO remove after testing against prev method in more progs + Address otherWayRef = getClassVftableRefInFunction(virtualFunction, recoveredClass); + if (vftableReference == null) { + return; + } + if (!vftableReference.equals(otherWayRef)) { + Msg.debug(this, + recoveredClass.getName() + " function " + + virtualFunction.getEntryPoint().toString() + " first ref: " + + vftableReference.toString() + " other way ref (with ances): " + + otherWayRef.toString()); + } + + List possibleParentDestructors = getPossibleParentDestructors(virtualFunction); + + boolean foundVftableRef = false; + Function parentDestructor = null; + + AddressSetView virtualFunctionBody = virtualFunction.getBody(); + CodeUnitIterator virtualFunctionCodeUnits = + program.getListing().getCodeUnits(virtualFunctionBody, true); + while (virtualFunctionCodeUnits.hasNext()) { + monitor.checkCanceled(); + CodeUnit codeUnit = virtualFunctionCodeUnits.next(); + Address codeUnitAddress = codeUnit.getAddress(); + + if (codeUnitAddress.equals(vftableReference)) { + foundVftableRef = true; + continue; + } + + Function referencedFunction = extraUtils.getReferencedFunction(codeUnitAddress, true); + if (referencedFunction == null) { + continue; + } + + if (referencedFunction.equals(operatorDeleteFunction)) { + // if find operator delete call before vftable ref then not valid deleting destructor + if (!foundVftableRef) { + return; + } + + recoveredClass.addDeletingDestructor(virtualFunction); + if (recoveredClass.getDestructorList().contains(virtualFunction)) { + 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; + } + + } + + } + + /** + * Method to remove functions from the class constructor/destructor lists (and the overall list) + * that are not self-contained constructor/destructor functions. Add them to the list of + * functions that contain inlined constructors or destructors. + * NOTE: this must be called after the global const/dest list is created but before + * functions get added to the other class lists + * @param recoveredClasses list of classes to process + * @throws CancelledException if cancelled + */ + public void separateInlinedConstructorDestructors(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List indeterminateFunctions = recoveredClass.getIndeterminateList(); + Iterator indeterminateIterator = indeterminateFunctions.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + + List
vftableReferenceList = getVftableReferences(indeterminateFunction); + if (vftableReferenceList == null) { + continue; + } + + // if inline, put on separate list and remove from indeterminate list + // process later + if (vftableReferenceList.size() > 1) { + if (!areVftablesInSameClass(vftableReferenceList)) { + recoveredClass.addIndeterminateInline(indeterminateFunction); + indeterminateIterator.remove(); + } + + continue; + } + } + } + } + + /** + * Method to add the given structcure component to the given structure at the given offset + * @param structureDataType the structure to add to + * @param structureToAdd the structure to add + * @param startOffset the starting offset where to add + * @param endOffset the ending offset of the added structure + * @return the updated structure + * @throws CancelledException if cancelled + */ + public Structure addIndividualComponentsToStructure(Structure structureDataType, + Structure structureToAdd, int startOffset, int endOffset) throws CancelledException { + + DataTypeComponent[] definedComponents = structureToAdd.getDefinedComponents(); + for (int ii = 0; ii < definedComponents.length; ii++) { + + monitor.checkCanceled(); + + DataTypeComponent dataTypeComponent = structureToAdd.getComponent(ii); + + int dataComponentOffset = dataTypeComponent.getOffset(); + if (endOffset != NONE && (dataComponentOffset + startOffset) >= endOffset) { + return structureDataType; + } + + // This is to distinguish between class items and parent items in classes that + // have individual components of the parent split out and added to them and not just + // the whole parent structure added to them + // TODO: figure out how to get the class object from the nameof the structure + // so i can get the shortened class name if a template and put that here instead + String fieldname = structureToAdd.getName() + "_" + dataTypeComponent.getFieldName(); + + structureDataType = structUtils.addDataTypeToStructure(structureDataType, + startOffset + dataComponentOffset, dataTypeComponent.getDataType(), fieldname, + monitor); + } + return structureDataType; + } + + /** + * Method to add alignment to the given length based on the default program address size + * @param len the given length + * @return len updated with alignment size + */ + public int addAlignment(int len) { + + int mod = len % defaultPointerSize; + int alignment = 0; + if (mod != 0) { + alignment = defaultPointerSize - mod; + len += alignment; + } + return len; + } + + /** + * Method to retrieve the offset of the virtual parent of the given class in the given structure + * @param recoveredClass the given class + * @param structure the given structure + * @return the offset of the virtual parent of the given class in the given structure + * @throws CancelledException if cancelled + */ + public int getOffsetOfVirtualParent(RecoveredClass recoveredClass, Structure structure) + throws CancelledException { + + DataTypeComponent[] definedComponents = structure.getDefinedComponents(); + + for (DataTypeComponent dataTypeComponent : definedComponents) { + // if run into a virtual parent class structure, return its offset + monitor.checkCanceled(); + if (isVirtualParentClassStructure(recoveredClass, dataTypeComponent.getDataType())) { + return dataTypeComponent.getOffset(); + } + } + return NONE; + + } + + /** + * Method to determine if the given data type is the virtual parent class structure for the given class + * @param recoveredClass the given class + * @param dataType the given data type + * @return true if the given data type is the virtual parent class structure for the given class + * @throws CancelledException if cancelled + */ + private boolean isVirtualParentClassStructure(RecoveredClass recoveredClass, DataType dataType) + throws CancelledException { + + // return false right away if it isn't even a structure + if (!(dataType instanceof Structure)) { + return false; + } + + String parentClassName = dataType.getName(); + + Map parentToBaseTypeMap = recoveredClass.getParentToBaseTypeMap(); + + Set parentClasses = parentToBaseTypeMap.keySet(); + Iterator parentClassIterator = parentClasses.iterator(); + while (parentClassIterator.hasNext()) { + + monitor.checkCanceled(); + RecoveredClass parentClass = parentClassIterator.next(); + if (parentClass.getName().equals(parentClassName)) { + Boolean isVirtualParent = parentToBaseTypeMap.get(parentClass); + if (isVirtualParent) { + return true; + } + } + + } + return false; + + } + + /** + * Method to determine if all of a class's vftables are accounted for in its classOffsetToVftableMap + * @param recoveredClass the given class + * @return true if all vftables have a mapping, false otherwise + */ + public boolean isClassOffsetToVftableMapComplete(RecoveredClass recoveredClass) { + + if (recoveredClass.getClassOffsetToVftableMap().values().containsAll( + recoveredClass.getVftableAddresses())) { + return true; + } + 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 = + extraUtils.getCalledFunctionByCallOrder(vFunction, 1); + Function secondCalledFunction = + extraUtils.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 + * @throws CancelledException if cancelled + */ + public void findDestructorsWithNoParamsOrReturn(List recoveredClasses) + throws CancelledException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + while (recoveredClassIterator.hasNext()) { + + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List indeterminateFunctions = recoveredClass.getIndeterminateList(); + Iterator indeterminateIterator = indeterminateFunctions.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + + DataType returnDataType = + decompilerUtils.getDecompilerReturnType(indeterminateFunction); + if (returnDataType == null) { + continue; + } + + String returnDataName = returnDataType.getDisplayName(); + //ParameterDefinition[] params = getParametersFromDecompiler(indeterminateFunction); + ParameterDefinition[] params = + decompilerUtils.getParametersFromDecompiler(indeterminateFunction); + int numberParams = 0; + + if (params == null) { + numberParams = indeterminateFunction.getParameterCount(); + } + else { + numberParams = params.length; + } + + if (numberParams == 0 && returnDataName.equals("void")) { + + Address firstVftableReference = + getFirstVftableReferenceInFunction(indeterminateFunction); + if (firstVftableReference == null) { + continue; + } + + FillOutStructureCmd fillCmd = + runFillOutStructureCmd(indeterminateFunction, firstVftableReference); + + if (fillCmd == null) { + continue; + } + + List stores = fillCmd.getStorePcodeOps(); + List loads = fillCmd.getLoadPcodeOps(); + stores = removePcodeOpsNotInFunction(indeterminateFunction, stores); + loads = removePcodeOpsNotInFunction(indeterminateFunction, loads); + + if (loads == null || stores == null) { + continue; + } + + if (stores.size() == 1 && loads.size() == 0) { + recoveredClass.addNonThisDestructor(indeterminateFunction); + indeterminateIterator.remove(); + } + } + } + } + } + + /** + * Method to determine if the vftable reference(s) in a constructor are not in the first code + * block for constructors that have more than one block + * @param recoveredClasses List of RecoveredClass objects + * @throws CancelledException when cancelled + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void findMoreInlinedConstructors(List recoveredClasses) + throws CancelledException, InvalidInputException, DuplicateNameException { + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + List constructorList = recoveredClass.getConstructorList(); + Iterator constructorIterator = constructorList.iterator(); + while (constructorIterator.hasNext()) { + monitor.checkCanceled(); + Function constructor = constructorIterator.next(); + + // get the references to the vftable(s) that are referenced in this function + + List
referencesToVftablesFromFunction = getVftableReferences(constructor); + + if (referencesToVftablesFromFunction == null) { + continue; + } + + Collections.sort(referencesToVftablesFromFunction); + Address firstVftableReferenceAddress = referencesToVftablesFromFunction.get(0); + + Address firstEndOfBlock = getEndOfFirstBlockAddress(constructor); + if (firstEndOfBlock != null) { + + // not as reliable for virtual or multi-virtual inheritance so skip + if (recoveredClass.inheritsVirtualAncestor() || + recoveredClass.hasMultipleVirtualInheritance()) { + 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 + // function inlined in it + + if (firstVftableReferenceAddress.compareTo(firstEndOfBlock) > 0) { + if (doesFunctionCallAnyNonParentConstructorsBeforeVtableReference( + recoveredClass, constructor, firstVftableReferenceAddress)) { + + // remove from the allConstructors too + addInlinedConstructorToClass(recoveredClass, constructor); + constructorIterator.remove(); + removeFromAllConstructors(constructor); + + } + } + } + } + } + + } + + /** + * Method to retrieve the address of the end of the first code block in the given function + * @param function the given function + * @return the address of the end of the first code block in the given function + * @throws CancelledException if cancelled + */ + private Address getEndOfFirstBlockAddress(Function function) throws CancelledException { + + Address instructionAddress = null; + Listing listing = program.getListing(); + AddressSetView functionAddressSet = function.getBody(); + InstructionIterator instructionsIterator = + listing.getInstructions(functionAddressSet, true); + while (instructionsIterator.hasNext()) { + monitor.checkCanceled(); + Instruction instruction = instructionsIterator.next(); + if (!instruction.isFallthrough() && (!instruction.getFlowType().isCall())) { + instructionAddress = instruction.getAddress(); + return instructionAddress; + } + + } + + return instructionAddress; + + } + + /** + * Method to run the FillOutStructureCmd and return a FillOutStructureCmd object when + * a high variable used to run the cmd is found that stores the given firstVftableReference + * address. + * @param function the given function + * @param firstVftableReference the first vftableReference in the given function + * @return FillOutStructureCmd for the highVariable that stores the firstVftableReference address + * or null if one isn't found. + * @throws CancelledException if cancelled + */ + public FillOutStructureCmd runFillOutStructureCmd(Function function, + Address firstVftableReference) throws CancelledException { + + Address vftableAddress = getVftableAddress(firstVftableReference); + + if (vftableAddress == null) { + return null; + } + + // get the decompiler highFunction + HighFunction highFunction = decompilerUtils.getHighFunction(function); + + if (highFunction == null) { + return null; + } + + List highVariables = new ArrayList(); + + // if there are params add the first or the "this" param to the list to be checked first + // It is the most likely to store the vftablePtr + if (highFunction.getFunctionPrototype().getNumParams() > 0) { + + HighVariable thisParam = + highFunction.getFunctionPrototype().getParam(0).getHighVariable(); + if (thisParam != null) { + highVariables.add(thisParam); + } + } + + // add the other high variables that store vftable pointer + highVariables.addAll( + getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); + + Iterator highVariableIterator = highVariables.iterator(); + + while (highVariableIterator.hasNext()) { + + HighVariable highVariable = highVariableIterator.next(); + monitor.checkCanceled(); + + FillOutStructureCmd fillCmd = new FillOutStructureCmd(program, location, tool); + fillCmd.processStructure(highVariable, function); + List stores = fillCmd.getStorePcodeOps(); + stores = removePcodeOpsNotInFunction(function, stores); + + // this method checks the storedPcodeOps to see if one is the vftable address + Address storedVftableAddress = getStoredVftableAddress(stores); + if (storedVftableAddress == null) { + continue; + } + + if (storedVftableAddress.equals(vftableAddress)) { + return fillCmd; + } + + } + return null; + } + + /** + * Method to figure out the indetermined inlined functions from each class as either combination + * constructor/inlined constructor or destrucor/inlined destructor. The method first uses any + * known called constructors or destructors to help determine which type then calls a method to + * determine, using vftable order, which class contains the constructor/destructor and which + * contains the inlined constructor/destructor. + * @param recoveredClasses List of classes + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if error setting return type + * @throws CancelledException when cancelled and others + */ + public void processInlinedConstructorsAndDestructors(List recoveredClasses) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List inlineFunctionsList = + new ArrayList<>(recoveredClass.getIndeterminateInlineList()); + + Iterator inlineIterator = inlineFunctionsList.iterator(); + while (inlineIterator.hasNext()) { + monitor.checkCanceled(); + + Function inlineFunction = inlineIterator.next(); + + // get the addresses in the function that refer to classes either by + // referencing a vftable in a class or by calling a function in a class + // TODO: add the atexit refs and then check them - make a map of atexit call to class map if I don't havei it already + Map referenceToClassMap = + getReferenceToClassMap(recoveredClass, inlineFunction); + List
referencesToFunctions = + extraUtils.getReferencesToFunctions(referenceToClassMap); + + // if some of the references are to functions figure out if they are + // constructors destructors or add them to list of indetermined + boolean isConstructor = false; + boolean isDestructor = false; + List
referenceToIndeterminates = new ArrayList
(); + + if (!referencesToFunctions.isEmpty()) { + Iterator
functionReferenceIterator = referencesToFunctions.iterator(); + while (functionReferenceIterator.hasNext()) { + + monitor.checkCanceled(); + Address functionReference = functionReferenceIterator.next(); + Function function = + extraUtils.getReferencedFunction(functionReference, true); + if (function == null) { + continue; + } + + if (getAllConstructors().contains(function) || + getAllInlinedConstructors().contains(function)) { + isConstructor = true; + continue; + } + + if (getAllDestructors().contains(function) || + getAllInlinedDestructors().contains(function)) { + isDestructor = true; + continue; + } + + // TODO: refactor to make this function and refactor method that uses + // it to use function instead of refiguring it out + referenceToIndeterminates.add(functionReference); + + } + + } + + // if one or more is a constructor and none are destructors then the indeterminate + // inline is is an inlined constructor + if (isConstructor == true && isDestructor == false) { + processInlineConstructor(recoveredClass, inlineFunction, referenceToClassMap); + } + // if one or more is a destructor and none are constructors then the indeterminate + // inline is an inlined destructor + else if (isConstructor == false && isDestructor == true) { + processInlineDestructor(recoveredClass, inlineFunction, referenceToClassMap); + } + else { + + // otherwise, use pcode info to figure out if inlined constructor or destructor + //If not already, make function a this call + makeFunctionThiscall(inlineFunction); + + List loads = getLoadPcodeOpPairs(inlineFunction); + List stores = getStorePcodeOpPairs(inlineFunction); + + if (loads == null || stores == null) { + Address firstVftableReferenceInFunction = + getFirstVftableReferenceInFunction(inlineFunction); + if (firstVftableReferenceInFunction == null) { + continue; + } + FillOutStructureCmd fillOutStructureCmd = + runFillOutStructureCmd(inlineFunction, firstVftableReferenceInFunction); + + if (fillOutStructureCmd == null) { + continue; + } + + loads = fillOutStructureCmd.getLoadPcodeOps(); + loads = removePcodeOpsNotInFunction(inlineFunction, loads); + stores = fillOutStructureCmd.getStorePcodeOps(); + stores = removePcodeOpsNotInFunction(inlineFunction, stores); + + updateFunctionToStorePcodeOpsMap(inlineFunction, stores); + updateFunctionToLoadPcodeOpsMap(inlineFunction, loads); + + } + + if (loads == null || stores == null) { + continue; + } + + // inlined constructor + if (stores.size() > 1 && loads.size() == 0) { + processInlineConstructor(recoveredClass, inlineFunction, + referenceToClassMap); + isConstructor = true; + } + + // inlined destructor + else if (stores.size() == 1 && loads.size() > 0) { + processInlineDestructor(recoveredClass, inlineFunction, + referenceToClassMap); + isDestructor = true; + } + } + + if (!referenceToIndeterminates.isEmpty()) { + // make the other referenced indeterminate c/d functions constructors + if (isConstructor == true && isDestructor == false) { + createListedConstructorFunctions(referenceToClassMap, + referenceToIndeterminates); + continue; + } + // make the other referenced indeterminate c/d functions destructors + if (isConstructor == false && isDestructor == true) { + createListedDestructorFunctions(referenceToClassMap, + referenceToIndeterminates); + continue; + } + + } + + } + + } + } + + /** + * Method to retrieve the offset of the class data in the given structure + * @param recoveredClass the given class + * @param structure the given structure + * @return the offset of the class data in the given structure + * @throws CancelledException if cancelled + */ + public int getDataOffset(RecoveredClass recoveredClass, Structure structure) + throws CancelledException { + + int offsetOfVirtualParent = getOffsetOfVirtualParent(recoveredClass, structure); + + int endOfData; + if (offsetOfVirtualParent == NONE) { + endOfData = structure.getLength(); + } + else { + // end of data is beginning of virt parent + endOfData = offsetOfVirtualParent; + } + + int dataLength = + structUtils.getNumberOfUndefinedsBeforeOffset(structure, endOfData, monitor); + if (dataLength < 0) { + return NONE; + } + + return endOfData - dataLength; + + } + + /** + * Method to process remaining indeterminate functions to determine if they are constructors or destructors + * @param recoveredClasses list of classes + * @throws CancelledException if cancelled + * @throws InvalidInputException if issues setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public void processRemainingIndeterminateConstructorsAndDestructors( + List recoveredClasses) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + Iterator classIterator = recoveredClasses.iterator(); + while (classIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = classIterator.next(); + + List indeterminateList = recoveredClass.getIndeterminateList(); + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + + // first try identifying useing known constructors and destructors + boolean callsKnownConstructor = callsKnownConstructor(indeterminateFunction); + boolean callsKnownDestrutor = callsKnownDestructor(indeterminateFunction); + boolean callsAtexit = + extraUtils.doesFunctionACallFunctionB(indeterminateFunction, atexit); + + if (callsKnownConstructor && !callsKnownDestrutor) { + addConstructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + continue; + } + if (!callsKnownConstructor && callsKnownDestrutor) { + addDestructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + continue; + } + + if (!callsKnownConstructor && callsAtexit) { + addDestructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + continue; + } + + // Next try identifying constructors using decompiler return type + DataType decompilerReturnType = + decompilerUtils.getDecompilerReturnType(indeterminateFunction); + if (decompilerReturnType != null) { + + String returnDataName = decompilerReturnType.getDisplayName(); + // TODO: update to check that isn't FID with diff name or should I assume + // that if it is a FID that wasn't found by traditional means it is an inline? + if (returnDataName.contains("*") && !isFidFunction(indeterminateFunction)) { + + addConstructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + continue; + } + } + + // Next try identifying using load/store information + List loads = getLoadPcodeOpPairs(indeterminateFunction); + + List stores = getStorePcodeOpPairs(indeterminateFunction); + + if (loads == null || stores == null) { + Address firstVftableReferenceInFunction = + getFirstVftableReferenceInFunction(indeterminateFunction); + if (firstVftableReferenceInFunction == null) { + continue; + } + FillOutStructureCmd fillOutStructureCmd = runFillOutStructureCmd( + indeterminateFunction, firstVftableReferenceInFunction); + + if (fillOutStructureCmd == null) { + continue; + } + + loads = fillOutStructureCmd.getLoadPcodeOps(); + loads = removePcodeOpsNotInFunction(indeterminateFunction, loads); + stores = fillOutStructureCmd.getStorePcodeOps(); + stores = removePcodeOpsNotInFunction(indeterminateFunction, stores); + + updateFunctionToStorePcodeOpsMap(indeterminateFunction, stores); + updateFunctionToLoadPcodeOpsMap(indeterminateFunction, loads); + + } + + if (loads == null || stores == null) { + continue; + } + + if (stores.size() > 1 && loads.size() == 0) { + addConstructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + } + else if (stores.size() == 1 && loads.size() > 0) { + addDestructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + } + + } + } + + } + + /** + * Method to determine if the given function is a "FID" function or one that has had function + * signature assigned by the FID (Function ID) Analyzer + * @param function the given function + * @return true if function is a FID function, false otherwise + */ + private boolean isFidFunction(Function function) { + + Address address = function.getEntryPoint(); + + BookmarkManager bm = program.getBookmarkManager(); + + Bookmark bookmark = bm.getBookmark(address, BookmarkType.ANALYSIS, "Function ID Analyzer"); + + if (bookmark == null) { + return false; + } + return true; + } + + /** + * Method to identify missing functions using param to _atexit function + * because it always is passed a pointer to a function. + * @throws CancelledException if cancelled + * @throws InvalidInputException if return type is not a fixed length + */ + public void findFunctionsUsingAtexit() throws CancelledException, InvalidInputException { + + Function atexitFunction = null; + List atexitFunctions = extraUtils.getGlobalFunctions("_atexit"); + if (atexitFunctions.size() != 1) { + return; + } + + atexitFunction = atexitFunctions.get(0); + atexit = atexitFunction; + + ReferenceIterator referenceIterator = + program.getReferenceManager().getReferencesTo(atexitFunction.getEntryPoint()); + while (referenceIterator.hasNext()) { + monitor.checkCanceled(); + Reference ref = referenceIterator.next(); + Address fromAddress = ref.getFromAddress(); + + Function function = extraUtils.getFunctionContaining(fromAddress); + if (function == null) { + AddressSet subroutineAddresses = + extraUtils.getSubroutineAddresses(program, fromAddress, monitor); + Address minAddress = subroutineAddresses.getMinAddress(); + + function = extraUtils.createFunction(minAddress, null); + if (function == null) { + continue; + } + } + + // get the decompiler highFunction + HighFunction highFunction = decompilerUtils.getHighFunction(function); + + if (highFunction == null) { + continue; + } + + Iterator pcodeOps = highFunction.getPcodeOps(fromAddress); + while (pcodeOps.hasNext()) { + monitor.checkCanceled(); + PcodeOpAST pcodeOp = pcodeOps.next(); + int opcode = pcodeOp.getOpcode(); + if (opcode == PcodeOp.CALL) { + Varnode input = pcodeOp.getInput(1); + if (input == null) { + continue; + } + Address callingAddress = input.getPCAddress(); + if (callingAddress.equals(Address.NO_ADDRESS)) { + continue; + } + + Address calledAddress = + decompilerUtils.getCalledAddressFromCallingPcodeOp(input); + + if (calledAddress == null) { + continue; + } + + Function calledFunction = extraUtils.getFunctionAt(calledAddress); + if (calledFunction == null) { + calledFunction = extraUtils.createFunction(calledAddress, null); + if (calledFunction == null) { + continue; + } + + if (!atexitCalledFunctions.contains(calledFunction)) { + atexitCalledFunctions.add(calledFunction); + } + calledFunction.setReturnType(DataType.VOID, SourceType.ANALYSIS); + } + else { + if (!atexitCalledFunctions.contains(calledFunction)) { + atexitCalledFunctions.add(calledFunction); + } + } + + } + + } + + } + + } + + /** + * 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 = extraUtils.getPointedToFunction(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) { + 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 = extraUtils.getPointedToFunction(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 + * is both a deleting destructor and has inlined the class destructor + * 2. reference their parent vftable (ie on parent c/d list) which means function + * is a deleting destructor for class and inlined destructor for parent class + * 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) + 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 + // determine if the inline is the class or parent/grandparent class destructor + if (getAllConstructorsAndDestructors().contains(firstVftableFunction)) { + + recoveredClass.addDeletingDestructor(firstVftableFunction); + recoveredClass.removeFromConstructorDestructorList(firstVftableFunction); + recoveredClass.removeIndeterminateConstructorOrDestructor(firstVftableFunction); + + List
vftableReferences = getVftableReferences(firstVftableFunction); + if (vftableReferences == null) { + return; + } + Iterator
vftableReferencesIterator = vftableReferences.iterator(); + while (vftableReferencesIterator.hasNext()) { + monitor.checkCanceled(); + Address vftableReference = vftableReferencesIterator.next(); + Address vftableAddress = getVftableAddress(vftableReference); + if (vftableAddress == null) { + continue; + } + // Type 1 + if (recoveredClass.getVftableAddresses().contains(vftableAddress)) { + recoveredClass.addInlinedDestructor(firstVftableFunction); + } + // Type 2 + else { + //RecoveredClass parentClass = vftableToClassMap.get(vftableAddress); + RecoveredClass parentClass = getVftableClass(vftableAddress); + parentClass.addInlinedDestructor(firstVftableFunction); + parentClass.removeFromConstructorDestructorList(firstVftableFunction); + parentClass.removeIndeterminateConstructorOrDestructor(firstVftableFunction); + } + } + + } + // else, if first function pointed to by the vftable CALLS a function on the constructor/destructor list + // then it is a deleting destructor and we have identified a destructor function for the class + else { + processClassDeletingDestructor(recoveredClass, firstVftableFunction); + } + + } + + /** + * 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 { + + List classConstructorOrDestructorFunctions = + recoveredClass.getConstructorOrDestructorFunctions(); + + Iterator functionIterator = classConstructorOrDestructorFunctions.iterator(); + while (functionIterator.hasNext()) { + monitor.checkCanceled(); + + Function function = functionIterator.next(); + + if (extraUtils.doesFunctionACallFunctionB(firstVirtualFunction, function)) { + recoveredClass.addDeletingDestructor(firstVirtualFunction); + addDestructorToClass(recoveredClass, function); + recoveredClass.removeIndeterminateConstructorOrDestructor(function); + + if (!deletingDestructorsThatCallDestructor.contains(firstVirtualFunction)) { + deletingDestructorsThatCallDestructor.add(firstVirtualFunction); + } + } + } + } + + /** + * Use the known parent class(es)to determine which possible constructor destructor + * functions are constructors and which are destructors + * @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 + * @throws CircularDependencyException if parent namespace is descendent of given namespace + */ + public void processRegularConstructorsAndDestructorsUsingCallOrder( + List recoveredClasses) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List parentsToProcess = recoveredClass.getParentList(); + + if (parentsToProcess.isEmpty()) { + continue; + } + + Iterator parentsToProcessIterator = parentsToProcess.iterator(); + + while (parentsToProcessIterator.hasNext()) { + + monitor.checkCanceled(); + + RecoveredClass parentToProcess = parentsToProcessIterator.next(); + processConstructorsAndDestructorsUsingParent(recoveredClass, parentToProcess); + } + } + + } + + /** + * Use known ancestor class constructors and destructors to help classify indeterminate ones + * by who they call, ie constructors call parent (or grandparent) constructors and destructors + * call parent (or grandparent) destructors so use this to help figure out if the given class's + * indeterminate functions are constructors or destructors. + * @param recoveredClasses List of class objects + * @throws CancelledException if cancelled + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if error setting return type + */ + public void findConstructorsAndDestructorsUsingAncestorClassFunctions( + List recoveredClasses) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List indeterminateList = recoveredClass.getIndeterminateList(); + if (indeterminateList.isEmpty()) { + continue; + } + + List allAncestorConstructors = getAllAncestorConstructors(recoveredClass); + List allAncestorDestructors = getAncestorDestructors(recoveredClass); + + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + + monitor.checkCanceled(); + + Function indeterminateFunction = indeterminateIterator.next(); + + List possibleAncestorConstructors = + getPossibleParentConstructors(indeterminateFunction); + Function ancestorConstructor = + getFunctionOnBothLists(possibleAncestorConstructors, allAncestorConstructors); + + List possibleAncestorDestructors = + getPossibleParentDestructors(indeterminateFunction); + Function ancestorDestructor = + getFunctionOnBothLists(possibleAncestorDestructors, allAncestorDestructors); + + // skip if both null - no results + if (ancestorConstructor == null && ancestorDestructor == null) { + continue; + } + + // skip if neither null - conflicting results + if (ancestorConstructor != null && ancestorDestructor != null) { + continue; + } + + if (ancestorConstructor != null) { + addConstructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + } + + if (ancestorDestructor != null) { + addDestructorToClass(recoveredClass, indeterminateFunction); + indeterminateIterator.remove(); + } + } + } + + } + + /** + * Method to classify indeterminate inline functions as either constructors or destructors + * using called ancestor information (may call parent or higher ancestor) or might be the same + * as a descendant constructor/destructor (ie a parent or ancestor is inlined into an + * indeterminate function so the same function is on both the parent inline list and the + * descendant regular list. + * @param recoveredClasses list of classes + * @throws CancelledException if cancelled + * @throws CircularDependencyException if parent namespace is descendent of given namespace + * @throws DuplicateNameException if try to create same symbol name already in namespace + * @throws InvalidInputException if error setting return type + */ + public void findInlineConstructorsAndDestructorsUsingRelatedClassFunctions( + List recoveredClasses) throws CancelledException, InvalidInputException, + DuplicateNameException, CircularDependencyException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List indeterminateList = + new ArrayList(recoveredClass.getIndeterminateInlineList()); + + if (indeterminateList.isEmpty()) { + continue; + } + + List allRelatedConstructors = getAllAncestorConstructors(recoveredClass); + List allRelatedDestructors = getAncestorDestructors(recoveredClass); + + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + + // get the addresses in the function that refer to classes either by + // referencing a vftable in a class or by calling a function in a class + Map referenceToClassMap = + getReferenceToClassMap(recoveredClass, indeterminateFunction); + + List allDescendantConstructors = + getAllDescendantConstructors(recoveredClass); + if (allDescendantConstructors.contains(indeterminateFunction)) { + processInlineConstructor(recoveredClass, indeterminateFunction, + referenceToClassMap); + continue; + } + + List allDescendantDestructors = + getAllDescendantDestructors(recoveredClass); + if (allDescendantDestructors.contains(indeterminateFunction)) { + processInlineDestructor(recoveredClass, indeterminateFunction, + referenceToClassMap); + continue; + } + + List possibleAncestorConstructors = + getPossibleParentConstructors(indeterminateFunction); + + Function ancestorConstructor = + getFunctionOnBothLists(possibleAncestorConstructors, allRelatedConstructors); + + List possibleAncestorDestructors = + getPossibleParentDestructors(indeterminateFunction); + Function ancestorDestructor = + getFunctionOnBothLists(possibleAncestorDestructors, allRelatedDestructors); + + // skip if both null - no results + if (ancestorConstructor == null && ancestorDestructor == null) { + continue; + } + + // skip if both null - conflicting results + if (ancestorConstructor != null && ancestorDestructor != null) { + continue; + } + + if (ancestorConstructor != null) { + processInlineConstructor(recoveredClass, indeterminateFunction, + referenceToClassMap); + continue; + } + + if (ancestorDestructor != null) { + processInlineDestructor(recoveredClass, indeterminateFunction, + referenceToClassMap); + continue; + } + + } + } + + } + + /** + * Method to find destructors using functions called by atexit. If they are on the list of + * indeterminate constructors or destructors and are called by atexit, then they are a + * destructor. + * @param recoveredClasses list of classes + * @throws CancelledException if cancelled + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void findDestructorsUsingAtexitCalledFunctions(List recoveredClasses) + throws CancelledException, InvalidInputException, DuplicateNameException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + + List indeterminateList = recoveredClass.getIndeterminateList(); + + Iterator indeterminateIterator = indeterminateList.iterator(); + while (indeterminateIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateIterator.next(); + if (atexitCalledFunctions.contains(indeterminateFunction)) { + recoveredClass.addNonThisDestructor(indeterminateFunction); + indeterminateIterator.remove(); + } + } + + List indeterminateInlineList = recoveredClass.getIndeterminateInlineList(); + + Iterator indeterminateInlineIterator = indeterminateInlineList.iterator(); + while (indeterminateInlineIterator.hasNext()) { + monitor.checkCanceled(); + Function indeterminateFunction = indeterminateInlineIterator.next(); + if (atexitCalledFunctions.contains(indeterminateFunction)) { + addInlinedDestructorToClass(recoveredClass, indeterminateFunction); + indeterminateInlineIterator.remove(); + } + } + } + } + + /** + * Method that calls various methods to find deleting destructor functions which help + * identify class destructors + * @param recoveredClasses List of all class objects + * @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 + */ + public void findDeletingDestructors(List recoveredClasses) + throws CancelledException, InvalidInputException, DuplicateNameException, Exception { + + AddressSetView beforeChanges = program.getChanges().getAddressSet(); + + findFirstDeletingDestructors(recoveredClasses); + findMoreDeletingDestructors(recoveredClasses); + findDeletingDestructorsWithCallToDestructorWithNoVftableReference(recoveredClasses); + + } + + /** + * Figure out which of the destructors that do not reference vftable are vbase destructors and + * which are destructors. + * @param recoveredClasses List of RecoveredClass objects + * @throws CancelledException if cancelled + * @throws InvalidInputException if error setting return type + * @throws DuplicateNameException if try to create same symbol name already in namespace + */ + public void findRealVBaseFunctions(List recoveredClasses) + throws CancelledException, InvalidInputException, DuplicateNameException { + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + RecoveredClass recoveredClass = recoveredClassIterator.next(); + Function vBaseDestructor = recoveredClass.getVBaseDestructor(); + if (vBaseDestructor == null) { + continue; + } + if (!hasVbaseDestructor(recoveredClass)) { + addDestructorToClass(recoveredClass, vBaseDestructor); + recoveredClass.setVBaseDestructor(null); + } + + } + } + + /** + * Method to create _data structure for given class + * @param recoveredClass the class + * @throws CancelledException if cancelled + */ + public Structure createClassMemberDataStructure(RecoveredClass recoveredClass, + Structure classStructure, int dataLen, int dataOffset) throws CancelledException { + + Structure classDataStructure = new StructureDataType(recoveredClass.getClassPath(), + recoveredClass.getName() + CLASS_DATA_STRUCT_NAME, dataLen, dataTypeManager); + + Structure computedClassDataStructure; + + if (recoveredClass.hasExistingClassStructure()) { + computedClassDataStructure = recoveredClass.getExistingClassStructure(); + } + else { + computedClassDataStructure = recoveredClass.getComputedClassStructure(); + } + + if (computedClassDataStructure == null || computedClassDataStructure.getLength() == 0) { + classDataStructure = (Structure) dataTypeManager.addDataType(classDataStructure, + DataTypeConflictHandler.DEFAULT_HANDLER); + return classDataStructure; + } + + DataTypeComponent[] definedComponents = computedClassDataStructure.getDefinedComponents(); + + for (DataTypeComponent definedComponent : definedComponents) { + + monitor.checkCanceled(); + + int offset = definedComponent.getOffset() - dataOffset; + + // skip any components that have offset less than the skip length + // for classes without parents the skip is in general just the vftable ptr length + // for classes with parents the skip is the entire parent(s) length + if (offset < 0) { + continue; + } + + if (offset >= dataLen) { + continue; + } + + DataType dataType = definedComponent.getDataType(); + if (dataType.getName().equals("undefined") && dataType.getLength() == 1) { + dataType = new Undefined1DataType(); + } + + String fieldName = new String(); + String comment = null; + + // if the computed class struct has field name (ie from pdb) use it otherwise create one + if (definedComponent.getFieldName() == null) { + fieldName = "offset_" + extraUtils.toHexString(offset, false, true); + } + else { + fieldName = definedComponent.getFieldName(); + comment = definedComponent.getComment(); + } + + classDataStructure.replaceAtOffset(offset, dataType, dataType.getLength(), fieldName, + comment); + } + + // only set alignment if all the offsets have been accounted for + if (classDataStructure.getNumComponents() == classDataStructure.getNumDefinedComponents()) { + classDataStructure.setInternallyAligned(true); + } + classDataStructure = (Structure) dataTypeManager.addDataType(classDataStructure, + DataTypeConflictHandler.DEFAULT_HANDLER); + + return classDataStructure; + } + + /** + * Method to use the computed or existing class structure contents for the main class structure. + * This is called when there is not enough information to create a full structure. + * @param computedClassStructure the structure computed using pcode store information or using pdb information + * @param classStructureDataType the structure that is getting created in the data type manager + * @return the default class structure for this class + * @throws CancelledException if cancelled + */ + public Structure createDefaultStructure(Structure computedClassStructure, + Structure classStructureDataType) throws CancelledException { + + DataTypeComponent[] definedComponents = computedClassStructure.getDefinedComponents(); + for (DataTypeComponent component : definedComponents) { + monitor.checkCanceled(); + + classStructureDataType = structUtils.addDataTypeToStructure( + classStructureDataType, component.getOffset(), component.getDataType(), + component.getFieldName(), monitor); + } + classStructureDataType = (Structure) dataTypeManager.addDataType(classStructureDataType, + DataTypeConflictHandler.DEFAULT_HANDLER); + + return classStructureDataType; + } + + /** + * Method to find the purecall function. + * @param recoveredClasses List of RecoveredClass objects + * @throws CancelledException when cancelled + * @throws Exception when issue making label + */ + public void identifyPureVirtualFunction(List recoveredClasses) + throws CancelledException, Exception { + + Function possiblePureCall = null; + + Iterator recoveredClassIterator = recoveredClasses.iterator(); + + while (recoveredClassIterator.hasNext()) { + monitor.checkCanceled(); + + RecoveredClass recoveredClass = recoveredClassIterator.next(); + if (recoveredClass.hasChildClass()) { + Function sameFunction = null; + List deletingDestructors = recoveredClass.getDeletingDestructors(); + List virtualFunctions = recoveredClass.getAllVirtualFunctions(); + if (virtualFunctions.size() < 3) { + continue; + } + Iterator vfunctionIterator = virtualFunctions.iterator(); + while (vfunctionIterator.hasNext()) { + monitor.checkCanceled(); + Function vfunction = vfunctionIterator.next(); + // skip the deleting destructors + if (deletingDestructors.contains(vfunction)) { + continue; + } + if (sameFunction == null) { + sameFunction = vfunction; + } + else if (!sameFunction.equals(vfunction)) { + sameFunction = null; + break; + } + + } + + if (sameFunction == null) { + continue; + } + + // if we didn't already assign it, do it here + if (possiblePureCall == null) { + possiblePureCall = sameFunction; + } + // if they ever don't match return + else if (!possiblePureCall.equals(sameFunction)) { + Msg.debug(this, "Could not identify pure call. "); + return; + } + } + } + + // If we get this far then we are sure the purecall function + // is correct so assign the global variable to it + if (possiblePureCall != null) { + purecall = possiblePureCall; + if (possiblePureCall.getSymbol().getSource() == SourceType.DEFAULT) { + Msg.debug(this, "Found unlabeled purecall function: " + + possiblePureCall.getEntryPoint().toString() + ". Creating label there."); + api.createLabel(possiblePureCall.getEntryPoint(), "purecall", true); + } + } + } + + /** + * Method to remove duplicate functions from the given list + * @param list the given list of functions + * @return the deduped list of functions + * @throws CancelledException if cancelled + */ + public List removeDuplicateFunctions(List list) throws CancelledException { + + List listOfUniqueFunctions = new ArrayList(); + + Iterator listIterator = list.iterator(); + while (listIterator.hasNext()) { + monitor.checkCanceled(); + Function function = listIterator.next(); + if (!listOfUniqueFunctions.contains(function)) { + listOfUniqueFunctions.add(function); + } + } + return listOfUniqueFunctions; + } + + /** + * Method to remove duplicate addresses from the given list + * @param list the given list of functions + * @return the deduped list of functions + * @throws CancelledException if cancelled + */ + public List
removeDuplicateAddresses(List
list) throws CancelledException { + + List
listOfUniqueAddresses = new ArrayList
(); + + Iterator
listIterator = list.iterator(); + while (listIterator.hasNext()) { + monitor.checkCanceled(); + Address address = listIterator.next(); + if (!listOfUniqueAddresses.contains(address)) { + listOfUniqueAddresses.add(address); + } + } + return listOfUniqueAddresses; + } + +} diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/FixUpRttiAnalysisScript.java b/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/FixUpRttiAnalysisScript.java new file mode 100644 index 0000000000..b6598df050 --- /dev/null +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/FixUpRttiAnalysisScript.java @@ -0,0 +1,651 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Script to fix up Windows RTTI vtables and structures +//@category C++ + +import java.util.*; + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.*; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.CircularDependencyException; +import ghidra.program.model.listing.Data; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.symbol.*; +import ghidra.util.exception.*; + +public class FixUpRttiAnalysisScript extends GhidraScript { + + private static final String RTTI_BASE_CLASS_ARRAY_LABEL = "RTTI_Base_Class_Array"; + private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL = + "RTTI_Class_Hierarchy_Descriptor"; + private static final String RTTI_BASE_CLASS_DESCRIPTOR_LABEL = "RTTI_Base_Class_Descriptor"; + private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL = + "RTTI_Complete_Object_Locator"; + private static final String RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME = "RTTIBaseClassDescriptor"; + private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME = + "RTTICompleteObjectLocator"; + private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME = + "RTTIClassHierarchyDescriptor"; + private static final String VFTABLE_META_PTR_LABEL = "vftable_meta_ptr"; + private static final String VFTABLE_LABEL = "vftable"; + + SymbolTable symbolTable = null; + DataTypeManager dataTypeManager = null; + GlobalNamespace globalNamespace = null; + int defaultPointerSize = 0; + boolean isWindows = false; + + @Override + public void run() throws Exception { + + if (currentProgram == null) { + println("There is no open program"); + return; + } + + setIsWindows(); + + if (!isWindows) { + println("This script only handles Windows programs"); + return; + } + + // TODO: check version and only run if before 9.3? + + symbolTable = currentProgram.getSymbolTable(); + dataTypeManager = currentProgram.getDataTypeManager(); + globalNamespace = (GlobalNamespace) currentProgram.getGlobalNamespace(); + + defaultPointerSize = currentProgram.getDefaultPointerSize(); + if (defaultPointerSize != 4 && defaultPointerSize != 8) { + println("This script only works on 32 or 64 bit programs"); + return; + } + + applyMissingRTTIStructures(); + } + + /** + * Method to find and apply missing RTTI structures + * @throws CancelledException if cancelled + * @throws Exception if error applying label or data + */ + private List applyMissingRTTIStructures() throws CancelledException, Exception { + + List completeObjectLocatorSymbols = createMissingRTTICompleteObjectLocator(); + + List baseClassDescriptorSymbols = createMissingBaseClassDescriptors(); + + List
classHierarchyDescriptors = createMissingClassHierarchyDescriptors( + baseClassDescriptorSymbols, completeObjectLocatorSymbols); + + createMissingBaseClassArrays(classHierarchyDescriptors); + + List vftableSymbols = createMissingVftableSymbols(completeObjectLocatorSymbols); + return vftableSymbols; + + } + + /** + * Method to set the global variable isWindows + */ + private void setIsWindows() { + + String compilerID = + currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase(); + isWindows = compilerID.contains("windows"); + } + + /** + * Method to iterate over all symbols with Base Class Descriptor symbol and if + * the correct data type has not already been created, do so. + * @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied + * @throws CancelledException when cancelled + * @throws Exception when data cannot be created + */ + private List createMissingRTTICompleteObjectLocator() + throws CancelledException, Exception { + + List completeObjectLocatorSymbols = new ArrayList(); + + SymbolIterator dataSymbols = + symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true); + + while (dataSymbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = dataSymbols.next(); + if (!symbol.getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL)) { + continue; + } + + Data data = getDataAt(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) { + completeObjectLocatorSymbols.add(symbol); + continue; + } + + // for some reason it was named but not created so create it + data = createCompleteObjectLocator(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) { + completeObjectLocatorSymbols.add(symbol); + continue; + } + + println("Cannot create RTTI_CompleteObjectLocator at " + symbol.getAddress()); + + } + return completeObjectLocatorSymbols; + } + + /** + * Method to create a CompleteObjectLocator structure at the given address + * @param address the address where the structure will be created + * @return the created CompleteObjectLocator data or null if it couldn't be created + * @throws CancelledException if cancelled + * @throws Exception if error creating data + */ + private Data createCompleteObjectLocator(Address address) throws CancelledException, Exception { + + DataType completeObjLocatorDataType = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME); + if (completeObjLocatorDataType == null) { + return null; + } + + int sizeOfDt = completeObjLocatorDataType.getLength(); + + clearListing(address, address.add(sizeOfDt)); + Data completeObjectLocator = createData(address, completeObjLocatorDataType); + if (completeObjectLocator == null) { + return null; + } + return completeObjectLocator; + } + + /** + * Method to iterate over all symbols with Base Class Descriptor symbol and if + * the correct data type has not already been created, do so. + * @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied + * @throws Exception when cancelled + */ + private List createMissingBaseClassDescriptors() throws Exception { + + List baseClassDescriptorSymbols = new ArrayList(); + + SymbolIterator dataSymbols = + symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true); + + while (dataSymbols.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = dataSymbols.next(); + if (!symbol.getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_LABEL)) { + continue; + } + + Data data = getDataAt(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { + baseClassDescriptorSymbols.add(symbol); + continue; + } + + // for some reason it was named but not created so create it + data = createBaseClassDescriptor(symbol.getAddress()); + if (data != null && + data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { + baseClassDescriptorSymbols.add(symbol); + continue; + } + + println("Cannot create RTTI_Base_Class_Descriptor at " + symbol.getAddress()); + + } + return baseClassDescriptorSymbols; + } + + /** + * Method to create a BaseClassDescriptor structure at the given address + * @param baseClassDescriptorAddress the address where the structure will be created + * @return the created BaseClassDescriptor data or null if it couldn't be created + * @throws CancelledException if cancelled + * @throws Exception if error creating data + */ + private Data createBaseClassDescriptor(Address baseClassDescriptorAddress) + throws CancelledException, Exception { + + DataType baseClassDescriptor = + dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME); + + int sizeOfDt = baseClassDescriptor.getLength(); + + clearListing(baseClassDescriptorAddress, baseClassDescriptorAddress.add(sizeOfDt)); + Data baseClassDescArray = createData(baseClassDescriptorAddress, baseClassDescriptor); + if (baseClassDescArray == null) { + return null; + } + return baseClassDescArray; + } + + /** + * Method to apply missing RTTI Base Class Descriptor structures and symbols + * @param address address to apply the missing structure and symbol + * @param numBaseClasses number of base classes in the array pointing to BaseClassDescriptors + * @param classNamespace name of the class + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws MemoryAccessException if cannot access memory + * @throws CancelledException if cancelled + * @throws Exception if issue making data + */ + private void createBaseClassDescriptors(Address address, int numBaseClasses, + Namespace classNamespace) throws CancelledException, MemoryAccessException, + AddressOutOfBoundsException, Exception { + + for (int i = 0; i < numBaseClasses; i++) { + + monitor.checkCanceled(); + + Address baseClassDescriptorAddress = getReferencedAddress(address.add(i * 4)); + + Data baseClassDescriptor = getDataAt(baseClassDescriptorAddress); + if (baseClassDescriptor == null || !baseClassDescriptor.getDataType().getName().equals( + RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { + + int num1 = getInt(baseClassDescriptorAddress.add(8)); + int num2 = getInt(baseClassDescriptorAddress.add(12)); + int num3 = getInt(baseClassDescriptorAddress.add(16)); + int num4 = getInt(baseClassDescriptorAddress.add(20)); + + baseClassDescriptor = createBaseClassDescriptor(baseClassDescriptorAddress); + if (baseClassDescriptor != null) { + symbolTable.createLabel( + baseClassDescriptorAddress, RTTI_BASE_CLASS_DESCRIPTOR_LABEL + "_at_(" + + num1 + "," + num2 + "," + num3 + "," + num4 + ")", + classNamespace, SourceType.ANALYSIS); + } + else { + println( + "Failed to create a baseClassDescArray structure at " + address.toString()); + } + } + } + } + + /** + * + * @param baseClassDescriptors the given list of BaseClassDescriptor symbols + * @param completeObjectLocators the given list of CompleteObjectLocator symbols + * @return list of ClassHierarchyDescriptor addresses + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws InvalidInputException if issue setting return type + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws Exception if there is an issue creating a label + */ + private List
createMissingClassHierarchyDescriptors(List baseClassDescriptors, + List completeObjectLocators) throws CancelledException, MemoryAccessException, + InvalidInputException, AddressOutOfBoundsException, Exception { + + List
classHierarchyDescriptorAddresses = new ArrayList
(); + + Iterator baseClassDescriptorIterator = baseClassDescriptors.iterator(); + while (baseClassDescriptorIterator.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = baseClassDescriptorIterator.next(); + Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor( + symbol.getAddress().add(24), symbol.getParentNamespace()); + + if (classHierarchyDescriptorAddress != null && + !classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) { + classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress); + } + + } + + Iterator completeObjectLocatorIterator = completeObjectLocators.iterator(); + while (completeObjectLocatorIterator.hasNext()) { + monitor.checkCanceled(); + Symbol symbol = completeObjectLocatorIterator.next(); + Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor( + symbol.getAddress().add(16), symbol.getParentNamespace()); + if (classHierarchyDescriptorAddress != null && + !classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) { + classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress); + } + } + + return classHierarchyDescriptorAddresses; + + } + + /** + * + * @param address the address where the ClassHierarchyDescriptor is to be created + * @param classNamespace the namespace of the class + * @return the given class's ClassHierarchyDescriptor address + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws InvalidInputException if issue setting return type + * @throws Exception if issue creating label + */ + private Address createClassHierarchyDescriptor(Address address, Namespace classNamespace) + throws CancelledException, MemoryAccessException, InvalidInputException, Exception { + + Address classHierarchyDescriptorAddress = getReferencedAddress(address); + + Data classHierarchyStructure = getDataAt(classHierarchyDescriptorAddress); + + if (classHierarchyStructure != null && + classHierarchyStructure.getDataType().getName().equals( + RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME)) { + return classHierarchyDescriptorAddress; + + } + + Symbol classHierarchySymbol; + + classHierarchySymbol = symbolTable.createLabel(classHierarchyDescriptorAddress, + RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL, classNamespace, SourceType.ANALYSIS); + + classHierarchyStructure = createClassHierarchyStructure(classHierarchyDescriptorAddress); + + if (classHierarchyStructure == null) { + println("Failed to create a classHierarchyDescriptor structure at " + + classHierarchyDescriptorAddress.toString()); + symbolTable.removeSymbolSpecial(classHierarchySymbol); + return null; + } + return classHierarchyDescriptorAddress; + } + + /** + * Method to create a ClassHierarchyDescriptor structure at the given address + * @param classHierarchyDescriptorAddress the address where the structure will be created + * @return the created ClassHierarchyDescriptor data or null if it couldn't be created + * @throws CancelledException if cancelled + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws Exception if issue creating data + */ + private Data createClassHierarchyStructure(Address classHierarchyDescriptorAddress) + throws CancelledException, AddressOutOfBoundsException, Exception { + + DataType classHDatatype = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME); + int sizeOfDt = classHDatatype.getLength(); + clearListing(classHierarchyDescriptorAddress, + classHierarchyDescriptorAddress.add(sizeOfDt)); + + Data classHierarchyStructure = createData(classHierarchyDescriptorAddress, classHDatatype); + if (classHierarchyStructure == null) { + return null; + } + return classHierarchyStructure; + } + + /** + * + * @param classHierarchyDescriptors the given list of applied ClassHierarchyDescriptor structures + * @return a list of base class array addresses + * @throws CancelledException if cancelled + * @throws MemoryAccessException if memory cannot be read + * @throws AddressOutOfBoundsException if try clear listing at address out of bounds + * @throws Exception if there is an issue creating a label + */ + private List
createMissingBaseClassArrays(List
classHierarchyDescriptors) + throws CancelledException, MemoryAccessException, AddressOutOfBoundsException, + Exception { + + List
baseClassArrayAddresses = new ArrayList
(); + + Iterator
classHierarchyDescriptorIterator = classHierarchyDescriptors.iterator(); + + while (classHierarchyDescriptorIterator.hasNext()) { + + monitor.checkCanceled(); + + Address classHierarchyDescriptorAddress = classHierarchyDescriptorIterator.next(); + Symbol classHierarchyDescriptorSymbol = + symbolTable.getPrimarySymbol(classHierarchyDescriptorAddress); + Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); + + int numBaseClasses = getInt(classHierarchyDescriptorAddress.add(8)); + + Address baseClassArrayAddress = + getReferencedAddress(classHierarchyDescriptorAddress.add(12)); + + Data baseClassDescArray = getDataAt(baseClassArrayAddress); + + if (baseClassDescArray != null && baseClassDescArray.isArray()) { + baseClassArrayAddresses.add(baseClassArrayAddress); + continue; + } + + baseClassDescArray = createBaseClassArray(baseClassArrayAddress, numBaseClasses); + if (baseClassDescArray != null && baseClassDescArray.isArray()) { + Symbol primarySymbol = symbolTable.getPrimarySymbol(baseClassArrayAddress); + if (primarySymbol == null || + !primarySymbol.getName().contains(RTTI_BASE_CLASS_ARRAY_LABEL)) { + + symbolTable.createLabel(baseClassArrayAddress, RTTI_BASE_CLASS_ARRAY_LABEL, + classNamespace, SourceType.ANALYSIS); + } + baseClassArrayAddresses.add(baseClassArrayAddress); + createBaseClassDescriptors(baseClassArrayAddress, numBaseClasses, classNamespace); + continue; + } + + println("Failed to create a baseClassDescArray structure at " + + baseClassArrayAddress.toString()); + } + return baseClassArrayAddresses; + } + + /** + * Method to create a base class array at the given address with the given number of base class's in the array + * @param baseClassArrayAddress the address where the array will be created + * @param numBaseClasses the number of BaseClass's in the array + * @return the created BaseClassArray data or null if cannot retrieve it + * @throws CancelledException if cancelled + * @throws Exception if error creating data + */ + private Data createBaseClassArray(Address baseClassArrayAddress, int numBaseClasses) + throws CancelledException, Exception { + + int sizeOfDt; + ArrayDataType baseClassDescArrayDT; + + int addressSize = baseClassArrayAddress.getSize(); + if (addressSize == 32) { + DataType baseClassDescriptor = dataTypeManager.getDataType(CategoryPath.ROOT, + RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME); + PointerDataType baseClassDescriptorPtr = new PointerDataType(baseClassDescriptor); + sizeOfDt = baseClassDescriptorPtr.getLength(); + + baseClassDescArrayDT = + new ArrayDataType(baseClassDescriptorPtr, numBaseClasses, sizeOfDt); + } + else if (addressSize == 64) { + DataType imageBaseOffset = + dataTypeManager.getDataType(CategoryPath.ROOT, "ImageBaseOffset32"); + sizeOfDt = imageBaseOffset.getLength(); + baseClassDescArrayDT = new ArrayDataType(imageBaseOffset, numBaseClasses, sizeOfDt); + } + else { + return null; + } + + clearListing(baseClassArrayAddress, baseClassArrayAddress.add(numBaseClasses * sizeOfDt)); + Data baseClassDescArray = createData(baseClassArrayAddress, baseClassDescArrayDT); + + if (baseClassDescArray == null) { + return null; + } + return baseClassDescArray; + } + + /** + * Method to create missing vftables and return a list of them + * @param completeObjectLocatorSymbols the list of completeObjectLocatorSymbols + * @return list of vftable symbols + * @throws CancelledException if cancelled + * @throws InvalidInputException if invalid input + * @throws CircularDependencyException if namespace has circular dependency + * @throws DuplicateNameException if try to create label with duplicate name in namespace + */ + private List createMissingVftableSymbols(List completeObjectLocatorSymbols) + throws CancelledException, InvalidInputException, DuplicateNameException, + CircularDependencyException { + + List vftables = new ArrayList(); + + Iterator iterator = completeObjectLocatorSymbols.iterator(); + while (iterator.hasNext()) { + monitor.checkCanceled(); + Symbol completeObjectLocatorSymbol = iterator.next(); + + Address completeObjectLocatorAddress = completeObjectLocatorSymbol.getAddress(); + + Namespace classNamespace = completeObjectLocatorSymbol.getParentNamespace(); + if (classNamespace.equals(globalNamespace)) { + println("no class namespace for " + completeObjectLocatorAddress.toString()); + continue; + } + + Reference[] referencesTo = getReferencesTo(completeObjectLocatorAddress); + if (referencesTo.length == 0) { + println("no refs to " + completeObjectLocatorAddress.toString()); + continue; + } + + 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; + } + + // if not created, create vftable meta pointer label + + if (getGivenSymbol(vftableAddress, VFTABLE_META_PTR_LABEL, + classNamespace) == null) { + + symbolTable.createLabel(vftableMetaPointer, VFTABLE_META_PTR_LABEL, + classNamespace, SourceType.ANALYSIS); + } + + // if not created, create vftable label + Symbol vftableSymbol = + getGivenSymbol(vftableAddress, VFTABLE_LABEL, classNamespace); + if (vftableSymbol == null) { + + vftableSymbol = symbolTable.createLabel(vftableAddress, VFTABLE_LABEL, + classNamespace, SourceType.ANALYSIS); + + if (vftableSymbol == null) { + continue; + } + } + + if (!vftables.contains(vftableSymbol)) { + vftables.add(vftableSymbol); + } + + } + } + return vftables; + } + + /** + * Method to retrieve the symbol with the given address, containing name (containing to account + * for pdb case where sometimes has extra chars) and namespace + * @param address the given address + * @param name the given name + * @param namespace the given namespace + * @return the symbol with the given address, containing name, with given namespace + * @throws CancelledException if cancelled + */ + private Symbol getGivenSymbol(Address address, String name, Namespace namespace) + throws CancelledException { + + Symbol[] symbols = symbolTable.getSymbols(address); + for (Symbol sym : symbols) { + monitor.checkCanceled(); + if (sym.getName().contains(name) && sym.getParentNamespace().equals(namespace)) { + return sym; + } + } + return null; + } + + /** + * Method to return referenced address at the given address + * @param address the address to look for a referenced address at + * @return the first referenced address from the given address + * @throws MemoryAccessException if memory cannot be read + */ + private Address getReferencedAddress(Address address) throws MemoryAccessException { + + //TODO: switch to this then test then just rewrite the call and get rid of this method + // MSDataTypeUtils.getReferencedAddress(currentProgram, address); + // this will work whether there is a created reference or not + int addressSize = address.getSize(); + if (addressSize == 32) { + long offset = getInt(address); + + return address.getNewAddress(offset); + } + + // this currently will workn only if there is a created reference + // TODO: get ibo bytes and figure out what the ibo ref address would be + if (addressSize == 64) { + Reference refs[] = getReferencesFrom(address); + if (refs.length == 0) { + return null; + } + return refs[0].getToAddress(); + } + return null; + } + + /** + * Method to retrieve the AddressSet of the current program's initialized memory + * @return the AddressSet of the current program's initialized memory + * @throws CancelledException if cancelled + */ + private AddressSet getInitializedMemory() throws CancelledException { + + AddressSet dataAddresses = new AddressSet(); + MemoryBlock[] blocks = currentProgram.getMemory().getBlocks(); + + for (MemoryBlock block : blocks) { + monitor.checkCanceled(); + + if (block.isInitialized()) { + dataAddresses.add(block.getStart(), block.getEnd()); + } + } + return dataAddresses; + } + +} diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/IdPeRttiScript.java b/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/IdPeRttiScript.java new file mode 100644 index 0000000000..dc3a7689c2 --- /dev/null +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/IdPeRttiScript.java @@ -0,0 +1,68 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Script to fix up Windows RTTI vtables and structures +//@category C++ + +import ghidra.app.cmd.data.rtti.RttiUtil; +import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.PEUtil; +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.Address; + +public class IdPeRttiScript extends GhidraScript { + + @Override + public void run() throws Exception { + + if (currentProgram == null) { + if (!isRunningHeadless()) { + println("There is no open program."); + return; + } + currentProgram.setTemporary(true); + return; + } + + boolean isPe = PEUtil.isVisualStudioOrClangPe(currentProgram); + if (!isPe) { + if (!isRunningHeadless()) { + println("The current program is not a Visual Studio or Clang PE program."); + return; + } + currentProgram.setTemporary(true); + return; + } + + Address commonVfTableAddress = RttiUtil.findTypeInfoVftableAddress(currentProgram, monitor); + + if (commonVfTableAddress == null) { + if (!isRunningHeadless()) { + println("The current program does not appear to contain RTTI."); + return; + } + currentProgram.setTemporary(true); + return; + } + + if (!isRunningHeadless()) { + println("The current program is a Visual Studio PE or Clang that contains RTTI."); + return; + } + currentProgram.setTemporary(false); + + } + + +} diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/RunRttiAnalyzerScript.java b/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/RunRttiAnalyzerScript.java new file mode 100644 index 0000000000..d834939bd6 --- /dev/null +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/ghidra_scripts/RunRttiAnalyzerScript.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Script to fix up Windows RTTI vtables and structures +//@category C++ + +import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.RttiAnalyzer; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.Analyzer; +import ghidra.app.util.importer.MessageLog; + +public class RunRttiAnalyzerScript extends GhidraScript { + + @Override + public void run() throws Exception { + runRTTIAnalyzer(); + } + + private void runRTTIAnalyzer() throws Exception { + Analyzer analyzer = new RttiAnalyzer(); + analyzer.added(currentProgram, currentProgram.getAddressFactory().getAddressSet(), monitor, + new MessageLog()); + } +}