/* ### * 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. */ //DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. package classrecovery; import java.util.*; import ghidra.app.cmd.label.DemanglerCmd; import ghidra.app.util.NamespaceUtils; import ghidra.app.util.demangler.*; 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.*; import ghidra.program.model.scalar.Scalar; 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.exception.*; import ghidra.util.task.TaskMonitor; public class RTTIGccClassRecoverer extends RTTIClassRecoverer { private static final String VMI_CLASS_TYPE_INFO_STRUCTURE = "VmiClassTypeInfoStructure"; private static final String BASE_CLASS_TYPE_INFO_STRUCTURE = "BaseClassTypeInfoStructure"; private static final String SI_CLASS_TYPE_INFO_STRUCTURE = "SiClassTypeInfoStructure"; private static final String CLASS_TYPE_INFO_STRUCTURE = "ClassTypeInfoStructure"; private static final String VTABLE_LABEL = "vtable"; private static final String CLASS_VTABLE_PTR_FIELD_EXT = "vftablePtr"; private static final int NONE = -1; private static final int UNKNOWN = -2; private static final boolean DEBUG = false; 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 multiAndOrVirtuallyInheritedGccClasses = new ArrayList(); List recoveredClasses = new ArrayList(); private Map> classToParentOrderMap = new HashMap>(); private Map> classToParentOffsetMap = new HashMap>(); boolean isDwarfLoaded; boolean replaceClassStructs; public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, boolean nameVfunctions, boolean isDwarfLoaded, boolean replaceExistingClassStructures, TaskMonitor monitor) throws Exception { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, replaceExistingClassStructures, isDwarfLoaded, monitor); this.isDwarfLoaded = isDwarfLoaded; this.replaceClassStructs = replaceExistingClassStructures; } @Override public boolean containsRTTI() throws CancelledException, InvalidInputException { if (!hasSpecialVtable()) { return false; } return true; } @Override public boolean isValidProgramType() { if (!isGcc()) { return false; } return true; } @Override public List createRecoveredClasses() throws CancelledException, Exception { processGccRTTI(); if (recoveredClasses == null) { Msg.debug(this, "Could not recover gcc rtti classes"); return null; } createCalledFunctionMap(recoveredClasses); createClassHierarchyListAndMapForGcc(); if (isDwarfLoaded) { retrieveExistingClassStructures(recoveredClasses); assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); } else { processConstructorAndDestructors(); } createVftableOrderMap(recoveredClasses); figureOutClassDataMembers(recoveredClasses); createAndApplyClassStructures(); return recoveredClasses; } private boolean isGcc() { boolean isELF = program.getExecutableFormat().contains("ELF"); if (!isELF) { return false; } boolean isCompilerSpecGcc = program.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase("gcc"); if (isCompilerSpecGcc) { return true; } MemoryBlock commentBlock = program.getMemory().getBlock(".comment"); if (commentBlock == null) { return false; } if (!commentBlock.isLoaded()) { return false; } // check memory bytes in block for GCC: bytes byte[] gccBytes = { (byte) 0x47, (byte) 0x43, (byte) 0x43, (byte) 0x3a }; byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; Address found = program.getMemory() .findBytes(commentBlock.getStart(), commentBlock.getEnd(), gccBytes, maskBytes, true, monitor); if (found == null) { return false; } return true; } /** * Method to check for at least one special RTTI vtable * @return true if the program has at least one special vtable, false if none * @throws CancelledException if cancelled * @throws InvalidInputException if bad characters creating labels */ private boolean hasSpecialVtable() throws CancelledException, InvalidInputException { boolean hasSpecialVtable = createSpecialVtables(); return hasSpecialVtable; } 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 void processGccRTTI() throws CancelledException, Exception { // create the appropriate type of type info struct at the various typeinfo symbol locations createTypeinfoStructs(); processVtables(); // process vtables and create classes for the vtables that have no typeinfo List vftableSymbols = findVftablesFromVtables(); recoveredClasses = recoverClassesFromVftables(vftableSymbols, true, true); // find all typeinfo symbols and get their class namespace and create RecoveredClass object List typeinfoSymbols = extendedFlatAPI.getListOfSymbolsInAddressSet( program.getAddressFactory().getAddressSet(), "typeinfo", true); // create class objects for each typeinfo struct and make a class to typeinfo mapping for each createClassesFromTypeinfoSymbols(typeinfoSymbols); updateClassesWithParentsAndFlags(typeinfoSymbols); // update the vftable offset map Iterator recoveredClassIterator = recoveredClasses.iterator(); while (recoveredClassIterator.hasNext()) { monitor.checkCanceled(); RecoveredClass recoveredClass = recoveredClassIterator.next(); List
vftableAddresses = recoveredClass.getVftableAddresses(); 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); } } return; } private void updateClassesWithParentsAndFlags(List typeinfoSymbols) throws Exception { // add properties and parents to each class 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); if (recoveredClass == null) { // this shoudln't be null at this point if (DEBUG) { Msg.debug(this, "***Shouldn't be a null class here: " + classNamespace.getName()); } recoveredClass = createNewClass(classNamespace, false); recoveredClasses.add(recoveredClass); } else { if (!recoveredClasses.contains(recoveredClass)) { recoveredClasses.add(recoveredClass); } } Address specialTypeinfoRef = extendedFlatAPI.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { if (DEBUG) { Msg.debug(this, "No special typeinfo reference found. Cannot process typeinfo struct at " + typeinfoAddress.toString()); } continue; } if (!isSpecialTypeinfo(specialTypeinfoRef)) { // check for EXTERNAL block and look for specialTypeinfoRef there // if fix works, put external block error message and to contact us if (!hasExternalBlock()) { if (DEBUG) { 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; } // use referenced vtable symbol name instead since when in EXTERNAL block // since can't get at the typeinfo ref in that block if (!isSpecialVtable(specialTypeinfoRef)) { if (DEBUG) { 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) || specialTypeinfoRef.equals(class_type_info_vtable)) { recoveredClass.setHasSingleInheritance(true); recoveredClass.setHasMultipleInheritance(false); recoveredClass.setHasMultipleVirtualInheritance(false); recoveredClass.setInheritsVirtualAncestor(false); // no parents so just add empty order and parent maps to the class maps Map orderToParentMap = new HashMap(); classToParentOrderMap.put(recoveredClass, orderToParentMap); Map parentToOffsetMap = new HashMap(); classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); 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) || specialTypeinfoRef.equals(si_class_type_info_vtable)) { RecoveredClass parentClass = getSiClassParent(typeinfoAddress); if (parentClass == null) { throw new Exception("Could not get si parent from typeinfoAddress " + typeinfoAddress.toString()); } if (DEBUG) { Msg.debug(this, recoveredClass.getName() + " adding si parent " + parentClass.getName()); } updateClassWithParent(parentClass, recoveredClass); recoveredClass.setHasSingleInheritance(true); recoveredClass.setHasMultipleInheritance(false); recoveredClass.setHasMultipleVirtualInheritance(false); parentClass.setIsPublicClass(true); recoveredClass.addParentToBaseTypeMapping(parentClass, false); // add order to parent and parent offset Map orderToParentMap = new HashMap(); orderToParentMap.put(0, parentClass); classToParentOrderMap.put(recoveredClass, orderToParentMap); Map parentToOffsetMap = new HashMap(); parentToOffsetMap.put(parentClass, 0L); classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); if (!recoveredClasses.contains(parentClass)) { recoveredClasses.add(parentClass); } continue; } if (specialTypeinfoRef.equals(vmi_class_type_info) || specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { List parents = addGccClassParentsFromVmiStruct(recoveredClass, typeinfoAddress); if (parents.isEmpty()) { continue; } for (RecoveredClass parent : parents) { monitor.checkCanceled(); if (!recoveredClasses.contains(parent)) { recoveredClasses.add(parent); } } } } return; } /** * Method to process the primary vtable for each "vtable" label * @throws Exception if Data cannot be created */ private void processVtables() throws Exception { List listOfVtableSymbols = new ArrayList(); // if dwarf loaded then get vtables using symbols if (!isDwarfLoaded) { listOfVtableSymbols = findVtablesUsingTypeinfoRefs(); } else { listOfVtableSymbols = extendedFlatAPI.getListOfSymbolsInAddressSet( program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); } List copyListOfVtableSymbols = new ArrayList(listOfVtableSymbols); 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, copyListOfVtableSymbols); } return; } private List findVtablesUsingTypeinfoRefs() throws Exception { List vtableSymbols = new ArrayList(); List
typeinfoAddresses = getTypeinfoAddressesUsingSymbols(); if (typeinfoAddresses.isEmpty()) { return vtableSymbols; } // find refs to typeinfo's that are not in functions, instructions, or typeinfo structs // we only want ones that may be in vtables List
typeinfoReferencesNotInTypeinfoStructs = findTypeinfoReferencesNotInTypeinfoStructs(typeinfoAddresses); if (typeinfoReferencesNotInTypeinfoStructs.isEmpty()) { return vtableSymbols; } for (Address typeinfoRef : typeinfoReferencesNotInTypeinfoStructs) { monitor.checkCanceled(); Address typeinfoAddress = extendedFlatAPI.getPointer(typeinfoRef); if (typeinfoAddress == null) { continue; } Structure typeinfoStructure = getTypeinfoStructure(typeinfoAddress); if (typeinfoStructure == null) { continue; } if (!isValidClassInfoStructure(typeinfoStructure)) { continue; } // get top of vtable Address vtableAddress = getPrimaryVtableAddress(typeinfoRef); if (vtableAddress == null) { continue; } // create symbol Symbol typeinfoSymbol = api.getSymbolAt(typeinfoAddress); if (typeinfoSymbol == null) { continue; } if (!typeinfoSymbol.getName().equals("typeinfo")) { continue; } // check for construction table and make new namespace if so Namespace classNamespace = typeinfoSymbol.getParentNamespace(); if (classNamespace.equals(globalNamespace)) { throw new Exception("typeinfo has global namespace " + typeinfoAddress); } Symbol vtableSymbol = symbolTable.createLabel(vtableAddress, VTABLE_LABEL, classNamespace, SourceType.ANALYSIS); vtableSymbols.add(vtableSymbol); api.setPlateComment(vtableAddress, "vtable for " + classNamespace.getName(true)); } return vtableSymbols; } private Address getPrimaryVtableAddress(Address typeinfoRef) throws CancelledException { // check the long just before and if not a zero then continue since the rest // are internal vtables and will get processed when the main one does Address longBeforeTypeinfoRef = getAddress(typeinfoRef, 0 - defaultPointerSize); // if this address doesn't exist then continue since not a valid vtable if (longBeforeTypeinfoRef == null) { return null; } // check for appropriately sized long that is value 0 to make sure the // vtable the typeinfo ref is in is the main one and skip otherwise since non-zero // ones are internal vtables that will get processed with the main one if (!extendedFlatAPI.hasNumZeros(longBeforeTypeinfoRef, defaultPointerSize)) { return null; } Address vtableAddress = longBeforeTypeinfoRef; MemoryBlock currentBlock = program.getMemory().getBlock(typeinfoRef); // stop if top of mem block // stop if bytes are an address // stop if referenced // are they ever zero - not that i have seen so far in the last vftable // if pointer to something or valid address // or is in a structure Address nextAddress = getAddress(vtableAddress, 0 - defaultPointerSize); while (nextAddress != null && program.getMemory().getBlock(nextAddress).equals(currentBlock) && getPointerToDefinedMemory(nextAddress) == null) { vtableAddress = nextAddress; nextAddress = getAddress(vtableAddress, 0 - defaultPointerSize); } return vtableAddress; } private Address getPointerToDefinedMemory(Address address) { Address pointer = extendedFlatAPI.getPointer(address); if (pointer == null) { return null; } if (program.getMemory().getAllInitializedAddressSet().contains(pointer)) { return pointer; } return null; } private boolean isValidClassInfoStructure(Structure typeinfoStructure) { String typeinfoStructureName = typeinfoStructure.getName(); if (typeinfoStructureName.equals(CLASS_TYPE_INFO_STRUCTURE)) { return true; } if (typeinfoStructureName.equals(SI_CLASS_TYPE_INFO_STRUCTURE)) { return true; } if (typeinfoStructureName.contains(VMI_CLASS_TYPE_INFO_STRUCTURE)) { return true; } return false; } private Namespace createConstructionNamespace(Symbol vtableSymbol, Symbol vttSymbol) throws Exception { Namespace vtableNamespace = vtableSymbol.getParentNamespace(); Namespace inNamespace = vttSymbol.getParentNamespace(); String name = vtableNamespace.getName() + "-in-" + inNamespace.getName(true); List namespacesByPath = NamespaceUtils.getNamespaceByPath(program, vtableNamespace, name); if (namespacesByPath.isEmpty()) { Namespace newNamespace = NamespaceUtils.createNamespaceHierarchy(name, vtableNamespace, program, SourceType.ANALYSIS); return newNamespace; } if (namespacesByPath.size() == 1) { return namespacesByPath.get(0); } throw new Exception( "More than one namespace " + vtableNamespace.getName(true) + " " + name); } private Structure getTypeinfoStructure(Address typeinfoAddress) { Data data = api.getDataAt(typeinfoAddress); if (!isTypeinfoStruct(data)) { return null; } return (Structure) data.getBaseDataType(); } public List
findTypeinfoReferencesNotInTypeinfoStructs(List
typeinfoAddresses) throws CancelledException { MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Typeinfo References"); AddressSet searchSet = new AddressSet(); AddressSetView initializedSet = program.getMemory().getAllInitializedAddressSet(); AddressRangeIterator addressRanges = initializedSet.getAddressRanges(); while (addressRanges.hasNext()) { monitor.checkCanceled(); AddressRange addressRange = addressRanges.next(); searchSet.add(addressRange.getMinAddress(), addressRange.getMaxAddress()); } List
validTypeinfoRefs = new ArrayList
(); Iterator
typeinfoIterator = typeinfoAddresses.iterator(); while (typeinfoIterator.hasNext()) { monitor.checkCanceled(); Address typeinfoAddress = typeinfoIterator.next(); // check direct refs to see if they are in undefined area or not in function byte[] bytes = ProgramMemoryUtil.getDirectAddressBytes(program, typeinfoAddress); addByteSearchPattern(searcher, validTypeinfoRefs, typeinfoAddress, bytes, monitor); } searcher.search(program, searchSet, monitor); return validTypeinfoRefs; } /** * Method to add a search pattern, to the searcher, for the set of bytes representing a typeinfo * address * @param searcher the MemoryBytePatternSearcher * @param typeinfoRefs a list typeinfo reference addresses that are not contained * in a function, instruction, or a typeinfo structure * @param typeinfoAddress the given typeinfo address * @param bytes the bytes to search for * @param taskMonitor a cancellable monitor */ private void addByteSearchPattern(MemoryBytePatternSearcher searcher, List
typeinfoRefs, Address typeinfoAddress, byte[] bytes, TaskMonitor taskMonitor) { // no pattern bytes. if (bytes == null) { return; } // Each time a match for this byte pattern ... GenericMatchAction
action = new GenericMatchAction
(typeinfoAddress) { @Override public void apply(Program prog, Address addr, Match match) { Function functionContainingTypeinfoRef = prog.getListing().getFunctionContaining(addr); Data dataContainingTypeinfoRef = 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 (functionContainingTypeinfoRef == null && instructionContainingAddr == null && dataContainingTypeinfoRef == null) { typeinfoRefs.add(addr); } else if (dataContainingTypeinfoRef != null && !isTypeinfoStruct(dataContainingTypeinfoRef)) { typeinfoRefs.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 determine if the given data is a typeinfo structure * @param data the given data * @return true if the given data is a typeinfo structure, else return false */ private boolean isTypeinfoStruct(Data data) { if (data == null) { return false; } DataType baseDataType = data.getBaseDataType(); if (!(baseDataType instanceof Structure)) { return false; } Structure structure = (Structure) baseDataType; if (structure.getName().contains(CLASS_TYPE_INFO_STRUCTURE)) { return true; } return false; } /** * Method to create an appropriate type of vtable (primary, internal, or construction) and * an associated VTT, if applicable * @param vtableAddress the given vtable address * @param vtableNamespace the namespace of the given vtable * @param isPrimary true if the vtable is the primary one for the class * @param listOfAllVtables list of all vtables * @throws CancelledException if cancelled */ private void processVtable(Address vtableAddress, Namespace vtableNamespace, boolean isPrimary, List listOfAllVtables) throws CancelledException, Exception { // 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) { if (DEBUG) { 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); api.createData(typeinfoAddress, nullPointer); } // if not already named a construction-vtable then check to see if it is one so it can // be renamed and the new namespace figured out // know it isn't null because the of the vtable symbol iterator used to call this method in the first place Symbol vtableSymbol = symbolTable.getPrimarySymbol(vtableAddress); if (!vtableSymbol.getName().equals("construction-vtable") && listOfAllVtables != null) { // get first VTT before this vtable Symbol vttSymbolBeforeConstructionVtable = getVTTBefore(vtableSymbol.getAddress()); if (vttSymbolBeforeConstructionVtable != null) { List
subVTTs = getSubVTTs(vttSymbolBeforeConstructionVtable.getAddress()); if (!subVTTs.isEmpty()) { int n = 0; for (Address subVTTAddress : subVTTs) { monitor.checkCanceled(); n++; Symbol constructionVtableSymbol = getNthSymbolOnListAfterAddress( vttSymbolBeforeConstructionVtable.getAddress(), listOfAllVtables, n); if (constructionVtableSymbol.equals(vtableSymbol)) { // change the namespace and name of the vtable Namespace classNamespace = createConstructionNamespace(vtableSymbol, vttSymbolBeforeConstructionVtable); vtableSymbol.setNameAndNamespace("construction-vtable", classNamespace, SourceType.ANALYSIS); vtableNamespace = vtableSymbol.getParentNamespace(); // label the subVTTaddress symbolTable.createLabel(subVTTAddress, "subVTT_" + n, vttSymbolBeforeConstructionVtable.getParentNamespace(), SourceType.ANALYSIS); api.setPlateComment(vtableAddress, "construction vtable " + n + " for class " + vttSymbolBeforeConstructionVtable.getParentNamespace() .getName(true)); } } } } } // create longs from top of vtable to the typeinfoAddress createLongs(vtableAddress, typeinfoAddress); Address possibleVftableAddress = getAddress(typeinfoAddress, defaultPointerSize); if (possibleVftableAddress == null) { return; } int numFunctionPointers = getNumFunctionPointers(possibleVftableAddress, true, true); if (numFunctionPointers == 0) { // if not a vftable check for an internal vtable boolean isInternalVtable = createInternalVtable(possibleVftableAddress, vtableNamespace); if (isInternalVtable) { return; } // if not an internal vtable check for VTT table boolean isVTT = createVTT(vtableNamespace, possibleVftableAddress); if (isVTT) { return; } return; } // if at least one function pointer make vftable label - the createVftable method will // create the table late String vftableLabel = VFTABLE_LABEL; if (!isPrimary) { vftableLabel = "internal_" + vftableLabel; } symbolTable.createLabel(possibleVftableAddress, vftableLabel, vtableNamespace, SourceType.ANALYSIS); createVftableArray(possibleVftableAddress, numFunctionPointers); // check for an internal vtable after the vftable and make a symbol there if there is one // will process them later Address possibleInternalVtableAddress = getAddress(possibleVftableAddress, defaultPointerSize * numFunctionPointers); // if there is no symbol or a non-default symbol then the nextAddress is an internal // vtable if (possibleInternalVtableAddress == null) { return; } // check to see if it is an internal vtable boolean isInternalVtable = createInternalVtable(possibleInternalVtableAddress, vtableNamespace); if (isInternalVtable) { return; } // otherwise check to see if it is a VTT table and create it if so boolean isVTT = createVTT(vtableNamespace, possibleInternalVtableAddress); if (isVTT) { return; } } private Symbol getVTTBefore(Address address) throws CancelledException { // get all symbols named VTT and get the one directly before the given address List vttSymbols = extendedFlatAPI.getListOfSymbolsInAddressSet( program.getAddressFactory().getAddressSet(), "VTT", true); return getSymbolOnListBeforeAddress(address, vttSymbols); } private List
getSubVTTs(Address vttAddress) { // keep getting next code unit and continue while in the VTT (check for pointers) // if there is a reference inside the vtt then count it - it is a subVTT int offset = 0; List
subVtts = new ArrayList
(); Address currentAddress = vttAddress; while (currentAddress != null && getPointerToDefinedMemory(currentAddress) != null) { if (offset > 0) { Reference[] referencesTo = api.getReferencesTo(currentAddress); if (referencesTo.length > 0) { subVtts.add(currentAddress); } } offset++; currentAddress = getAddress(vttAddress, defaultPointerSize * offset); } return subVtts; } /* * Method to get the address on list that is the first that comes after the given address */ private Symbol getSymbolOnListBeforeAddress(Address givenAddress, List listOfSymbols) throws CancelledException { if (listOfSymbols.isEmpty()) { return null; } Symbol symbolBefore = null; listOfSymbols.sort((a1, a2) -> a1.getAddress().compareTo(a2.getAddress())); for (Symbol symbol : listOfSymbols) { monitor.checkCanceled(); if (symbol.getAddress().getOffset() >= givenAddress.getOffset()) { return symbolBefore; } if (symbolBefore == null) { symbolBefore = symbol; continue; } if (symbol.getAddress().getOffset() > symbolBefore.getAddress().getOffset()) { symbolBefore = symbol; } } return symbolBefore; } private Symbol getNthSymbolOnListAfterAddress(Address givenAddress, List listOfSymbols, int n) throws CancelledException { if (listOfSymbols.isEmpty()) { return null; } int numSymbolsAfter = 0; listOfSymbols.sort((a1, a2) -> a1.getAddress().compareTo(a2.getAddress())); for (Symbol symbol : listOfSymbols) { monitor.checkCanceled(); if (symbol.getAddress().getOffset() > givenAddress.getOffset()) { numSymbolsAfter++; if (numSymbolsAfter == n) { return symbol; } } } return null; } private boolean createInternalVtable(Address possibleInternalVtableAddress, Namespace vtableNamespace) throws CancelledException, InvalidInputException, Exception { // check to see if it is a pointer and if so, it cannot be an internal vtable // as they contain at least one long Address pointer = getPointerToDefinedMemory(possibleInternalVtableAddress); if (pointer != null) { return false; } Symbol possibleInternalVtableSymbol = symbolTable.getPrimarySymbol(possibleInternalVtableAddress); if (possibleInternalVtableSymbol != null && possibleInternalVtableSymbol.getSource() != SourceType.DEFAULT && (!possibleInternalVtableSymbol.getParentNamespace().equals(vtableNamespace) || !possibleInternalVtableSymbol.getName().contains("vtable"))) { return false; } if (possibleInternalVtableSymbol == null || (possibleInternalVtableSymbol.getSource() == SourceType.DEFAULT && (isValidVtableStart(possibleInternalVtableAddress) || isValidVftableStart(possibleInternalVtableAddress)))) { symbolTable.createLabel(possibleInternalVtableAddress, "internal_vtable_" + possibleInternalVtableAddress.toString(), vtableNamespace, SourceType.ANALYSIS); processVtable(possibleInternalVtableAddress, vtableNamespace, false, null); return true; } return false; } /** * Method to create a VTT table label at the given address if it is deemed a valid VTT * @param classNamespace the given namespace * @param address the address of the potential VTT table * @return true if a valid VTT has been discovered and label created * @throws Exception if data creation results in an exception */ private boolean createVTT(Namespace classNamespace, Address address) throws Exception { // get pointer at address Address pointer = getPointerToDefinedMemory(address); if (pointer == null) { return false; } // check to see if pointer is to the class vftable or to a class internal vtable or to itself // if not one of those things it isn't a VTT Symbol symbol = symbolTable.getPrimarySymbol(pointer); if ((!symbol.getName().equals(VFTABLE_LABEL) || !symbol.getName().contains("internal_vtable")) && !symbol.getParentNamespace().equals(classNamespace) && !pointer.equals(address)) { return false; } // if it is then create the VTT symbol and create pointer there symbolTable.createLabel(address, "VTT", classNamespace, SourceType.ANALYSIS); DataType nullPointer = dataTypeManager.getPointer(null); try { api.createData(pointer, nullPointer); } catch (Exception e) { // already data there so don't try and overwrite it } api.setPlateComment(address, "VTT for " + classNamespace.getName(true)); return true; } private Data createVftableArray(Address vftableAddress, int numFunctionPointers) throws Exception { api.clearListing(vftableAddress, vftableAddress.add((numFunctionPointers * defaultPointerSize - 1))); DataType pointerDataType = dataTypeManager.getPointer(null); ArrayDataType vftableArrayDataType = new ArrayDataType(pointerDataType, numFunctionPointers, defaultPointerSize); Data vftableArrayData = api.createData(vftableAddress, vftableArrayDataType); return vftableArrayData; } /** * Method to check for a valid vtable at the given address * @param vtableAddress the given address * @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; } 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 = extendedFlatAPI.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 = extendedFlatAPI.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 (extendedFlatAPI.hasNumZeros(vftableAddress, defaultPointerSize)) { return true; } Data data = api.getDataAt(vftableAddress); if (data != null) { if (!data.isPointer()) { return false; } Address referencedAddress = extendedFlatAPI.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 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 * @throws InvalidInputException if bad characters when creating label */ private Address createSpecialVtable(Address vtableAddress) throws CancelledException, InvalidInputException { Symbol vtableSymbol = symbolTable.getPrimarySymbol(vtableAddress); api.clearListing(vtableAddress); 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; } /** * 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.getPrimarySymbol(address) != null) { 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 = extendedFlatAPI.getSingleReferencedAddress(address); // if it isn't a valid pointer, 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; } } // if bump into existing data return the number found so far catch (Exception e) { return numLongs; } } } /** * Method to create and apply typeinfo structs of one of the three types used by rtti classes * @throws CancelledException if cancelled * @throws Exception if could not apply a type info structure */ private void createTypeinfoStructs() throws CancelledException, Exception { StructureDataType classTypeInfoStructure = createClassTypeInfoStructure(); StructureDataType siClassTypeInfoStructure = createSiClassTypeInfoStructure(classTypeInfoStructure); StructureDataType baseClassTypeInfoStructure = createBaseClassTypeInfoStructure(classTypeInfoStructure); List
typeinfoAddresses; // if dwarf get using symbols if (isDwarfLoaded) { typeinfoAddresses = getTypeinfoAddressesUsingSymbols(); } else { // if not, get using ref to specials if (hasExternalRelocationRefs()) { typeinfoAddresses = getTypeinfoAddressesUsingRelocationTable(); } else { typeinfoAddresses = getTypeinfoAddressesUsingSpecialTypeinfos(); } } if (typeinfoAddresses.isEmpty()) { return; } for (Address typeinfoAddress : typeinfoAddresses) { Address specialTypeinfoRef = extendedFlatAPI.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { continue; } if (!isSpecialTypeinfo(specialTypeinfoRef)) { // check for EXTERNAL block and look for specialTypeinfoRef there // if fix works, put external block error message and to contact us if (!hasExternalBlock()) { continue; } // use referenced vtable symbol name instead since when in EXTERNAL block // since can't get at the typeinfo ref in that block if (!isSpecialVtable(specialTypeinfoRef)) { continue; } } Data newStructure = null; // create a "no inheritance" struct here if (specialTypeinfoRef.equals(class_type_info) || specialTypeinfoRef.equals(class_type_info_vtable)) { newStructure = applyTypeinfoStructure(classTypeInfoStructure, typeinfoAddress); } // create a "single inheritance" struct here else if (specialTypeinfoRef.equals(si_class_type_info) || specialTypeinfoRef.equals(si_class_type_info_vtable)) { newStructure = applyTypeinfoStructure(siClassTypeInfoStructure, typeinfoAddress); } // create a "virtual multip inheritance" struct here else if (specialTypeinfoRef.equals(vmi_class_type_info) || specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { Structure vmiClassTypeinfoStructure = getOrCreateVmiTypeinfoStructure(typeinfoAddress, baseClassTypeInfoStructure); if (vmiClassTypeinfoStructure != null) { newStructure = applyTypeinfoStructure(vmiClassTypeinfoStructure, typeinfoAddress); } } if (newStructure == null) { throw new Exception( "ERROR: Could not apply typeinfo structure to " + typeinfoAddress); } // check for existing symbol and if none, demangle the name and apply Symbol typeinfoSymbol = api.getSymbolAt(typeinfoAddress); if (typeinfoSymbol == null || typeinfoSymbol.getSource() == SourceType.DEFAULT) { typeinfoSymbol = createDemangledTypeinfoSymbol(typeinfoAddress); if (typeinfoSymbol == null) { Msg.debug(this, "Could not create demangled typeinfo symbol at " + typeinfoAddress.toString()); } } if (typeinfoSymbol != null && typeinfoSymbol.getName().equals("typeinfo")) { promoteToClassNamespace(typeinfoSymbol.getParentNamespace()); continue; } } } private Data applyTypeinfoStructure(Structure typeInfoStructure, Address typeinfoAddress) throws CancelledException, AddressOutOfBoundsException, Exception { api.clearListing(typeinfoAddress, typeinfoAddress.add(typeInfoStructure.getLength() - 1)); Data newStructure; newStructure = api.createData(typeinfoAddress, typeInfoStructure); return newStructure; } private Structure getOrCreateVmiTypeinfoStructure(Address typeinfoAddress, StructureDataType baseClassTypeInfoStructure) { // get num base classes int offsetOfNumBases = 2 * defaultPointerSize + 4; int numBases; try { numBases = api.getInt(typeinfoAddress.add(offsetOfNumBases)); } // if there isn't enough memory to get the int then return null catch (MemoryAccessException | AddressOutOfBoundsException e) { return null; } // get or create the vmiClassTypeInfoStruct Structure vmiClassTypeinfoStructure = (Structure) dataTypeManager .getDataType(classDataTypesCategoryPath, VMI_CLASS_TYPE_INFO_STRUCTURE + numBases); if (vmiClassTypeinfoStructure == null) { vmiClassTypeinfoStructure = createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); } return vmiClassTypeinfoStructure; } private Symbol createDemangledTypeinfoSymbol(Address typeinfoAddress) throws DuplicateNameException, InvalidInputException { String mangledTypeinfo = getTypeinfoName(typeinfoAddress); if (mangledTypeinfo == null) { Msg.debug(this, "Could not get typeinfo string from " + typeinfoAddress.toString()); return null; } if (mangledTypeinfo.startsWith("*")) { mangledTypeinfo = mangledTypeinfo.substring(1); } mangledTypeinfo = "_Z" + mangledTypeinfo; DemanglerOptions options = new DemanglerOptions(); options.setDemangleOnlyKnownPatterns(false); options.setApplySignature(false); options.setDoDisassembly(false); DemangledObject demangled = DemanglerUtil.demangle(mangledTypeinfo); if (demangled == null) { Msg.debug(this, "Could not demangle typeinfo string at " + typeinfoAddress.toString()); return null; } String namespaceString = demangled.getNamespaceString(); Namespace classNamespace = createTypeinfoClassNamespace(namespaceString); Msg.debug(this, typeinfoAddress.toString() + " " + namespaceString); if (classNamespace == null) { Msg.debug(this, typeinfoAddress.toString() + "Could not create a class namespace for demangled namespace string " + namespaceString); return null; } // create the new typeinfo symbol in the demangled namespace Symbol newSymbol = symbolTable.createLabel(typeinfoAddress, "typeinfo", classNamespace, SourceType.ANALYSIS); return newSymbol; } private Namespace createTypeinfoClassNamespace(String namespaceString) throws DuplicateNameException, InvalidInputException { int indexOfColons = namespaceString.indexOf("::"); Namespace namespace = globalNamespace; while (indexOfColons != -1) { String namespaceName = namespaceString.substring(0, indexOfColons); Namespace newNamespace = getOrCreateNamespace(namespaceName, namespace); if (newNamespace == null) { return null; } namespace = newNamespace; namespaceString = namespaceString.substring(indexOfColons + 2); indexOfColons = namespaceString.indexOf("::"); } // the substring after the last :: is the class namespace we want to return Namespace classNamespace = getOrCreateNamespace(namespaceString, namespace); if (classNamespace == null) { return null; } if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { classNamespace = promoteToClassNamespace(classNamespace); } return classNamespace; } private Namespace getOrCreateNamespace(String namespaceName, Namespace parentNamespace) throws DuplicateNameException, InvalidInputException { Namespace namespace = symbolTable.getNamespace(namespaceName, parentNamespace); if (namespace == null) { namespace = symbolTable.createNameSpace(parentNamespace, namespaceName, SourceType.ANALYSIS); } return namespace; } private String getTypeinfoName(Address address) { Data dataAt = api.getDataAt(address); if (dataAt == null) { return null; } if (!(dataAt.getBaseDataType() instanceof Structure)) { return null; } Structure typeinfoStructure = (Structure) dataAt.getBaseDataType(); if (!typeinfoStructure.getName().contains(CLASS_TYPE_INFO_STRUCTURE)) { return null; } DataTypeComponent typeinfoNameComponent = typeinfoStructure.getComponent(1); DataType typeinfoNameDatatype = typeinfoNameComponent.getDataType(); if (!(typeinfoNameDatatype instanceof Pointer)) { return null; } Address stringReference = extendedFlatAPI.getSingleReferencedAddress(address.add(typeinfoNameComponent.getOffset())); Data stringData = api.getDataAt(stringReference); if (stringData == null) { return null; } int stringLen = stringData.getLength(); MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), stringReference); StringDataType sdt = new StringDataType(); String str; str = (String) sdt.getValue(buf, sdt.getDefaultSettings(), stringLen); return str; } /** * Method to get a list typeinfo addresses using symbols * @return a list of non-special typeinfo addresses that have "typeinfo" symbols * @throws CancelledException if cancelled */ private List
getTypeinfoAddressesUsingSymbols() throws CancelledException { List
typeinfoAddresses = new ArrayList
(); List typeinfoSymbols = extendedFlatAPI.getListOfSymbolsInAddressSet( program.getAddressFactory().getAddressSet(), "typeinfo", true); 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; } // check for EXTERNAL block and look for specialTypeinfoRef there // if fix works, put external block error message and to contact us if (hasExternalBlock() && isSpecialVtable(typeinfoAddress)) { continue; } typeinfoAddresses.add(typeinfoAddress); } return typeinfoAddresses; } /** * Method to get a list typeinfo addresses using relocation table info * @return a list of typeinfo addresses * @throws CancelledException if cancelled */ private List
getTypeinfoAddressesUsingRelocationTable() throws CancelledException { List
typeinfoAddresses = new ArrayList
(); Iterator bookmarksIterator = program.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR); while (bookmarksIterator.hasNext()) { monitor.checkCanceled(); Bookmark bookmark = bookmarksIterator.next(); if (bookmark.getCategory().equals("EXTERNAL Relocation") && bookmarkContainsSpecialTypeinfoName(bookmark.getComment())) { typeinfoAddresses.add(bookmark.getAddress()); } } return typeinfoAddresses; } private boolean bookmarkContainsSpecialTypeinfoName(String bookmarkComment) { if (bookmarkComment.contains("class_type_info")) { return true; } if (bookmarkComment.contains("si_class_type_info")) { return true; } if (bookmarkComment.contains("vmi_class_type_info")) { return true; } return false; } /** * Method to check to see if there are any EXTERNAL block relocations * @return true if there are any EXTERNAL block relocations in the program, false otherwise * @throws CancelledException if cancelled */ private boolean hasExternalRelocationRefs() throws CancelledException { // if no external block then there won't be any refernces to special typeinfos in the external // block so return empty list if (!hasExternalBlock()) { return false; } Iterator bookmarksIterator = program.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR); while (bookmarksIterator.hasNext()) { monitor.checkCanceled(); Bookmark bookmark = bookmarksIterator.next(); if (bookmark.getCategory().equals("EXTERNAL Relocation")) { return true; } } return false; } /** * Get the references to the special type infos that exist in the current program. * @return the references to the special type infos that exist in the current program * @throws CancelledException if cancelled */ private List
getTypeinfoAddressesUsingSpecialTypeinfos() throws CancelledException { List
specialTypeinfoRefs = new ArrayList
(); if (class_type_info != null) { Reference[] refsToClassTypeinfo = api.getReferencesTo(class_type_info); for (Reference ref : refsToClassTypeinfo) { monitor.checkCanceled(); specialTypeinfoRefs.add(ref.getFromAddress()); } } if (si_class_type_info != null) { Reference[] refsToSiClassTypeinfo = api.getReferencesTo(si_class_type_info); for (Reference ref : refsToSiClassTypeinfo) { monitor.checkCanceled(); specialTypeinfoRefs.add(ref.getFromAddress()); } } if (vmi_class_type_info != null) { Reference[] refsToVmiClassTypeinfo = api.getReferencesTo(vmi_class_type_info); for (Reference ref : refsToVmiClassTypeinfo) { monitor.checkCanceled(); specialTypeinfoRefs.add(ref.getFromAddress()); } } return specialTypeinfoRefs; } /** * 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 * @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() 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); } private StructureDataType createClassTypeInfoStructure() { StructureDataType classTypeInfoStructure = new StructureDataType(classDataTypesCategoryPath, CLASS_TYPE_INFO_STRUCTURE, 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.setPackingEnabled(true); return classTypeInfoStructure; } private StructureDataType createSiClassTypeInfoStructure( StructureDataType classTypeInfoStructure) { StructureDataType siClassTypeInfoStructure = new StructureDataType( classDataTypesCategoryPath, SI_CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager); CharDataType characterDT = new CharDataType(); DataType pointer = dataTypeManager.getPointer(null); DataType charPointer = dataTypeManager.getPointer(characterDT); siClassTypeInfoStructure.add(pointer, "classTypeinfoPtr", null); siClassTypeInfoStructure.add(charPointer, "typeinfoName", null); DataType pointerToClassTypeInfoStruct = dataTypeManager.getPointer(classTypeInfoStructure); siClassTypeInfoStructure.add(pointerToClassTypeInfoStruct, "baseClassTypeInfoPtr", null); siClassTypeInfoStructure.setPackingEnabled(true); return siClassTypeInfoStructure; } private StructureDataType createBaseClassTypeInfoStructure( StructureDataType classTypeInfoStructure) throws InvalidDataTypeException { StructureDataType baseclassTypeInfoStructure = new StructureDataType( classDataTypesCategoryPath, BASE_CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager); DataType classTypeInfoPointer = dataTypeManager.getPointer(classTypeInfoStructure); int offsetBitSize = 24; if (defaultPointerSize == 8) { offsetBitSize = 56; } baseclassTypeInfoStructure.add(classTypeInfoPointer, "classTypeinfoPtr", null); if (program.getMemory().isBigEndian()) { baseclassTypeInfoStructure.addBitField(LongDataType.dataType, offsetBitSize, "baseClassOffset", null); baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isPublicBase", null); baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isVirtualBase", null); baseclassTypeInfoStructure.addBitField(ByteDataType.dataType, 6, "unused", null); } else { baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isVirtualBase", null); baseclassTypeInfoStructure.addBitField(BooleanDataType.dataType, 1, "isPublicBase", null); baseclassTypeInfoStructure.addBitField(ByteDataType.dataType, 6, "unused", null); baseclassTypeInfoStructure.addBitField(LongDataType.dataType, offsetBitSize, "baseClassOffset", null); } baseclassTypeInfoStructure.setPackingEnabled(true); return baseclassTypeInfoStructure; } private StructureDataType createVmiClassTypeInfoStructure( StructureDataType baseClassTypeInfoStructure, int numBaseClasses) { StructureDataType vmiClassTypeInfoStructure = new StructureDataType(classDataTypesCategoryPath, VMI_CLASS_TYPE_INFO_STRUCTURE + numBaseClasses, 0, dataTypeManager); CharDataType characterDT = new CharDataType(); UnsignedIntegerDataType unsignedIntDT = new UnsignedIntegerDataType(); DataType pointer = dataTypeManager.getPointer(null); DataType charPointer = dataTypeManager.getPointer(characterDT); 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.setPackingEnabled(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 Exception if cannot access the given typeinfo structure, one of its components, or it is not a vmi structure */ private List addGccClassParentsFromVmiStruct(RecoveredClass recoveredClass, Address typeinfoAddress) throws Exception { Structure vmiTypeinfoStructure = getTypeinfoStructure(typeinfoAddress); if (vmiTypeinfoStructure == null || !vmiTypeinfoStructure.getName().contains(VMI_CLASS_TYPE_INFO_STRUCTURE)) { throw new Exception( "Could not get vmi base typeinfo structure at address " + typeinfoAddress); } // process the inheritance flag DataTypeComponent inheritanceFlagComponent = vmiTypeinfoStructure.getComponent(2); int flagOffset = inheritanceFlagComponent.getOffset(); DataType inheritanceFlagDataType = inheritanceFlagComponent.getDataType(); MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), getAddress(typeinfoAddress, flagOffset)); Scalar scalar = (Scalar) inheritanceFlagDataType.getValue(buf, inheritanceFlagDataType.getDefaultSettings(), inheritanceFlagDataType.getLength()); long inheritanceFlagValue = scalar.getUnsignedValue(); // 0x01: class has non-diamond repeated inheritance // 0x02: class is diamond shaped // add flag for non-diamond repeated and diamond shape types if (inheritanceFlagValue == 1) { if (DEBUG) { Msg.debug(this, "from typeinfo at address " + typeinfoAddress.toString() + " " + recoveredClass.getClassNamespace().getName(true) + " has non-diamond repeated inheritance"); } } if (inheritanceFlagValue == 2) { recoveredClass.setIsDiamondShaped(true); } // process the base classes // create parent maps Map orderToParentMap = new HashMap(); Map parentToOffsetMap = new HashMap(); DataTypeComponent numBaseClassesComponent = vmiTypeinfoStructure.getComponent(3); int numBaseClassesOffset = numBaseClassesComponent.getOffset(); DataType numBaseClassesDataType = numBaseClassesComponent.getDataType(); buf = new DumbMemBufferImpl(program.getMemory(), getAddress(typeinfoAddress, numBaseClassesOffset)); scalar = (Scalar) numBaseClassesDataType.getValue(buf, numBaseClassesDataType.getDefaultSettings(), numBaseClassesDataType.getLength()); int numBaseClasses = (int) scalar.getUnsignedValue(); if (numBaseClasses > 1) { recoveredClass.setHasMultipleInheritance(true); recoveredClass.setHasSingleInheritance(false); } else { recoveredClass.setHasMultipleInheritance(false); recoveredClass.setHasSingleInheritance(true); } // process the base class array DataTypeComponent baseClassArrayComponent = vmiTypeinfoStructure.getComponent(4); if (baseClassArrayComponent == null) { throw new Exception( "Could not get base class array in vmi structure at " + typeinfoAddress.toString()); } int baseClassArrayOffset = baseClassArrayComponent.getOffset(); List parentClassList = new ArrayList(); int numParents = numBaseClasses; for (int i = 0; i < numParents; i++) { // get parent from pointer to parent typeinfo Address parentRefAddress = getAddress(typeinfoAddress, baseClassArrayOffset + (i * 2 * defaultPointerSize)); RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentRefAddress); if (parentClass == null) { throw new Exception("Could not get parent class number " + (i + 1) + " from typeinfo struct at " + typeinfoAddress.toString()); } if (DEBUG) { Msg.debug(this, recoveredClass.getName() + " adding vmi parent " + parentClass.getName()); } updateClassWithParent(parentClass, recoveredClass); parentClassList.add(parentClass); LongDataType longDT = new LongDataType(); // get public/virtual/offset flag Address flagAddress = getAddress(typeinfoAddress, baseClassArrayOffset + (i * 2 * defaultPointerSize + defaultPointerSize)); buf = new DumbMemBufferImpl(program.getMemory(), flagAddress); Scalar value = (Scalar) longDT.getValue(buf, longDT.getDefaultSettings(), defaultPointerSize); long publicVirtualOffsetFlag = value.getSignedValue(); //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 boolean isVirtual = false; boolean isPublic = false; long virtualMask = 0x1L; long publicMask = 0x2L; long offsetMask; if (defaultPointerSize == 4) { offsetMask = 0xffffff00L; } else { offsetMask = 0xffffffffffffff00L; } if ((publicVirtualOffsetFlag & virtualMask) == 1) { isVirtual = true; } if (recoveredClass.hasMultipleInheritance()) { recoveredClass.setHasMultipleVirtualInheritance(isVirtual); } recoveredClass.addParentToBaseTypeMapping(parentClass, isVirtual); recoveredClass.setInheritsVirtualAncestor(isVirtual); if (((publicVirtualOffsetFlag & publicMask) >> 1) == 1) { isPublic = true; } parentClass.setIsPublicClass(isPublic); // 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). long offset = (publicVirtualOffsetFlag & offsetMask) >> 8; if (DEBUG) { Msg.debug(this, "typeinfo " + typeinfoAddress + " base [" + i + "] isVirtual = " + isVirtual + " isPublic = " + isPublic + " offset = " + offset); } // add order to parent and parent offset orderToParentMap.put(i, parentClass); parentToOffsetMap.put(parentClass, offset); continue; } if (DEBUG) { Msg.debug(this, recoveredClass.getName() + " has " + numParents + " parents"); } classToParentOrderMap.put(recoveredClass, orderToParentMap); classToParentOffsetMap.put(recoveredClass, parentToOffsetMap); 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 * @throws Exception if cannot access parent's type info reference address or if could not get * the parent class */ private RecoveredClass getSiClassParent(Address typeinfoAddress) throws Exception { int offset = defaultPointerSize * 2; Address parentTypeinfoRef = getAddress(typeinfoAddress, offset); if (parentTypeinfoRef == null) { throw new Exception("Could not access address " + typeinfoAddress.toString() + " plus offset " + offset); } 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 = extendedFlatAPI.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 * @throws InvalidInputException if bad characters creating labels */ private boolean createSpecialVtables() throws CancelledException, InvalidInputException { 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 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 = extendedFlatAPI.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 = extendedFlatAPI.getPointer(address); if (possibleTypeinfo == null) { offset += defaultPointerSize; address = extendedFlatAPI.getAddress(startAddress, offset); continue; } Symbol possibleTypeinfoSymbol = symbolTable.getPrimarySymbol(possibleTypeinfo); if (possibleTypeinfoSymbol != null && possibleTypeinfoSymbol.getName().equals("typeinfo")) { return address; } offset += defaultPointerSize; address = extendedFlatAPI.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 = extendedFlatAPI.getListOfSymbolsInAddressSet( program.getAddressFactory().getAddressSet(), VTABLE_LABEL, true); 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; } Data vtableData = api.getDataAt(vtableAddress); if (vtableData == null) { continue; } // find the special type info ref Address typeinfoAddress = findNextTypeinfoRef(vtableAddress); if (typeinfoAddress == null) { if (DEBUG) { Msg.debug(this, vtableAddress.toString() + " " + vtableNamespace.getName() + " vtable has no typeinfo ref"); } continue; } Address vftableAddress = extendedFlatAPI.getAddress(typeinfoAddress, defaultPointerSize); // no valid address here so continue if (vftableAddress == null) { //createNewClass(vtableNamespace, false); // 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 address the given typeinfo address * @return true if it is a special one, false otherwise */ private boolean isSpecialTypeinfo(Address address) { if (address.equals(class_type_info) || address.equals(si_class_type_info) || address.equals(vmi_class_type_info)) { return true; } return false; } private boolean isSpecialVtable(Address address) { if (address.equals(class_type_info_vtable) || address.equals(si_class_type_info_vtable) || address.equals(vmi_class_type_info_vtable)) { return true; } return false; } private void createClassesFromTypeinfoSymbols(List typeinfoSymbols) throws CancelledException { 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; } // check for EXTERNAL block and look for specialTypeinfoRef there // if fix works, put external block error message and to contact us if (hasExternalBlock() && isSpecialVtable(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 = createNewClass(classNamespace, false); 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 = extendedFlatAPI.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { if (DEBUG) { Msg.debug(this, "No special typeinfo reference found. Cannot process typeinfo struct at " + typeinfoAddress.toString()); } continue; } if (!isSpecialTypeinfo(specialTypeinfoRef)) { // check for EXTERNAL block and look for specialTypeinfoRef there // if fix works, put external block error message and to contact us if (!hasExternalBlock()) { continue; } // use referenced vtable symbol name instead since when in EXTERNAL block // since can't get at the typeinfo ref in that block if (!isSpecialVtable(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) if (specialTypeinfoRef.equals(class_type_info) || specialTypeinfoRef.equals(class_type_info_vtable)) { nonInheritedGccClasses.add(recoveredClass); recoveredClass.setHasSingleInheritance(true); recoveredClass.setHasParentClass(false); recoveredClass.setInheritsVirtualAncestor(false); 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) || specialTypeinfoRef.equals(si_class_type_info_vtable)) { singleInheritedGccClasses.add(recoveredClass); recoveredClass.setHasSingleInheritance(true); recoveredClass.setInheritsVirtualAncestor(false); continue; } if (specialTypeinfoRef.equals(vmi_class_type_info) || specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { multiAndOrVirtuallyInheritedGccClasses.add(recoveredClass); // not necessarily multiple - maybe just a single virtual ancestor or maybe a single // non-public one } } } /** * Use information from RTTI Base class Arrays to create class hierarchy lists and maps * @throws CancelledException if cancelled */ private void createClassHierarchyListAndMapForGcc() 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 (multiAndOrVirtuallyInheritedGccClasses.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()); } } // update the inherits virtual ancestor flag using ancestors - previously was only done for // parents but now have all classes with flag set for direct parent so can get the other ancestors // too recoveredClassIterator = recoveredClasses.iterator(); while (recoveredClassIterator.hasNext()) { monitor.checkCanceled(); RecoveredClass recoveredClass = recoveredClassIterator.next(); // if we already know it then skip if (recoveredClass.inheritsVirtualAncestor()) { continue; } // if hasn't been set yet - check the other ancestors besides parents if (hasVirtualAncestor(recoveredClass)) { recoveredClass.setInheritsVirtualAncestor(true); } } } private boolean hasVirtualAncestor(RecoveredClass recoveredClass) throws CancelledException { List classHierarchy = recoveredClass.getClassHierarchy(); Iterator classIterator = classHierarchy.iterator(); while (classIterator.hasNext()) { monitor.checkCanceled(); RecoveredClass ancestor = classIterator.next(); if (ancestor.inheritsVirtualAncestor()) { return true; } } return false; } /** * 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 */ private 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 (multiAndOrVirtuallyInheritedGccClasses.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 * @throws CancelledException if cancelled * @throws Exception if data has conflict when created */ private void createLongs(Address start, Address end) throws CancelledException, Exception { LongDataType longDT = new LongDataType(); int offset = 0; Address address = start; while (address != null && !address.equals(end)) { api.clearListing(address, address.add(defaultPointerSize - 1)); api.createData(address, longDT); offset += defaultPointerSize; address = getAddress(start, offset); } } /** * 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 (!extendedFlatAPI.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 = extendedFlatAPI.getPointer(address); if (possibleFunctionPointer == null) { return false; } Function function = api.getFunctionAt(possibleFunctionPointer); if (function != null) { return true; } return false; } /** * 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. * @throws CancelledException when cancelled * @throws Exception if issue creating data */ private void createAndApplyClassStructures() 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 { // can't handle creating class data types for classes with virtual parents yet if (recoveredClass.inheritsVirtualAncestor()) { if (DEBUG) { Msg.debug(this, "Cannot create class data type for " + recoveredClass.getClassNamespace().getName(true) + " because it has virtual ancestors and we don't yet handle that use case."); } return; } // can't handle creating class data types for diamond shaped classes yet if (recoveredClass.isDiamondShaped()) { if (DEBUG) { Msg.debug(this, "Cannot create class data type for " + recoveredClass.getClassNamespace().getName(true) + " because it is diamond shaped and we don't yet handle that use case."); } return; } if (!recoveredClass.hasVftable()) { createSimpleClassStructure(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 = createSimpleClassStructure(recoveredClass, vfPointerDataTypes); // Now that we have a class data type // name constructor and destructor functions and put into the class namespace addConstructorsToClassNamespace(recoveredClass, classStruct); addDestructorsToClassNamespace(recoveredClass, classStruct); //TODO: // addNonThisDestructorsToClassNamespace(recoveredClass); // addVbaseDestructorsToClassNamespace(recoveredClass); // addVbtableToClassNamespace(recoveredClass); //TODO: // // 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, classStruct); // This is done after the class structure is created and added to the dtmanager // because if done before the class structures are created // then empty classes will get auto-created in the wrong place // when the vfunctions are put in the class fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes, classStruct); } private Structure createSimpleClassStructure(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); // if no inheritance - add pointer to class vftable structure if (nonInheritedGccClasses.contains(recoveredClass) && vfPointerDataTypes != null) { // the size was checked before calling this method so we know there is one and only // one for this simple case Address vftableAddress = recoveredClass.getVftableAddresses().get(0); DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress); // simple case the offset for vftablePtr is 0 if (EditStructureUtils.canAdd(classStructureDataType, 0, classVftablePointer.getLength(), monitor)) { classStructureDataType = EditStructureUtils.addDataTypeToStructure(classStructureDataType, 0, classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); } } // if single inheritance or multi non-virtual (wouldn't have called this method if // it were virtually inherited) put parent struct and data into class struct else { Map orderToParentMap = classToParentOrderMap.get(recoveredClass); if (orderToParentMap.isEmpty()) { throw new Exception( "Vmi class " + recoveredClass.getClassNamespace().getName(true) + " should have a parent in the classToParentOrderMap but doesn't"); } Map parentToOffsetMap = classToParentOffsetMap.get(recoveredClass); if (parentToOffsetMap.isEmpty()) { throw new Exception( "Vmi class " + recoveredClass.getClassNamespace().getName(true) + " should have a parent in the classToParentOffsetMap but doesn't"); } int numParents = orderToParentMap.keySet().size(); for (int i = 0; i < numParents; i++) { RecoveredClass parent = orderToParentMap.get(i); Long parentOffsetLong = parentToOffsetMap.get(parent); if (parentOffsetLong == null) { throw new Exception( "Can't get parent offset for " + parent.getClassNamespace().getName(true)); } int parentOffset = parentOffsetLong.intValue(); Structure baseClassStructure = getClassStructureFromDataTypeManager(parent); // if we can't get the parent throw exception because it shouldn't get here if the parent // doesn't exist if (baseClassStructure == null) { throw new Exception(parent.getClassNamespace().getName(true) + " : structure should exist but doesn't."); } if (EditStructureUtils.canAdd(classStructureDataType, parentOffset, baseClassStructure.getLength(), monitor)) { classStructureDataType = EditStructureUtils.addDataTypeToStructure(classStructureDataType, parentOffset, baseClassStructure, baseClassStructure.getName(), monitor); } } } // figure out class data, if any, create it and add to class structure int dataOffset = getDataOffset(recoveredClass, classStructureDataType); int dataLen = UNKNOWN; if (dataOffset != NONE) { dataLen = EditStructureUtils.getNumberOfUndefinedsStartingAtOffset(classStructureDataType, dataOffset, monitor); } if (dataLen != UNKNOWN && dataLen > 0) { Structure recoveredClassDataStruct = createClassMemberDataStructure(recoveredClass, classStructureDataType, dataLen, dataOffset); if (recoveredClassDataStruct != null) { classStructureDataType = EditStructureUtils.addDataTypeToStructure( classStructureDataType, dataOffset, recoveredClassDataStruct, "data", monitor); } } if (classStructureDataType.getNumComponents() == classStructureDataType .getNumDefinedComponents()) { classStructureDataType.setPackingEnabled(true); } classStructureDataType.setDescription(createParentStringBuffer(recoveredClass).toString()); classStructureDataType = (Structure) dataTypeManager.addDataType(classStructureDataType, DataTypeConflictHandler.DEFAULT_HANDLER); return classStructureDataType; } private boolean hasExternalBlock() { MemoryBlock externalBlock = program.getMemory().getBlock("EXTERNAL"); if (externalBlock == null) { return false; } return true; } }