diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java index 17f2fa611b..e1da6aebd4 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/RecoverClassesFromRTTIScript.java @@ -63,6 +63,8 @@ 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.bin.format.dwarf4.next.DWARFFunctionImporter; +import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.Options; @@ -127,7 +129,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { private static final String INDETERMINATE_BOOKMARK = "INDETERMINATE"; boolean programHasRTTIApplied = false; - boolean isPDBLoaded; + boolean hasDebugSymbols; boolean isGcc = false; boolean isWindows = false; String ghidraVersion = null; @@ -155,11 +157,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { if (isWindows()) { - isPDBLoaded = isPDBLoadedInProgram(); - nameVfunctions = !isPDBLoaded; + hasDebugSymbols = isPDBLoadedInProgram(); + nameVfunctions = !hasDebugSymbols; recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram, currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, - USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor); + USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, hasDebugSymbols, + monitor); } else if (isGcc()) { @@ -168,10 +171,18 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { if (!runGcc) { return; } - nameVfunctions = true; + + hasDebugSymbols = isDwarfLoadedInProgram(); + if (hasDwarf() && !hasDebugSymbols) { + println( + "The program contains DWARF but the DWARF analyzer has not been run. Please run the DWARF analyzer to get best results from this script."); + return; + } + nameVfunctions = !hasDebugSymbols; recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, - USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, monitor); + USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, hasDebugSymbols, + monitor); } else { println("This script will not work on this program type"); @@ -245,7 +256,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { " class member functions to assign."); - if (!isPDBLoaded) { + if (!hasDebugSymbols) { if (BOOKMARK_FOUND_FUNCTIONS) { bookmarkFunctions(recoveredClasses); @@ -268,6 +279,10 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { decompilerUtils.disposeDecompilerInterface(); } + private boolean hasDwarf() { + return DWARFProgram.isDWARF(currentProgram); + } + /** * Method to determine if pdb info has been applied to the program * @return true if pdb info has been applied to program @@ -277,6 +292,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { return options.getBoolean(PdbParserConstants.PDB_LOADED, false); } + private boolean isDwarfLoadedInProgram() { + + return DWARFFunctionImporter.hasDWARFProgModule(currentProgram, + DWARFProgram.DWARF_ROOT_NAME); + } + public String validate() { if (currentProgram == null) { @@ -454,8 +475,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { /** - * 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 + * Script works on versions of ghidra including and after 9.2 except for 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() { @@ -571,9 +592,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript { private String getVersionOfGhidra() { Options options = currentProgram.getOptions("Program Information"); - Object ghidraVersionObject = options.getObject("Created With Ghidra Version", null); - - return ghidraVersionObject.toString(); + return options.getString("Created With Ghidra Version", null); } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java index 6ab0834396..f049af9b82 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/DecompilerScriptUtils.java @@ -1,19 +1,3 @@ -/* ### - * 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. - */ -package classrecovery; /* ### * IP: GHIDRA * @@ -30,6 +14,7 @@ package classrecovery; * limitations under the License. */ //DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. +package classrecovery; import ghidra.app.decompiler.*; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.PluginTool; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java index ed8b8ced60..d276ed0150 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java @@ -1,19 +1,3 @@ -/* ### - * 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. - */ -package classrecovery; /* ### * IP: GHIDRA * @@ -30,6 +14,8 @@ package classrecovery; * 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.program.model.data.*; @@ -347,6 +333,10 @@ public class EditStructureUtils { public int getNumberOfUndefinedsBeforeOffset(Structure structure, int offset, TaskMonitor monitor) throws CancelledException { + if (structure.getNumComponents() == 0) { + return -1; + } + int numUndefineds = 0; int index = offset - 1; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtraScriptUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtraScriptUtils.java index 6cd529b01d..150c8696b6 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtraScriptUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtraScriptUtils.java @@ -13,23 +13,9 @@ * 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; -/* ### - * 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. + import java.util.*; import ghidra.app.cmd.function.CreateFunctionCmd; @@ -110,8 +96,8 @@ public class ExtraScriptUtils extends FlatProgramAPI { return null; } - Address possibleFunctionPointer = address.getNewAddress(offset); - return possibleFunctionPointer; + Address possiblePointer = address.getNewAddress(offset); + return possiblePointer; } catch (MemoryAccessException e) { @@ -686,6 +672,11 @@ public class ExtraScriptUtils extends FlatProgramAPI { public boolean doesFunctionACallAnyListedFunction(Function aFunction, List bFunctions) throws CancelledException { + + if (aFunction == null) { + return false; + } + Iterator bFunctionsIterator = bFunctions.iterator(); while (bFunctionsIterator.hasNext()) { monitor.checkCanceled(); @@ -798,6 +789,14 @@ public class ExtraScriptUtils extends FlatProgramAPI { public boolean doesFunctionACallFunctionB(Function aFunction, Function bFunction) throws CancelledException { + if (aFunction == null) { + return false; + } + + if (bFunction == null) { + return false; + } + Set calledFunctions = aFunction.getCalledFunctions(monitor); if (calledFunctions.contains(bFunction)) { return true; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java index b06ec50c80..daba6d4698 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIClassRecoverer.java @@ -1,19 +1,3 @@ -/* ### - * 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. - */ -package classrecovery; /* ### * IP: GHIDRA * @@ -30,6 +14,8 @@ package classrecovery; * 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.util.NamespaceUtils; @@ -52,10 +38,11 @@ public class RTTIClassRecoverer extends RecoveredClassUtils { String ghidraVersion; Program program; TaskMonitor monitor; + boolean hasDebugSymbols; RTTIClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVfunctions, + boolean nameVfunctions, boolean hasDebugSymbols, TaskMonitor monitor) { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, @@ -69,6 +56,7 @@ public class RTTIClassRecoverer extends RecoveredClassUtils { this.createBookmarks = createBookmarks; this.useShortTemplates = useShortTemplates; this.nameVfunctions = nameVfunctions; + this.hasDebugSymbols = hasDebugSymbols; ghidraVersion = getVersionOfGhidra(); } @@ -109,9 +97,7 @@ public class RTTIClassRecoverer extends RecoveredClassUtils { public String getVersionOfGhidra() { Options options = program.getOptions("Program Information"); - Object ghidraVersionObject = options.getObject("Created With Ghidra Version", null); - - return ghidraVersionObject.toString(); + return options.getString("Created With Ghidra Version", null); } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index df0405d5d1..43a70f8a19 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -1,20 +1,3 @@ -/* ### - * 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. - */ -package classrecovery; - /* ### * IP: GHIDRA * @@ -31,26 +14,34 @@ package classrecovery; * 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 java.util.stream.Collectors; import ghidra.app.cmd.label.DemanglerCmd; +import ghidra.app.util.NamespaceUtils; import ghidra.app.util.demangler.*; -import ghidra.framework.options.Options; 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; @@ -67,14 +58,26 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { List nonInheritedGccClasses = new ArrayList(); List singleInheritedGccClasses = new ArrayList(); - List multiInheritedGccClasses = new ArrayList(); + List multiAndOrVirtuallyInheritedGccClasses = new ArrayList(); + + List recoveredClasses = new ArrayList(); + + private Map> classToParentOrderMap = + new HashMap>(); + + private Map> classToParentOffsetMap = + new HashMap>(); + + boolean isDwarfLoaded; public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, - boolean nameVfunctions, TaskMonitor monitor) { + boolean nameVfunctions, boolean isDwarfLoaded, TaskMonitor monitor) { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, + isDwarfLoaded, monitor); + this.isDwarfLoaded = isDwarfLoaded; } @Override @@ -100,7 +103,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { try { - List recoveredClasses = processGccRTTI(); + processGccRTTI(); if (recoveredClasses == null) { Msg.debug(this, "Could not recover gcc rtti classes"); return null; @@ -108,21 +111,21 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { createCalledFunctionMap(recoveredClasses); - createClassHierarchyListAndMapForGcc(recoveredClasses); + createClassHierarchyListAndMapForGcc(); - //TODO: check for dwarf - //boolean isDwarfLoaded = isDwarfLoaded(); - assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); - //else - // find c/ds other way + if (isDwarfLoaded) { + retrieveExistingClassStructures(recoveredClasses); + assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); + } + else { + processConstructorAndDestructors(); + } createVftableOrderMap(recoveredClasses); - retrieveExistingClassStructures(recoveredClasses); - figureOutClassDataMembers(recoveredClasses); - createAndApplyClassStructures(recoveredClasses); + createAndApplyClassStructures(); return recoveredClasses; } @@ -232,64 +235,58 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } - private List processGccRTTI() throws CancelledException, Exception { + private void processGccRTTI() throws CancelledException, Exception { - // create rtti vtables and typeinfo structs - // find the three special vtables and replace the incorrectly made array with - // data types found in vtable - createGccRttiData(); + // 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 = 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); + createClassesFromTypeinfoSymbols(typeinfoSymbols); - // process vtables and create classes for the vtables that have no typeinfo - List vftableSymbols = findVftablesFromVtables(); - if (DEBUG) { - Msg.debug(this, "Found " + vftableSymbols.size() + "vftableSymbols"); - } + updateClassesWithParentsAndFlags(typeinfoSymbols); - List recoveredClasses = - recoverClassesFromVftables(vftableSymbols, true, true); - if (!nonVftableRecoveredClasses.isEmpty()) { - for (RecoveredClass nonVClass : nonVftableRecoveredClasses) { + // 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(); - if (!recoveredClasses.contains(nonVClass)) { - recoveredClasses.add(nonVClass); - } - } - } + Address vftableAddress = vftableAddressIterator.next(); - // check map for classes that are not in the recoveredClass list - Set keySet = namespaceToClassMap.keySet(); - for (Namespace namespace : keySet) { - monitor.checkCanceled(); - RecoveredClass recoveredClass = namespaceToClassMap.get(namespace); - if (!recoveredClasses.contains(recoveredClass)) { - recoveredClasses.add(recoveredClass); - } - } + Address offsetAddress = vftableAddress.subtract(2 * defaultPointerSize); + int offsetValue = (int) api.getLong(offsetAddress); - // 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); + recoveredClass.addClassOffsetToVftableMapping(offsetValue, vftableAddress); } + } + return; + + } + + private void updateClassesWithParentsAndFlags(List typeinfoSymbols) + throws Exception { // add properties and parents to each class - if (DEBUG) { - Msg.debug(this, "Found " + typeinfoSymbols.size() + " typeinfo symbols"); - } Iterator typeinfoIterator = typeinfoSymbols.iterator(); while (typeinfoIterator.hasNext()) { @@ -309,11 +306,11 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { 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 if (DEBUG) { - Msg.debug(this, "Shouldn't be a null class here: " + classNamespace.getName()); + Msg.debug(this, + "***Shouldn't be a null class here: " + classNamespace.getName()); } recoveredClass = createNewClass(classNamespace, false); recoveredClasses.add(recoveredClass); @@ -360,7 +357,20 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { if (specialTypeinfoRef.equals(class_type_info) || specialTypeinfoRef.equals(class_type_info_vtable)) { - nonInheritedGccClasses.add(recoveredClass); + 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; } @@ -368,99 +378,64 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // 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 parentClass = getSiClassParent(typeinfoAddress); - if (parentClass != null) { - if (DEBUG) { - Msg.debug(this, - recoveredClass.getName() + " adding parent " + parentClass.getName()); - } - updateClassWithParent(parentClass, recoveredClass); - - if (!recoveredClasses.contains(parentClass)) { - if (DEBUG) { - Msg.debug(this, recoveredClass.getName() + - " adding an unknown parent " + parentClass.getName()); - } - recoveredClasses.add(parentClass); - } - - } - else { - if (DEBUG) { - Msg.debug(this, "Could not get si parent from typeinfoAddress " + - typeinfoAddress.toString()); - } + 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)) { - multiInheritedGccClasses.add(recoveredClass); - recoveredClass.setHasMultipleInheritance(true); + List parents = + addGccClassParentsFromVmiStruct(recoveredClass, typeinfoAddress); - if (recoveredClass.inheritsVirtualAncestor()) { - recoveredClass.setHasMultipleVirtualInheritance(true); - } - - List parents = addGccClassParents(recoveredClass, typeinfoAddress); if (parents.isEmpty()) { continue; } + for (RecoveredClass parent : parents) { monitor.checkCanceled(); if (!recoveredClasses.contains(parent)) { - Msg.debug(this, "adding parent " + parent.getName() + " to list."); recoveredClasses.add(parent); } } - } } - Iterator recoveredClassIterator = recoveredClasses.iterator(); - while (recoveredClassIterator.hasNext()) { + return; - monitor.checkCanceled(); - - RecoveredClass recoveredClass = recoveredClassIterator.next(); - - Msg.debug(this, "Processing class " + recoveredClass.getClassNamespace().getName(true)); - - 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); - } - - } - - createCalledFunctionMap(recoveredClasses); - - assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); - - return recoveredClasses; - - } - - private void createGccRttiData() throws CancelledException, Exception { - - // create the appropriate type of type info struct at the various typeinfo symbol locations - createTypeinfoStructs(); - - // process vtables and create classes for the vtables that have no typeinfo - processVtables(); } /** @@ -469,11 +444,21 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { */ private void processVtables() throws Exception { - // find all vtable symbols - List listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet( - program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); + List listOfVtableSymbols = new ArrayList(); + + // if dwarf loaded then get vtables using symbols + if (!isDwarfLoaded) { + listOfVtableSymbols = findVtablesUsingTypeinfoRefs(); + } + else { + listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet( + program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); + } + + List copyListOfVtableSymbols = new ArrayList(listOfVtableSymbols); Iterator vtableIterator = listOfVtableSymbols.iterator(); + while (vtableIterator.hasNext()) { monitor.checkCanceled(); @@ -482,15 +467,326 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { Namespace vtableNamespace = vtableSymbol.getParentNamespace(); Address vtableAddress = vtableSymbol.getAddress(); - processVtable(vtableAddress, vtableNamespace, true); - + processVtable(vtableAddress, vtableNamespace, true, copyListOfVtableSymbols); } return; } - private void processVtable(Address vtableAddress, Namespace vtableNamespace, boolean isPrimary) + 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 = extraUtils.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); + } + + + try { + Symbol vtableSymbol = symbolTable.createLabel(vtableAddress, VTABLE_LABEL, + classNamespace, SourceType.ANALYSIS); + vtableSymbols.add(vtableSymbol); + } + catch (InvalidInputException e) { + continue; + } + + 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 (!extraUtils.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 = extraUtils.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()) { + + try { + Namespace newNamespace = + NamespaceUtils.createNamespaceHierarchy(name, vtableNamespace, + program, SourceType.ANALYSIS); + return newNamespace; + } + catch (InvalidInputException e) { + e.printStackTrace(); + return null; + } + } + 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 + */ + private void processVtable(Address vtableAddress, Namespace vtableNamespace, boolean isPrimary, + List listOfAllVtables) + throws Exception { + // skip the special tables if (vtableAddress.equals(class_type_info_vtable) || vtableAddress.equals(si_class_type_info_vtable) || @@ -533,65 +829,243 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } } + // 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); + + try { + 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)); + + } + catch (InvalidInputException e) { + Msg.debug(this, e.getMessage()); + continue; + } + catch (CircularDependencyException e) { + Msg.debug(this, e.getMessage()); + continue; + } + catch (DuplicateNameException e) { + continue; + } + } + } + + } + + } + + } + // create longs from top of vtable to the typeinfoAddress createLongs(vtableAddress, typeinfoAddress); - Address vftableAddress = getAddress(typeinfoAddress, defaultPointerSize); + Address possibleVftableAddress = getAddress(typeinfoAddress, defaultPointerSize); - if (vftableAddress == null) { + if (possibleVftableAddress == 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) { + 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; } - catch (AddressOutOfBoundsException e) { - Msg.debug(this, "Couldn't create vftable due to Address out of bounds issue"); + // if not an internal vtable check for VTT table + boolean isVTT = createVTT(vtableNamespace, possibleVftableAddress); + if (isVTT) { return; } + return; } - // check for an internal vtable and make a symbol there if there is one + // 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; + } + + try { + symbolTable.createLabel(possibleVftableAddress, vftableLabel, vtableNamespace, + SourceType.ANALYSIS); + + createVftableArray(possibleVftableAddress, numFunctionPointers); + } + catch (IllegalArgumentException e) { + Msg.debug(this, "Could not label vftable at " + possibleVftableAddress.toString()); + } + catch (InvalidInputException e) { + Msg.debug(this, "Could not label vftable at " + possibleVftableAddress.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 after the vftable and make a symbol there if there is one // will process them later Address possibleInternalVtableAddress = - getAddress(vftableAddress, defaultPointerSize * numFunctionPointers); + 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 = extraUtils.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 { + // 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; + return false; } if (possibleInternalVtableSymbol == null || @@ -602,19 +1076,72 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { symbolTable.createLabel(possibleInternalVtableAddress, "internal_vtable_" + possibleInternalVtableAddress.toString(), vtableNamespace, SourceType.ANALYSIS); - processVtable(possibleInternalVtableAddress, vtableNamespace, false); + processVtable(possibleInternalVtableAddress, vtableNamespace, false, null); + return true; } catch (IllegalArgumentException e) { Msg.debug(this, "Could not label internal vtable at " + possibleInternalVtableAddress.toString()); + return true; // still created vtable, just couldn't name it } catch (InvalidInputException e) { Msg.debug(this, "Could not label internal vtable at " + possibleInternalVtableAddress.toString()); + return true; // still created vtable, just couldn't name it + } + catch (Exception e) { + e.printStackTrace(); + } } + 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 + */ + private boolean createVTT(Namespace classNamespace, Address address) { + + // 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 + try { + symbolTable.createLabel(address, "VTT", classNamespace, SourceType.ANALYSIS); + } + catch (IllegalArgumentException e) { + Msg.debug(this, "Could not label VTT at " + address.toString()); + } + catch (InvalidInputException e) { + Msg.debug(this, "Could not label VTT at " + address.toString()); + } + + DataType nullPointer = dataTypeManager.getPointer(null); + try { + api.createData(pointer, nullPointer); + } + catch (Exception e) { + // already data there + } + + api.setPlateComment(address, "VTT for " + classNamespace.getName(true)); + + return true; } private Data createVftableArray(Address vftableAddress, int numFunctionPointers) @@ -904,7 +1431,12 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } } - private void createTypeinfoStructs() throws CancelledException { + /** + * 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 = @@ -913,8 +1445,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { createBaseClassTypeInfoStructure(classTypeInfoStructure); List
typeinfoAddresses; - // get dwarf option - boolean isDwarfLoaded = isDwarfLoaded(); + // if dwarf get using symbols if (isDwarfLoaded) { typeinfoAddresses = getTypeinfoAddressesUsingSymbols(); @@ -937,11 +1468,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); if (specialTypeinfoRef == null) { - if (DEBUG) { - Msg.debug(this, - "No special typeinfo reference found. Cannot process typeinfo struct at " + - typeinfoAddress.toString()); - } continue; } @@ -986,12 +1512,13 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } if (newStructure == null) { - Msg.debug(this, "ERROR: Could not apply typeinfo structure to " + typeinfoAddress); + 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) { + if (typeinfoSymbol == null || typeinfoSymbol.getSource() == SourceType.DEFAULT) { typeinfoSymbol = createDemangledTypeinfoSymbol(typeinfoAddress); if (typeinfoSymbol == null) { Msg.debug(this, "Could not create demangled typeinfo symbol at " + @@ -1004,8 +1531,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { continue; } - - } } @@ -1043,7 +1568,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // get or create the vmiClassTypeInfoStruct Structure vmiClassTypeinfoStructure = (Structure) dataTypeManager.getDataType(classDataTypesCategoryPath, - "VmiClassTypeInfoStructure" + numBases); + VMI_CLASS_TYPE_INFO_STRUCTURE + numBases); if (vmiClassTypeinfoStructure == null) { vmiClassTypeinfoStructure = createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); @@ -1164,7 +1689,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } Structure typeinfoStructure = (Structure) dataAt.getBaseDataType(); - if (!typeinfoStructure.getName().contains("ClassTypeInfoStructure")) { + if (!typeinfoStructure.getName().contains(CLASS_TYPE_INFO_STRUCTURE)) { return null; } DataTypeComponent typeinfoNameComponent = typeinfoStructure.getComponent(1); @@ -1315,20 +1840,65 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { return specialTypeinfoRefs; } - private boolean isDwarfLoaded() { - Options options = program.getOptions("Program Information"); - boolean isDwarfLoaded = false; - Object isPDBLoadedObject = options.getObject("DWARF Loaded", null); - if (isPDBLoadedObject != null) { - isDwarfLoaded = (boolean) isPDBLoadedObject; - } - return isDwarfLoaded; + + /** + * 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, - "ClassTypeInfoStructure", 0, dataTypeManager); + CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager); CharDataType characterDT = new CharDataType(); DataType pointer = dataTypeManager.getPointer(null); @@ -1345,12 +1915,12 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { StructureDataType classTypeInfoStructure) { StructureDataType siClassTypeInfoStructure = new StructureDataType( - classDataTypesCategoryPath, "SiClassTypeInfoStructure", 0, dataTypeManager); + classDataTypesCategoryPath, SI_CLASS_TYPE_INFO_STRUCTURE, 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); @@ -1363,17 +1933,38 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } private StructureDataType createBaseClassTypeInfoStructure( - StructureDataType classTypeInfoStructure) { + StructureDataType classTypeInfoStructure) throws InvalidDataTypeException { StructureDataType baseclassTypeInfoStructure = new StructureDataType( - classDataTypesCategoryPath, "BaseClassTypeInfoStructure", 0, dataTypeManager); + classDataTypesCategoryPath, BASE_CLASS_TYPE_INFO_STRUCTURE, 0, dataTypeManager); DataType classTypeInfoPointer = dataTypeManager.getPointer(classTypeInfoStructure); - LongDataType longDT = new LongDataType(); + int offsetBitSize = 24; + if (defaultPointerSize == 8) { + offsetBitSize = 56; + } baseclassTypeInfoStructure.add(classTypeInfoPointer, "classTypeinfoPtr", null); - baseclassTypeInfoStructure.add(longDT, "offsetFlags", 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); @@ -1386,7 +1977,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { StructureDataType vmiClassTypeInfoStructure = new StructureDataType(classDataTypesCategoryPath, - "VmiClassTypeInfoStructure" + numBaseClasses, 0, dataTypeManager); + VMI_CLASS_TYPE_INFO_STRUCTURE + numBaseClasses, 0, dataTypeManager); CharDataType characterDT = new CharDataType(); UnsignedIntegerDataType unsignedIntDT = new UnsignedIntegerDataType(); @@ -1394,7 +1985,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { 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); @@ -1415,124 +2006,178 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { * @param recoveredClass the given class * @param typeinfoAddress the address of the typeinfo * @return list of parents for the given class - * @throws CancelledException if cancelled + * @throws Exception if cannot access the given typeinfo structure, one of its components, or it is not a vmi structure */ - private List addGccClassParents(RecoveredClass recoveredClass, - Address typeinfoAddress) throws CancelledException { + 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(); - 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) { - - if (DEBUG) { - Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() + - " plus offset " + inheritanceTypeFlagOffset); - } - return parentClassList; - } - - // process the multiple inheritance flag - try { - int inheritanceFlagValue = api.getInt(inheritanceFlagAddress); - if (inheritanceFlagValue == 0) { - recoveredClass.hasMultipleInheritance(); - } - // TODO: add flags to class for non-diamond repeated and diamond shape types - - if (DEBUG) { - 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")) { - - if (DEBUG) { - Msg.debug(this, "Could not acess baseClassArray at " + - typeinfoStructure.toString() + baseClassArrayOffset); - } - return parentClassList; - } - - int numParents = baseClassArrayData.getNumComponents(); + int numParents = numBaseClasses; 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; - } + + // get parent from pointer to parent typeinfo + Address parentRefAddress = + getAddress(typeinfoAddress, baseClassArrayOffset + (i * 2 * defaultPointerSize)); RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentRefAddress); - - if (parentClass != null) { - if (DEBUG) { - Msg.debug(this, - recoveredClass.getName() + " adding vmi parent " + parentClass.getName()); - } - - updateClassWithParent(parentClass, recoveredClass); - parentClassList.add(parentClass); + if (parentClass == null) { + throw new Exception("Could not get parent class number " + (i + 1) + + " from typeinfo struct at " + typeinfoAddress.toString()); } - // get long value flag - Address flagAddress = extraUtils.getAddress(typeinfoAddress, + 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)); - 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). + 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 - try { - long flags = api.getLong(flagAddress); - // TODO: process flag - // split out the offset from the virt/public flag - // offset >> 8 & 0xffffff + boolean isVirtual = false; + boolean isPublic = false; + + long virtualMask = 0x1L; + long publicMask = 0x2L; + long offsetMask; + if (defaultPointerSize == 4) { + offsetMask = 0xffffff00L; } - catch (MemoryAccessException e) { - Msg.debug(this, "couldn't get long at address " + flagAddress.toString()); + 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; + + 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; } @@ -1541,21 +2186,23 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { * 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) { + private RecoveredClass getSiClassParent(Address typeinfoAddress) throws Exception { int offset = defaultPointerSize * 2; - Address parentTypeinfoRef = extraUtils.getAddress(typeinfoAddress, offset); + Address parentTypeinfoRef = getAddress(typeinfoAddress, offset); if (parentTypeinfoRef == null) { - if (DEBUG) { - Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() + - " plus offset " + offset); - } - return null; + + throw new Exception("Could not access address " + typeinfoAddress.toString() + + " plus offset " + offset); + } RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentTypeinfoRef); + return parentClass; } @@ -1566,6 +2213,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { * @return the associated parent class */ private RecoveredClass getParentClassFromParentTypeInfoRef(Address parentTypeinfoRef) { + Address parentAddress = extraUtils.getSingleReferencedAddress(parentTypeinfoRef); if (parentAddress == null) { return null; @@ -1645,65 +2293,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { 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 @@ -1758,7 +2347,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // find all vtable symbols List listOfVtableSymbols = extraUtils.getListOfSymbolsInAddressSet( - program.getAddressFactory().getAddressSet(), VTABLE_LABEL, false); + program.getAddressFactory().getAddressSet(), VTABLE_LABEL, true); Iterator vtableIterator = listOfVtableSymbols.iterator(); while (vtableIterator.hasNext()) { @@ -1776,15 +2365,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { 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) { - createNewClass(vtableNamespace, false); - continue; - } - Data vtableData = api.getDataAt(vtableAddress); if (vtableData == null) { continue; @@ -1803,7 +2383,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { Address vftableAddress = extraUtils.getAddress(typeinfoAddress, defaultPointerSize); // no valid address here so continue if (vftableAddress == null) { - createNewClass(vtableNamespace, false); + //createNewClass(vtableNamespace, false); // if so should also add to no vftable class continue; } @@ -1840,11 +2420,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { return false; } - private List createClassesFromTypeinfoSymbols(List typeinfoSymbols) + private void createClassesFromTypeinfoSymbols(List typeinfoSymbols) throws CancelledException { - List recoveredClasses = new ArrayList(); - Iterator typeinfoIterator = typeinfoSymbols.iterator(); while (typeinfoIterator.hasNext()) { @@ -1863,8 +2441,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { continue; } - // TODO: make sure it is a valid typeinfo - Namespace classNamespace = typeinfoSymbol.getParentNamespace(); RecoveredClass recoveredClass = getClass(classNamespace); @@ -1912,47 +2488,43 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // 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) || specialTypeinfoRef.equals(class_type_info_vtable)) { - // //TODO: make this a method - addGccNoInhClass + + nonInheritedGccClasses.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) || specialTypeinfoRef.equals(si_class_type_info_vtable)) { - // TODO: make this a method and pull the part out of add parents that handles the - // single parent one + singleInheritedGccClasses.add(recoveredClass); recoveredClass.setHasSingleInheritance(true); recoveredClass.setInheritsVirtualAncestor(false); - recoveredClass.setIsPublicClass(true); - continue; } if (specialTypeinfoRef.equals(vmi_class_type_info) || specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { - recoveredClass.setHasMultipleInheritance(true); + multiAndOrVirtuallyInheritedGccClasses.add(recoveredClass); + // not necessarily multiple - maybe just a single virtual ancestor or maybe a single + // non-public one + } } - 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) + private void createClassHierarchyListAndMapForGcc() throws CancelledException, Exception { Iterator recoveredClassIterator = recoveredClasses.iterator(); @@ -1987,7 +2559,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // 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)) { + if (multiAndOrVirtuallyInheritedGccClasses.contains(recoveredClass)) { classHierarchyList = getGccMultiClassHierarchy(recoveredClass); recoveredClass.setClassHierarchy(classHierarchyList); } @@ -2009,8 +2581,40 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } } - // TODO: create base type maps to add if virtual parent or not + // 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; } /** @@ -2018,7 +2622,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { * @param recoveredClass the given class * @return the class hierarchy list for the given class with no inheritance */ - List getGccNoClassHierarchy(RecoveredClass recoveredClass) { + private List getGccNoClassHierarchy(RecoveredClass recoveredClass) { List classHierarchyList = new ArrayList(); classHierarchyList.add(recoveredClass); return classHierarchyList; @@ -2073,7 +2677,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { classHierarchyList.addAll(parentClass.getClassHierarchy()); continue; } - if (multiInheritedGccClasses.contains(parentClass)) { + if (multiAndOrVirtuallyInheritedGccClasses.contains(parentClass)) { classHierarchyList.addAll(getGccMultiClassHierarchy(parentClass)); } } @@ -2190,12 +2794,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { /** * 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 { + private void createAndApplyClassStructures() throws CancelledException, Exception { List listOfClasses = new ArrayList(recoveredClasses); @@ -2270,18 +2872,28 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { private void processDataTypes(RecoveredClass recoveredClass) throws CancelledException, Exception { - // can't handle gcc multi and/or virtual class data types yet - if (multiInheritedGccClasses.contains(recoveredClass) || - recoveredClass.inheritsVirtualAncestor()) { + // 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; } - // shouldn't happen if we get to this point since it should be all single or no inherited classes - // but check anyway - if (recoveredClass.getVftableAddresses().size() > 1) { + // 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 @@ -2293,18 +2905,17 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // 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; + // create current class structure and add pointer to vftable, all parent member data strutures, + // and class member data structure + Structure classStruct = createSimpleClassStructure(recoveredClass, vfPointerDataTypes); - classStruct = createSimpleClassStructure(recoveredClass, vfPointerDataTypes); + // check for DWARF -- if none add c/d/etc to class + if (!isDwarfLoaded) { - // TODO: check for DWARF -- if none add c/d/etc to class -// if (!isDWARFLoaded) { -// -// // Now that we have a class data type -// // name constructor and destructor functions and put into the class namespace -// addConstructorsToClassNamespace(recoveredClass, classStruct); -// addDestructorsToClassNamespace(recoveredClass); + // 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); @@ -2313,10 +2924,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { // createInlinedConstructorComments(recoveredClass); // createInlinedDestructorComments(recoveredClass); // createIndeterminateInlineComments(recoveredClass); -// -// // add label on constructor destructor functions that could not be determined which were which -// createIndeterminateLabels(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 @@ -2352,12 +2963,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { Structure classStructureDataType = new StructureDataType(classPath, className, structLen, dataTypeManager); - List parentList = recoveredClass.getParentList(); - // shouldn't happen but check anyway - if (parentList.size() > 1) { - return classStructureDataType; - } - // if no inheritance - add pointer to class vftable structure if (nonInheritedGccClasses.contains(recoveredClass) && vfPointerDataTypes != null) { @@ -2374,18 +2979,54 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { } } - // if single inheritance put parent and data - else if (singleInheritedGccClasses.contains(recoveredClass)) { + // 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 { - RecoveredClass baseClass = parentList.get(0); - Structure baseClassStructure = getClassStructureFromDataTypeManager(baseClass); - - // simple case - base class at offset 0 - if (structUtils.canAdd(classStructureDataType, 0, baseClassStructure.getLength(), - monitor)) { - classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType, - 0, baseClassStructure, baseClassStructure.getName(), monitor); + 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 (structUtils.canAdd(classStructureDataType, parentOffset, + baseClassStructure.getLength(), monitor)) { + classStructureDataType = + structUtils.addDataTypeToStructure(classStructureDataType, parentOffset, + baseClassStructure, baseClassStructure.getName(), monitor); + } + } + } // figure out class data, if any, create it and add to class structure diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index ae095681e2..20d7f9afa1 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -13,23 +13,9 @@ * 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; -/* ### - * 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. + import java.util.*; import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd; @@ -89,7 +75,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { TaskMonitor monitor) throws CancelledException { super(program, location, tool, api, createBookmarks, useShortTemplates, nameVFunctions, - monitor); + isPDBLoaded, monitor); this.isPDBLoaded = isPDBLoaded; diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java index 16f0b588a2..495b630200 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClass.java @@ -1,19 +1,3 @@ -/* ### - * 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. - */ -package classrecovery; /* ### * IP: GHIDRA * @@ -30,6 +14,8 @@ package classrecovery; * 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 java.util.Map.Entry; @@ -97,6 +83,7 @@ public class RecoveredClass { private boolean inheritsVirtualAncestor = false; private boolean isPublicClass = false; + private boolean isDiamondShaped = false; private Structure existingClassStructure = null; private Structure computedClassStructure = null; @@ -283,6 +270,14 @@ public class RecoveredClass { return isPublicClass; } + public void setIsDiamondShaped(boolean setting) { + isDiamondShaped = setting; + } + + public boolean isDiamondShaped() { + return isDiamondShaped; + } + public void setHasVftable(boolean setting) { hasVftable = setting; } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java index 7331621216..cd070c0389 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassUtils.java @@ -1,19 +1,3 @@ -/* ### - * 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. - */ -package classrecovery; /* ### * IP: GHIDRA * @@ -30,6 +14,8 @@ package classrecovery; * 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 java.util.stream.Collectors; @@ -1512,7 +1498,7 @@ public class RecoveredClassUtils { /** * 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 + * @return List of ancestors without vfunctions * @throws CancelledException if cancelled */ public List getAncestorsWithoutVfunctions(RecoveredClass recoveredClass) @@ -2772,7 +2758,6 @@ public class RecoveredClassUtils { } // add it to the vftableAddress to Class map - //vftableToClassMap.put(vftableAddress, recoveredClass); updateVftableToClassMap(vftableAddress, recoveredClass); List
referencesToVftable = getReferencesToVftable(vftableAddress);