From ac1c9d9018d8f6c42f138f45d435641590002dea Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Fri, 16 Jul 2021 13:19:52 -0400 Subject: [PATCH 01/10] GP-1111 chnage create structure from components to adopt the pack settings from its parent --- .../compositeeditor/StructureEditorModel.java | 10 ++++++---- .../program/model/data/StructureFactory.java | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java index f5436d210d..f731f37cc0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java @@ -1216,11 +1216,13 @@ class StructureEditorModel extends CompEditorModel { final StructureDataType structureDataType = new StructureDataType(originalCategoryPath, uniqueName, length, originalDTM); -// if (isPackingEnabled()) { -// structureDataType.setPackingValue(getPackingValue()); -// } + // adopt pack setting from current structure + structureDataType.setPackingEnabled(isPackingEnabled()); + if (getPackingType() == PackingType.EXPLICIT) { + structureDataType.setExplicitPackingValue(getExplicitPackingValue()); + } -// Get data type components to make into structure. + // Get data type components to make into structure. DataTypeComponent firstDtc = null; DataTypeComponent lastDtc = null; for (int rowIndex = minRow; rowIndex < maxRow; rowIndex++) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java index 7d3355ffd0..8769663e0b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java @@ -238,9 +238,19 @@ public class StructureFactory { throw new IllegalArgumentException("No data type components found"); } - for (int i = 0; i < dataComps.length; i++) { - structure.add(dataComps[i].getDataType(), dataComps[i].getLength(), - dataComps[i].getFieldName(), dataComps[i].getComment()); + // adopt pack settings from parent - things could move as a result + DataType parent = dataComps[0].getParent(); + if (parent instanceof Composite) { + Composite c = (Composite) parent; + structure.setPackingEnabled(c.isPackingEnabled()); + if (c.getPackingType() == PackingType.EXPLICIT) { + structure.setExplicitPackingValue(c.getExplicitPackingValue()); + } + } + + for (DataTypeComponent dataComp : dataComps) { + structure.add(dataComp.getDataType(), dataComp.getLength(), + dataComp.getFieldName(), dataComp.getComment()); } } } From e9ebb476266c8031941c502675164c59dfb79689 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Fri, 30 Jul 2021 15:19:06 -0400 Subject: [PATCH 02/10] GP-1173 - fixed bad exception message Closes #3246 --- .../src/main/java/ghidra/program/model/data/EnumDataType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java index afbf6578dd..02a6b08145 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java @@ -115,7 +115,7 @@ public class EnumDataType extends GenericDataType implements Enum { bitGroups = null; checkValue(value); if (nameMap.containsKey(valueName)) { - throw new IllegalArgumentException(name + " already exists in this enum"); + throw new IllegalArgumentException(valueName + " already exists in this enum"); } nameMap.put(valueName, value); List list = valueMap.get(value); From 6d5f30f44805b118363ccf1c8c1d1320abb43a82 Mon Sep 17 00:00:00 2001 From: ghidra007 Date: Fri, 30 Jul 2021 18:50:08 -0400 Subject: [PATCH 03/10] GP-1055 Improvements to prototype RecoverClassesFromRTTIScript for stripped gcc binaries including improved finding and creating of virtual tables, some constructor/destructor determination, and improved class data creation. --- .../RecoverClassesFromRTTIScript.java | 43 +- .../classrecovery/DecompilerScriptUtils.java | 17 +- .../classrecovery/EditStructureUtils.java | 22 +- .../classrecovery/ExtraScriptUtils.java | 35 +- .../classrecovery/RTTIClassRecoverer.java | 26 +- .../classrecovery/RTTIGccClassRecoverer.java | 1523 ++++++++++++----- .../RTTIWindowsClassRecoverer.java | 20 +- .../classrecovery/RecoveredClass.java | 27 +- .../classrecovery/RecoveredClassUtils.java | 21 +- 9 files changed, 1160 insertions(+), 574 deletions(-) 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); From 3e3b2f4062f7155594bc4998454f2e0c91c09853 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Wed, 28 Jul 2021 10:26:39 -0400 Subject: [PATCH 04/10] GP-1058: Added test which manifests the SUBPIECE problem. --- .../exec/trace/TracePcodeEmulatorTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java index 2effa373c0..d84487727b 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java @@ -630,4 +630,42 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes TraceSleighUtils.evaluate("r1", tb.trace, 1, thread, 0)); } } + + /** + * Test x86's MOVAPS instruction + * + *

+ * This test hits a SUBPIECE instruction where the two input operands have differing sizes. + */ + @Test + public void testMOVAPS() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { + Register pc = tb.language.getProgramCounter(); + + TraceThread thread = initTrace(tb, + List.of( + "RIP = 0x00400000;", + "RSP = 0x00110000;", + "*:8 0x00600000:8 = 0x0123456789abcdef;", + "*:8 0x00600008:8 = 0xfedcba9876543210;"), + List.of( + "MOVAPS XMM0, xmmword ptr [0x00600007]")); + + TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0); + PcodeThread emuThread = emu.newThread(thread.getPath()); + emuThread.overrideContextWithDefault(); + emuThread.stepInstruction(); + + assertEquals(tb.addr(0x00400007), emuThread.getCounter()); + assertArrayEquals(tb.arr(0x07, 0, 0x40, 0, 0, 0, 0, 0), + emuThread.getState().getVar(pc)); + + try (UndoableTransaction tid = tb.startTransaction()) { + emu.writeDown(tb.trace, 1, 1, false); + } + + assertEquals(new BigInteger("0123456789abcdeffedcba9876543210", 16), + TraceSleighUtils.evaluate("XMM0", tb.trace, 1, thread, 0)); + } + } } From 802a7869f1f65c7c7b919f5baa98ce9799371a2b Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Wed, 28 Jul 2021 10:38:31 -0400 Subject: [PATCH 05/10] GP-1058: Adding sizein2 to PcodeArithmetic.binaryOp --- .../exec/AsyncWrappedPcodeArithmetic.java | 11 +++---- .../TraceMemoryStatePcodeArithmetic.java | 6 ++-- .../pcode/exec/AddressOfPcodeArithmetic.java | 4 +-- .../pcode/exec/BigIntegerPcodeArithmetic.java | 10 +++---- .../pcode/exec/BytesPcodeArithmetic.java | 29 ++++++++++--------- .../pcode/exec/PairedPcodeArithmetic.java | 14 ++++----- .../ghidra/pcode/exec/PcodeArithmetic.java | 4 +-- .../java/ghidra/pcode/exec/PcodeExecutor.java | 8 ++--- 8 files changed, 44 insertions(+), 42 deletions(-) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeArithmetic.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeArithmetic.java index 4f2938d33b..dbe14e5c51 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeArithmetic.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/pcode/exec/AsyncWrappedPcodeArithmetic.java @@ -45,15 +45,16 @@ public class AsyncWrappedPcodeArithmetic implements PcodeArithmetic unaryOp(UnaryOpBehavior op, int sizeout, int sizein, + public CompletableFuture unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, CompletableFuture in1) { - return in1.thenApply(t1 -> arithmetic.unaryOp(op, sizeout, sizein, t1)); + return in1.thenApply(t1 -> arithmetic.unaryOp(op, sizeout, sizein1, t1)); } @Override - public CompletableFuture binaryOp(BinaryOpBehavior op, int sizeout, int sizein, - CompletableFuture in1, CompletableFuture in2) { - return in1.thenCombine(in2, (t1, t2) -> arithmetic.binaryOp(op, sizeout, sizein, t1, t2)); + public CompletableFuture binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, + CompletableFuture in1, int sizein2, CompletableFuture in2) { + return in1.thenCombine(in2, + (t1, t2) -> arithmetic.binaryOp(op, sizeout, sizein1, t1, sizein2, t2)); } @Override diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceMemoryStatePcodeArithmetic.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceMemoryStatePcodeArithmetic.java index e4bc75b3cd..ebe7b286ec 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceMemoryStatePcodeArithmetic.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceMemoryStatePcodeArithmetic.java @@ -26,14 +26,14 @@ public enum TraceMemoryStatePcodeArithmetic implements PcodeArithmetic { INSTANCE; @Override - public Address unaryOp(UnaryOpBehavior op, int sizeout, int sizein, Address in1) { + public Address unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, Address in1) { return null; } @Override - public Address binaryOp(BinaryOpBehavior op, int sizeout, int sizein, Address in1, + public Address binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, Address in1, int sizein2, Address in2) { return null; } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BigIntegerPcodeArithmetic.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BigIntegerPcodeArithmetic.java index 1bb18b9ab5..0a510f289b 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BigIntegerPcodeArithmetic.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BigIntegerPcodeArithmetic.java @@ -24,14 +24,14 @@ public enum BigIntegerPcodeArithmetic implements PcodeArithmetic { INSTANCE; @Override - public BigInteger unaryOp(UnaryOpBehavior op, int sizeout, int sizein, BigInteger in1) { - return op.evaluateUnary(sizeout, sizein, in1); + public BigInteger unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, BigInteger in1) { + return op.evaluateUnary(sizeout, sizein1, in1); } @Override - public BigInteger binaryOp(BinaryOpBehavior op, int sizeout, int sizein, BigInteger in1, - BigInteger in2) { - return op.evaluateBinary(sizeout, sizein, in1, in2); + public BigInteger binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, BigInteger in1, + int sizein2, BigInteger in2) { + return op.evaluateBinary(sizeout, sizein1, in1, in2); } @Override diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java index b4ecfaf86b..0d82d0f200 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java @@ -40,31 +40,32 @@ public enum BytesPcodeArithmetic implements PcodeArithmetic { } @Override - public byte[] unaryOp(UnaryOpBehavior op, int sizeout, int sizein, byte[] in1) { - if (sizein > 8 || sizeout > 8) { - BigInteger in1Val = Utils.bytesToBigInteger(in1, sizein, isBigEndian, false); - BigInteger outVal = op.evaluateUnary(sizeout, sizein, in1Val); + public byte[] unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, byte[] in1) { + if (sizein1 > 8 || sizeout > 8) { + BigInteger in1Val = Utils.bytesToBigInteger(in1, in1.length, isBigEndian, false); + BigInteger outVal = op.evaluateUnary(sizeout, sizein1, in1Val); return Utils.bigIntegerToBytes(outVal, sizeout, isBigEndian); } else { - long in1Val = Utils.bytesToLong(in1, sizein, isBigEndian); - long outVal = op.evaluateUnary(sizeout, sizein, in1Val); + long in1Val = Utils.bytesToLong(in1, sizein1, isBigEndian); + long outVal = op.evaluateUnary(sizeout, sizein1, in1Val); return Utils.longToBytes(outVal, sizeout, isBigEndian); } } @Override - public byte[] binaryOp(BinaryOpBehavior op, int sizeout, int sizein, byte[] in1, byte[] in2) { - if (sizein > 8 || sizeout > 8) { - BigInteger in1Val = Utils.bytesToBigInteger(in1, sizein, isBigEndian, false); - BigInteger in2Val = Utils.bytesToBigInteger(in2, sizein, isBigEndian, false); - BigInteger outVal = op.evaluateBinary(sizeout, sizein, in1Val, in2Val); + public byte[] binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, byte[] in1, int sizein2, + byte[] in2) { + if (sizein1 > 8 || sizeout > 8) { + BigInteger in1Val = Utils.bytesToBigInteger(in1, sizein1, isBigEndian, false); + BigInteger in2Val = Utils.bytesToBigInteger(in2, sizein2, isBigEndian, false); + BigInteger outVal = op.evaluateBinary(sizeout, sizein1, in1Val, in2Val); return Utils.bigIntegerToBytes(outVal, sizeout, isBigEndian); } else { - long in1Val = Utils.bytesToLong(in1, sizein, isBigEndian); - long in2Val = Utils.bytesToLong(in2, sizein, isBigEndian); - long outVal = op.evaluateBinary(sizeout, sizein, in1Val, in2Val); + long in1Val = Utils.bytesToLong(in1, sizein1, isBigEndian); + long in2Val = Utils.bytesToLong(in2, sizein2, isBigEndian); + long outVal = op.evaluateBinary(sizeout, sizein1, in1Val, in2Val); return Utils.longToBytes(outVal, sizeout, isBigEndian); } } diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeArithmetic.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeArithmetic.java index 6b93a88509..e12fe73794 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeArithmetic.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PairedPcodeArithmetic.java @@ -45,18 +45,18 @@ public class PairedPcodeArithmetic implements PcodeArithmetic> } @Override - public Pair unaryOp(UnaryOpBehavior op, int sizeout, int sizein, Pair in1) { + public Pair unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, Pair in1) { return new ImmutablePair<>( - leftArith.unaryOp(op, sizeout, sizein, in1.getLeft()), - rightArith.unaryOp(op, sizeout, sizein, in1.getRight())); + leftArith.unaryOp(op, sizeout, sizein1, in1.getLeft()), + rightArith.unaryOp(op, sizeout, sizein1, in1.getRight())); } @Override - public Pair binaryOp(BinaryOpBehavior op, int sizeout, int sizein, Pair in1, - Pair in2) { + public Pair binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, Pair in1, + int sizein2, Pair in2) { return new ImmutablePair<>( - leftArith.binaryOp(op, sizeout, sizein, in1.getLeft(), in2.getLeft()), - rightArith.binaryOp(op, sizeout, sizein, in2.getRight(), in2.getRight())); + leftArith.binaryOp(op, sizeout, sizein1, in1.getLeft(), sizein2, in2.getLeft()), + rightArith.binaryOp(op, sizeout, sizein1, in2.getRight(), sizein2, in2.getRight())); } @Override diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeArithmetic.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeArithmetic.java index add7bf045d..735aa42593 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeArithmetic.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeArithmetic.java @@ -25,9 +25,9 @@ public interface PcodeArithmetic { PcodeArithmetic BYTES_LE = BytesPcodeArithmetic.LITTLE_ENDIAN; PcodeArithmetic BIGINT = BigIntegerPcodeArithmetic.INSTANCE; - T unaryOp(UnaryOpBehavior op, int sizeout, int sizein, T in1); + T unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, T in1); - T binaryOp(BinaryOpBehavior op, int sizeout, int sizein, T in1, T in2); + T binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, T in1, int sizein2, T in2); T fromConst(long value, int size); diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java index b0365f9e68..9145bfd3cd 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java @@ -105,8 +105,8 @@ public class PcodeExecutor { Varnode in1Var = op.getInput(0); Varnode outVar = op.getOutput(); T in1 = state.getVar(in1Var); - T out = - arithmetic.unaryOp((UnaryOpBehavior) b, outVar.getSize(), in1Var.getSize(), in1); + T out = arithmetic.unaryOp((UnaryOpBehavior) b, outVar.getSize(), + in1Var.getSize(), in1); state.setVar(outVar, out); return; } @@ -116,8 +116,8 @@ public class PcodeExecutor { Varnode outVar = op.getOutput(); T in1 = state.getVar(in1Var); T in2 = state.getVar(in2Var); - T out = arithmetic.binaryOp((BinaryOpBehavior) b, outVar.getSize(), in1Var.getSize(), - in1, in2); + T out = arithmetic.binaryOp((BinaryOpBehavior) b, outVar.getSize(), + in1Var.getSize(), in1, in2Var.getSize(), in2); state.setVar(outVar, out); return; } From 2c4806dedd58ef9ff55f2bb3d4b1d4c867bdb9e0 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Wed, 28 Jul 2021 10:45:14 -0400 Subject: [PATCH 06/10] GP-1058: Fixed the test assertions, now that it executes. --- .../ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java index d84487727b..619ef81d4f 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java @@ -646,10 +646,10 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes List.of( "RIP = 0x00400000;", "RSP = 0x00110000;", - "*:8 0x00600000:8 = 0x0123456789abcdef;", - "*:8 0x00600008:8 = 0xfedcba9876543210;"), + "*:8 0x00600008:8 = 0x0123456789abcdef;", // LE + "*:8 0x00600000:8 = 0xfedcba9876543210;"), List.of( - "MOVAPS XMM0, xmmword ptr [0x00600007]")); + "MOVAPS XMM0, xmmword ptr [0x00600000]")); TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0); PcodeThread emuThread = emu.newThread(thread.getPath()); From c50a4e282a7394eb4e728a35cb27d9cf21156312 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 2 Aug 2021 12:33:50 -0400 Subject: [PATCH 07/10] GP-1058: Added another test case. --- .../exec/trace/TracePcodeEmulatorTest.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java index 619ef81d4f..568c3ad71f 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/TracePcodeEmulatorTest.java @@ -329,7 +329,6 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes @Test public void testBRDS() throws Throwable { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) { - Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0)); TraceThread thread = initTrace(tb, List.of( "pc = 0x00400000;", @@ -668,4 +667,42 @@ public class TracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes TraceSleighUtils.evaluate("XMM0", tb.trace, 1, thread, 0)); } } + + /** + * ( Test x86's SAR instruction + * + *

+ * This test hits an INT_SRIGHT p-code op where the two input operands have differing sizes. + */ + @Test + public void testSAR() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { + Register pc = tb.language.getProgramCounter(); + + TraceThread thread = initTrace(tb, + List.of( + "RIP = 0x00400000;", + "RSP = 0x00110000;", + "RAX = 0x7fffffff;", + "RCX = 4;"), + List.of( + "SAR EAX, CL")); + + TracePcodeEmulator emu = new TracePcodeEmulator(tb.trace, 0); + PcodeThread emuThread = emu.newThread(thread.getPath()); + emuThread.overrideContextWithDefault(); + emuThread.stepInstruction(); + + assertEquals(tb.addr(0x00400002), emuThread.getCounter()); + assertArrayEquals(tb.arr(0x02, 0, 0x40, 0, 0, 0, 0, 0), + emuThread.getState().getVar(pc)); + + try (UndoableTransaction tid = tb.startTransaction()) { + emu.writeDown(tb.trace, 1, 1, false); + } + + assertEquals(BigInteger.valueOf(0x7ffffff), + TraceSleighUtils.evaluate("RAX", tb.trace, 1, thread, 0)); + } + } } From 5a1250e204699b30c52c7841a6db2284ad54cbe8 Mon Sep 17 00:00:00 2001 From: ghidravore Date: Mon, 2 Aug 2021 13:12:44 -0400 Subject: [PATCH 08/10] GP-1178 fixed Address compareTo for 64 bit signed spaces --- .../program/model/address/GenericAddress.java | 23 +--- .../model/address/GenericAddressTest.java | 122 +++++++++++++++++- 2 files changed, 127 insertions(+), 18 deletions(-) diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java index 6d4b295404..b004567d5e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java @@ -249,23 +249,14 @@ public class GenericAddress implements Address { @Override public int compareTo(Address a) { int comp = addrSpace.compareTo(a.getAddressSpace()); - if (comp == 0) { - long otherOffset = a.getOffset(); - if (!addrSpace.hasSignedOffset() && addrSpace.getMaxAddress().getOffset() < 0) { - // Address space offsets are 64-bit unsigned - if ((offset < 0 && otherOffset >= 0) || (offset >= 0 && otherOffset < 0)) { - return offset >= 0 ? -1 : 1; - } - } - long diff = offset - otherOffset; - if (diff > 0) { - return 1; - } - if (diff < 0) { - return -1; - } + if (comp != 0) { + return comp; } - return comp; + long otherOffset = a.getOffset(); + if (addrSpace.hasSignedOffset()) { + return Long.compare(offset, otherOffset); + } + return Long.compareUnsigned(offset, otherOffset); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/GenericAddressTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/GenericAddressTest.java index 536223f256..b0e3d6aa2a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/GenericAddressTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/GenericAddressTest.java @@ -15,8 +15,7 @@ */ package ghidra.program.model.address; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.*; @@ -186,6 +185,125 @@ public class GenericAddressTest extends AbstractGenericTest { } + @Test + public void testCompareToWithUnSigned32AddressSpace() { + AddressSpace space32Unsigned = + new GenericAddressSpace("test", 32, AddressSpace.TYPE_CODE, 0); + + Address addr0 = new GenericAddress(space32Unsigned, 0); + Address addrMax = space32Unsigned.getMaxAddress(); + Address addrPositive = new GenericAddress(space32Unsigned, 1); + // this will not be negative, but instead will be large positive + Address addrLargePositive = new GenericAddress(space32Unsigned, -2); + + // test both directions for all combinations of the 4 addresses + assertEquals(-1, addr0.compareTo(addrMax)); + assertEquals(1, addrMax.compareTo(addr0)); + + assertEquals(-1, addr0.compareTo(addrPositive)); + assertEquals(1, addrPositive.compareTo(addr0)); + + assertEquals(-1, addr0.compareTo(addrLargePositive)); + assertEquals(1, addrLargePositive.compareTo(addr0)); + + assertEquals(1, addrMax.compareTo(addrPositive)); + assertEquals(-1, addrPositive.compareTo(addrMax)); + + assertEquals(1, addrMax.compareTo(addrLargePositive)); + assertEquals(-1, addrLargePositive.compareTo(addrMax)); + + assertEquals(-1, addrPositive.compareTo(addrLargePositive)); + assertEquals(1, addrLargePositive.compareTo(addrPositive)); + } + + @Test + public void testCompareToWithSigned32AddressSpace() { + AddressSpace space32Signed = + new GenericAddressSpace("test", 32, AddressSpace.TYPE_STACK, 0); + Address addr0 = new GenericAddress(space32Signed, 0); + Address addrMax = space32Signed.getMaxAddress(); + Address addrPositive = new GenericAddress(space32Signed, 1); + Address addrNegative = new GenericAddress(space32Signed, -2); + + // test both directions for all combinations of the 4 addresses + assertEquals(-1, addr0.compareTo(addrMax)); + assertEquals(1, addrMax.compareTo(addr0)); + + assertEquals(-1, addr0.compareTo(addrPositive)); + assertEquals(1, addrPositive.compareTo(addr0)); + + assertEquals(1, addr0.compareTo(addrNegative)); + assertEquals(-1, addrNegative.compareTo(addr0)); + + assertEquals(1, addrMax.compareTo(addrPositive)); + assertEquals(-1, addrPositive.compareTo(addrMax)); + + assertEquals(1, addrMax.compareTo(addrNegative)); + assertEquals(-1, addrNegative.compareTo(addrMax)); + + assertEquals(1, addrPositive.compareTo(addrNegative)); + assertEquals(-1, addrNegative.compareTo(addrPositive)); + } + + @Test + public void testCompareWithSigned64BitAddressSpace() { + AddressSpace space64Signed = + new GenericAddressSpace("test", 64, AddressSpace.TYPE_STACK, 0); + + Address addr0 = new GenericAddress(space64Signed, 0); + Address addrMax = space64Signed.getMaxAddress(); + Address addrPositive = new GenericAddress(space64Signed, 1); + Address addrNegative = new GenericAddress(space64Signed, -2); + + // test both directions for all combinations of the 4 addresses + assertEquals(-1, addr0.compareTo(addrMax)); + assertEquals(1, addrMax.compareTo(addr0)); + + assertEquals(-1, addr0.compareTo(addrPositive)); + assertEquals(1, addrPositive.compareTo(addr0)); + + assertEquals(1, addr0.compareTo(addrNegative)); + assertEquals(-1, addrNegative.compareTo(addr0)); + + assertEquals(1, addrMax.compareTo(addrPositive)); + assertEquals(-1, addrPositive.compareTo(addrMax)); + + assertEquals(1, addrMax.compareTo(addrNegative)); + assertEquals(-1, addrNegative.compareTo(addrMax)); + + assertEquals(1, addrPositive.compareTo(addrNegative)); + assertEquals(-1, addrNegative.compareTo(addrPositive)); + } + + @Test + public void testCompareWithUnSigned64BitAddressSpace() { + AddressSpace space64Signed = new GenericAddressSpace("test", 64, AddressSpace.TYPE_CODE, 0); + + Address addr0 = new GenericAddress(space64Signed, 0); + Address addrMax = space64Signed.getMaxAddress(); + Address addrPositive = new GenericAddress(space64Signed, 1); + // since this is unsigned space the following will actually be a large positive value + Address addrLarge = new GenericAddress(space64Signed, -2); + + assertEquals(-1, addr0.compareTo(addrMax)); + assertEquals(1, addrMax.compareTo(addr0)); + + assertEquals(-1, addr0.compareTo(addrPositive)); + assertEquals(1, addrPositive.compareTo(addr0)); + + assertEquals(-1, addr0.compareTo(addrLarge)); + assertEquals(1, addrLarge.compareTo(addr0)); + + assertEquals(1, addrMax.compareTo(addrPositive)); + assertEquals(-1, addrPositive.compareTo(addrMax)); + + assertEquals(1, addrMax.compareTo(addrLarge)); + assertEquals(-1, addrLarge.compareTo(addrMax)); + + assertEquals(-1, addrPositive.compareTo(addrLarge)); + assertEquals(1, addrLarge.compareTo(addrPositive)); + } + @Test public void testEquals() { Address addr1 = new GenericAddress(space, 10); From 99d28b076bc268cf9015aa0d0800f599904f148a Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 2 Aug 2021 15:41:27 -0400 Subject: [PATCH 09/10] GP-1058: Fix after review suggestion. --- .../src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java index 0d82d0f200..bacf1baa9e 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java @@ -56,7 +56,7 @@ public enum BytesPcodeArithmetic implements PcodeArithmetic { @Override public byte[] binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, byte[] in1, int sizein2, byte[] in2) { - if (sizein1 > 8 || sizeout > 8) { + if (sizein1 > 8 || sizein2 > 8 || sizeout > 8) { BigInteger in1Val = Utils.bytesToBigInteger(in1, sizein1, isBigEndian, false); BigInteger in2Val = Utils.bytesToBigInteger(in2, sizein2, isBigEndian, false); BigInteger outVal = op.evaluateBinary(sizeout, sizein1, in1Val, in2Val); From 0a24532bf7b5a0866c95f8052ffe665e08969b51 Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Mon, 2 Aug 2021 15:58:37 -0400 Subject: [PATCH 10/10] GP-1110 Refactor ElfDefaultGotPltMarkup.processDynamicPLTGOT implementation, Correct duplicate ELF relocation table processing, and other minor ELF cleanup --- .../app/util/bin/format/elf/ElfConstants.java | 14 - .../format/elf/ElfDefaultGotPltMarkup.java | 395 +++++++++++++----- .../app/util/bin/format/elf/ElfHeader.java | 5 + .../util/bin/format/elf/ElfLoadHelper.java | 15 + .../elf/relocation/ElfRelocationHandler.java | 4 +- .../app/util/opinion/ElfProgramBuilder.java | 18 + .../format/elf/extend/MIPS_ElfExtension.java | 10 +- .../elf/extend/PowerPC64_ElfExtension.java | 10 +- .../elf/extend/X86_32_ElfExtension.java | 5 +- 9 files changed, 346 insertions(+), 130 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java index 44956e2a04..6f89d99c02 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java @@ -512,18 +512,4 @@ public interface ElfConstants { /** used by NetBSD/avr32 - AVR 32-bit */ public static final short EM_AVR32_unofficial = 0x18ad; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * The size in bytes of the entry in the program - * location table (PLT). - */ - public static final int PLT_ENTRY_SIZE = 0x10; - - /** - * The size in bytes of the entry in the program - * location table (PLT) in ARM files. - */ - //public static final int PLT_ENTRY_SIZE_ARM = 0x12; - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java index 51b5a43572..3c69648868 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java @@ -15,10 +15,10 @@ */ package ghidra.app.util.bin.format.elf; +import java.util.*; + import ghidra.app.cmd.refs.RemoveReferenceCmd; -import ghidra.framework.store.LockException; import ghidra.program.disassemble.Disassembler; -import ghidra.program.disassemble.DisassemblerMessageListener; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.*; @@ -38,6 +38,9 @@ import ghidra.util.task.TaskMonitor; */ public class ElfDefaultGotPltMarkup { + // When PLT head is known and named sections are missing this label will be placed at head of PLT + private static final String PLT_HEAD_SYMBOL_NAME = "__PLT_HEAD"; + private ElfLoadHelper elfLoadHelper; private ElfHeader elf; private Program program; @@ -58,7 +61,7 @@ public class ElfDefaultGotPltMarkup { public void process(TaskMonitor monitor) throws CancelledException { if (elf.e_shnum() == 0) { - processDynamicPLTGOT(monitor); + processDynamicPLTGOT(ElfDynamicType.DT_PLTGOT, ElfDynamicType.DT_JMPREL, monitor); } else { processGOTSections(monitor); @@ -87,30 +90,55 @@ public class ElfDefaultGotPltMarkup { } } + private static class PltGotSymbol implements Comparable { + final ElfSymbol elfSymbol; + final long offset; + + PltGotSymbol(ElfSymbol elfSymbol, long offset) { + this.elfSymbol = elfSymbol; + this.offset = offset; + } + + @Override + public int compareTo(PltGotSymbol o) { + return Long.compareUnsigned(offset, o.offset); + } + } + + // When scanning PLT for symbols the min/max entry size are used to control the search + private static final int MAX_SUPPORTED_PLT_ENTRY_SIZE = 32; + private static final int MIN_SUPPORTED_PLT_ENTRY_SIZE = 8; + + // When scanning PLT for symbol spacing this is the threashold used to stop the search + // when the same spacing size is detected in an attempt to identify the PLT entry size + private static final int PLT_SYMBOL_SAMPLE_COUNT_THRESHOLD = 10; + /** - * Process GOT table specified by Dynamic Program Header (DT_PLTGOT). - * Entry count determined by corresponding relocation table identified by - * the dynamic table entry DT_JMPREL. + * Process GOT and associated PLT based upon specified dynamic table entries. + * The primary goal is to identify the bounds of the GOT and PLT and process + * any external symbols which may be defined within the PLT. Processing of PLT + * is only critical if it contains external symbols which must be processed, otherwise + * they will likely resolve adequately during subsequent analysis. + * @param pltGotType dynamic type for dynamic PLTGOT lookup (identifies dynamic PLTGOT) + * @param pltGotRelType dynamic type for associated dynamic JMPREL lookup (identifies dynamic PLTGOT relocation table) * @param monitor task monitor * @throws CancelledException thrown if task cancelled */ - private void processDynamicPLTGOT(TaskMonitor monitor) throws CancelledException { + private void processDynamicPLTGOT(ElfDynamicType pltGotType, ElfDynamicType pltGotRelType, + TaskMonitor monitor) throws CancelledException { ElfDynamicTable dynamicTable = elf.getDynamicTable(); - if (dynamicTable == null || !dynamicTable.containsDynamicValue(ElfDynamicType.DT_PLTGOT) || - !dynamicTable.containsDynamicValue(ElfDynamicType.DT_JMPREL)) { + if (dynamicTable == null || !dynamicTable.containsDynamicValue(pltGotType) || + !dynamicTable.containsDynamicValue(pltGotRelType)) { return; } - // NOTE: there may be other relocation table affecting the GOT - // corresponding to DT_PLTGOT - AddressSpace defaultSpace = program.getAddressFactory().getDefaultAddressSpace(); long imageBaseAdj = elfLoadHelper.getImageBaseWordAdjustmentOffset(); try { long relocTableAddr = - elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(ElfDynamicType.DT_JMPREL)); + elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(pltGotRelType)); ElfProgramHeader relocTableLoadHeader = elf.getProgramLoadHeaderContaining(relocTableAddr); @@ -123,44 +151,199 @@ public class ElfDefaultGotPltMarkup { return; } - // External dynamic symbol entries in the PLTGOT, if any, will be placed - // after any local symbol entries. + // External dynamic symbol entries in the GOT, if any, will be placed + // after any local symbol entries. Local entries are assumed to have original + // bytes of zero, whereas non-local entries will refer to the PLT - // While DT_PLTGOT identifies the start of the PLTGOT it does not - // specify its length. If there are dynamic non-local entries in the - // PLTGOT they should have relocation entries in the table identified - // by DT_JMPREL. It is important to note that this relocation table - // can include entries which affect other processor-specific PLTGOT - // tables (e.g., MIPS_PLTGOT) so we must attempt to isolate the - // entries which correspond to DT_PLTGOT. - - // WARNING: This implementation makes a potentially bad assumption that - // the last relocation entry will identify the endof the PLTGOT if its - // offset is beyond the start of the PLTGOT. This assumption could - // easily be violated by a processor-specific PLTGOT which falls after - // the standard PLTGOT in memory and shares the same relocation table. + // While the dynamic value for pltGotType (e.g., DT_PLTGOT) identifies the start of + // dynamic GOT table it does not specify its length. The associated relocation + // table, identified by the dynamic value for pltGotRelType, will have a relocation + // record for each PLT entry linked via the GOT. The number of relocations matches + // the number of PLT entries and the one with the greatest offset correspionds + // to the last GOT entry. Unfortuntely, the length of each PLT entry and initial + // PLT head is unknown. If the binary has not placed external symbols within the PLT + // processing and disassembly of the PLT may be skipped. long pltgot = elf.adjustAddressForPrelink( - dynamicTable.getDynamicValue(ElfDynamicType.DT_PLTGOT)); + dynamicTable.getDynamicValue(pltGotType)); + Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj); ElfRelocation[] relocations = relocationTable.getRelocations(); - - long lastGotOffset = relocations[relocations.length - 1].getOffset(); - if (lastGotOffset < pltgot) { + ElfSymbolTable associatedSymbolTable = relocationTable.getAssociatedSymbolTable(); + if (associatedSymbolTable == null) { return; } - Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj); - Address gotEnd = defaultSpace.getAddress(lastGotOffset + imageBaseAdj); + // Create ordered list of PLTGOT symbols based upon offset with GOT. + // It assumed that the PLT entry sequence will match this list. + ElfSymbol[] symbols = associatedSymbolTable.getSymbols(); + List pltGotSymbols = new ArrayList<>(); + for (ElfRelocation reloc : relocations) { + pltGotSymbols + .add(new PltGotSymbol(symbols[reloc.getSymbolIndex()], reloc.getOffset())); + } + Collections.sort(pltGotSymbols); + + // Identify end of GOT table based upon relocation offsets + long maxGotOffset = pltGotSymbols.get(pltGotSymbols.size() - 1).offset; + Address gotEnd = defaultSpace.getAddress(maxGotOffset + imageBaseAdj); processGOT(gotStart, gotEnd, monitor); - processDynamicPLT(gotStart, gotEnd, monitor); + + // + // Examine the first two GOT entries which correspond to the relocations (i.e., pltGotSymbols). + // An adjusted address from the original bytes is computed. These will point into the PLT. + // These two pointers will either refer to the same address (i.e., PLT head) or different + // addresses which correspond to the first two PLT entries. While likely offcut into each PLT + // entry, the differing PLT addresses can be used to identify the PLT entry size/spacing but + // not the top of PLT. If symbols are present within the PLT for each entry, they may + // be used to identify the PLT entry size/spacing and will be converted to external symbols. + // + + long pltEntryCount = pltGotSymbols.size(); + + // Get original bytes, converted to addresses, for first two PLT/GOT symbols + Address pltAddr1 = null; + Address pltAddr2 = null; + for (PltGotSymbol pltGotSym : pltGotSymbols) { + Address gotEntryAddr = defaultSpace.getAddress(pltGotSym.offset + imageBaseAdj); + long originalGotEntry = elfLoadHelper.getOriginalValue(gotEntryAddr, true); + if (originalGotEntry == 0) { + return; // unexpected original bytes for PLTGOT entry - skip PLT processing + } + if (pltAddr1 == null) { + pltAddr1 = defaultSpace.getAddress(originalGotEntry + imageBaseAdj); + } + else { + pltAddr2 = defaultSpace.getAddress(originalGotEntry + imageBaseAdj); + break; + } + } + if (pltAddr2 == null) { + return; // unable to find two GOT entries which refer to PLT - skip PLT processing + } + + // NOTE: This approach assumes that all PLT entries have the same structure (i.e., instruction sequence) + long pltSpacing = pltAddr2.subtract(pltAddr1); + if (pltSpacing < 0 || pltSpacing > MAX_SUPPORTED_PLT_ENTRY_SIZE || + (pltSpacing % 2) != 0) { + return; // unsupported PLT entry size - skip PLT processing + } + + Address minSymbolSearchAddress; + long symbolSearchSpacing; // nominal PLT entry size for computing maxSymbolSearchAddress + + Address firstPltEntryAddr = null; // may be offcut within first PLT entry + + if (pltSpacing == 0) { // Entries have same original bytes which refer to PLT head + Function pltHeadFunc = elfLoadHelper.createOneByteFunction(null, pltAddr1, false); + if (pltHeadFunc.getSymbol().getSource() == SourceType.DEFAULT) { + try { + pltHeadFunc.setName(PLT_HEAD_SYMBOL_NAME, SourceType.ANALYSIS); + } + catch (DuplicateNameException | InvalidInputException e) { + // Ignore - unexpected + } + } + + // PLT spacing is not known. pltAddr1 is PLT head + minSymbolSearchAddress = pltAddr1.next(); + + // Use conservative PLT entry size when computing address limit for PLT symbol search. + // For a PLT with an actual entry size of 16 this will reduce the scan to less than half + // of the PLT. This should only present an issue for very small PLTs or those + // with sparsely placed symbols. + symbolSearchSpacing = MIN_SUPPORTED_PLT_ENTRY_SIZE; + } + else { + // PLT spacing is known, but start of entry and head is not known. pltAddr1 points to middle of first PLT entry (not head). + firstPltEntryAddr = pltAddr1; + minSymbolSearchAddress = pltAddr1.subtract(pltSpacing - 1); // try to avoid picking up symbol which may be at head + symbolSearchSpacing = pltSpacing; + } + + // Attempt to find symbols located within the PLT. + Address maxSymbolSearchAddress = + minSymbolSearchAddress.add(pltEntryCount * symbolSearchSpacing); + + // Scan symbols within PLT; helps to identify start of first entry and PLT entry size/spacing if unknown + Symbol firstSymbol = null; + Symbol lastSymbol = null; + long discoveredPltSpacing = Long.MAX_VALUE; + Map spacingCounts = new HashMap<>(); + for (Symbol sym : elfLoadHelper.getProgram() + .getSymbolTable() + .getSymbolIterator(minSymbolSearchAddress, true)) { + if (sym.getSource() == SourceType.DEFAULT) { + continue; + } + Address addr = sym.getAddress(); + if (addr.compareTo(maxSymbolSearchAddress) > 0) { + break; + } + if (firstSymbol == null) { + firstSymbol = sym; + } + if (pltSpacing == 0) { + // Collect spacing samples if PLT spacing is unknown + if (lastSymbol != null) { + long spacing = addr.subtract(lastSymbol.getAddress()); + if (spacing > MAX_SUPPORTED_PLT_ENTRY_SIZE) { + lastSymbol = null; // reset on large symbol spacing + continue; + } + int count = + spacingCounts.compute(spacing, (k, v) -> (v == null) ? 1 : v + 1); + discoveredPltSpacing = Math.min(discoveredPltSpacing, spacing); + if (count == PLT_SYMBOL_SAMPLE_COUNT_THRESHOLD) { + break; // stop on 10 occurances of the same spacing (rather arbitrary sample limit) + } + } + lastSymbol = sym; + } + } + + if (pltSpacing == 0) { + if (discoveredPltSpacing == Long.MAX_VALUE || + spacingCounts.get(discoveredPltSpacing) == 1) { // NOTE: required number of symbol-spacing samples could be increased from 1 + return; // PLT spacing not determined / too large or insufficient PLT symbols - skip PLT processing + } + pltSpacing = discoveredPltSpacing; + } + + if (firstSymbol != null) { + // use PLT symbol if found to identify start of first PLT entry + int firstSymbolEntryIndex = -1; + Address firstSymbolAddr = firstSymbol.getAddress(); + int entryIndex = 0; + for (PltGotSymbol entrySymbol : pltGotSymbols) { + if (firstSymbolAddr + .equals(elfLoadHelper.getElfSymbolAddress(entrySymbol.elfSymbol))) { + firstSymbolEntryIndex = entryIndex; + break; + } + ++entryIndex; + } + if (firstSymbolEntryIndex >= 0) { + firstPltEntryAddr = firstSymbolAddr; + if (firstSymbolEntryIndex > 0) { + firstPltEntryAddr = + firstPltEntryAddr.subtract(firstSymbolEntryIndex * pltSpacing); + } + } + } + + if (firstPltEntryAddr == null) { + return; // failed to identify first PLT entry - skip PLT processing + } + + Address pltEnd = firstPltEntryAddr.add(pltSpacing * (pltEntryCount - 1)); + processLinkageTable("PLT", firstPltEntryAddr, pltEnd, monitor); } - catch (NotFoundException e) { - throw new AssertException(e); - } - catch (AddressOutOfBoundsException e) { - log("Failed to process GOT: " + e.getMessage()); + catch (Exception e) { + String msg = "Failed to process " + pltGotType + ": " + e.getMessage(); + log(msg); + Msg.error(this, msg, e); } } @@ -175,20 +358,47 @@ public class ElfDefaultGotPltMarkup { private void processGOT(Address gotStart, Address gotEnd, TaskMonitor monitor) throws CancelledException { - boolean imageBaseAlreadySet = elf.isPreLinked(); + // Bail if GOT was previously marked-up or not within initialized memory + MemoryBlock block = memory.getBlock(gotStart); + if (block == null || !block.isInitialized()) { + return; // unsupported memory region - skip GOT processing + } + Data data = program.getListing().getDataAt(gotStart); + if (data == null || !Undefined.isUndefined(data.getDataType())) { + return; // evidence of prior markup - skip GOT processing + } try { - Address newImageBase = null; - while (gotStart.compareTo(gotEnd) <= 0) { - monitor.checkCanceled(); + // Fixup first GOT entry which frequently refers to _DYNAMIC but generally lacks relocation (e.g. .got.plt) + ElfDynamicTable dynamicTable = elf.getDynamicTable(); + long imageBaseAdj = elfLoadHelper.getImageBaseWordAdjustmentOffset(); + if (dynamicTable != null && imageBaseAdj != 0) { + long entry1Value = elfLoadHelper.getOriginalValue(gotStart, false); + if (entry1Value == dynamicTable.getAddressOffset()) { + // TODO: record artificial relative relocation for reversion/export concerns + entry1Value += imageBaseAdj; // adjust first entry value + if (elf.is64Bit()) { + memory.setLong(gotStart, entry1Value); + } + else { + memory.setInt(gotStart, (int) entry1Value); + } + } + } - Data data = createPointer(gotStart, true); + boolean imageBaseAlreadySet = elf.isPreLinked(); + + Address newImageBase = null; + Address nextGotAddr = gotStart; + while (nextGotAddr.compareTo(gotEnd) <= 0) { + + data = createPointer(nextGotAddr, true); if (data == null) { break; } try { - gotStart = data.getMaxAddress().add(1); + nextGotAddr = data.getMaxAddress().add(1); } catch (AddressOutOfBoundsException e) { break; // no more room @@ -208,20 +418,20 @@ public class ElfDefaultGotPltMarkup { } } } - catch (CodeUnitInsertionException e) { - log("Failed to process GOT: " + e.getMessage()); - } - catch (AddressOverflowException e) { - log("Failed to adjust image base: " + e.getMessage()); - } - catch (LockException e) { - throw new AssertException(e); + catch (Exception e) { + String msg = "Failed to process GOT at " + gotStart + ": " + e.getMessage(); + log(msg); + Msg.error(this, msg, e); } } private void processPLTSection(TaskMonitor monitor) throws CancelledException { - // TODO: Handle case where PLT is non-executable pointer table + // TODO: May want to consider using analysis to fully disassemble PLT, we only + // really need to migrate external symbols contained within the PLT + + // FIXME: Code needs help ... bad assumption about PLT head size (e.g., 16) + int assumedPltHeadSize = 16; if (elf.isRelocatable()) { return; //relocatable files do not have .PLT sections @@ -229,17 +439,15 @@ public class ElfDefaultGotPltMarkup { MemoryBlock pltBlock = memory.getBlock(ElfSectionHeaderConstants.dot_plt); // TODO: This is a band-aid since there are many PLT implementations and this assumes only one. - if (pltBlock == null || !pltBlock.isExecute() || - pltBlock.getSize() <= ElfConstants.PLT_ENTRY_SIZE) { + if (pltBlock == null || !pltBlock.isExecute() || pltBlock.getSize() <= assumedPltHeadSize) { return; } - int skipPointers = ElfConstants.PLT_ENTRY_SIZE; + int skipPointers = assumedPltHeadSize; // ARM, AARCH64 and others may not store pointers at start of .plt if (elf.e_machine() == ElfConstants.EM_ARM || elf.e_machine() == ElfConstants.EM_AARCH64) { - // TODO: Should be handled by extension - skipPointers = 0; + skipPointers = 0; // disassemble entire PLT } // Process PLT section @@ -248,37 +456,6 @@ public class ElfDefaultGotPltMarkup { processLinkageTable(ElfSectionHeaderConstants.dot_plt, minAddress, maxAddress, monitor); } - private void processDynamicPLT(Address gotStart, Address gotEnd, TaskMonitor monitor) - throws CancelledException { - - Address pltStart = null; - Address pltEnd = null; - - for (Data gotPtr : listing.getDefinedData(new AddressSet(gotStart.next(), gotEnd), true)) { - monitor.checkCanceled(); - if (!gotPtr.isPointer()) { - Msg.error(this, "ELF PLTGOT contains non-pointer"); - return; // unexpected - } - Address ptr = (Address) gotPtr.getValue(); - if (ptr.getOffset() == 0) { - continue; - } - MemoryBlock block = memory.getBlock(ptr); - if (block == null || block.getName().equals(MemoryBlock.EXTERNAL_BLOCK_NAME)) { - continue; - } - if (pltStart == null) { - pltStart = ptr; - } - pltEnd = ptr; - } - - if (pltStart != null) { - processLinkageTable("PLT", pltStart, pltEnd, monitor); - } - } - /** * Perform disassembly and markup of specified external linkage table which * consists of thunks to external functions. If symbols are defined within the @@ -292,16 +469,24 @@ public class ElfDefaultGotPltMarkup { public void processLinkageTable(String pltName, Address minAddress, Address maxAddress, TaskMonitor monitor) throws CancelledException { - // Disassemble section. - // Disassembly is only done so we can see all instructions since many - // of them are unreachable after applying relocations - disassemble(minAddress, maxAddress, program, monitor); + try { + // Disassemble section. + // Disassembly is only done so we can see all instructions since many + // of them are unreachable after applying relocations + disassemble(minAddress, maxAddress, program, monitor); - // Any symbols in the linkage section should be converted to External function thunks - // This can be seen with ARM Android examples. - int count = convertSymbolsToExternalFunctions(minAddress, maxAddress); - if (count > 0) { - log("Converted " + count + " " + pltName + " section symbols to external thunks"); + // Any symbols in the linkage section should be converted to External function thunks + // This can be seen with ARM Android examples. + int count = convertSymbolsToExternalFunctions(minAddress, maxAddress); + if (count > 0) { + log("Converted " + count + " " + pltName + " section symbols to external thunks"); + } + } + catch (Exception e) { + String msg = + "Failed to process " + pltName + " at " + minAddress + ": " + e.getMessage(); + log(msg); + Msg.error(this, msg, e); } } @@ -341,15 +526,13 @@ public class ElfDefaultGotPltMarkup { private void disassemble(Address start, Address end, Program prog, TaskMonitor monitor) throws CancelledException { - DisassemblerMessageListener dml = msg -> { - //don't care... - }; // TODO: Should we restrict disassembly or follows flows? AddressSet set = new AddressSet(start, end); - Disassembler disassembler = Disassembler.getDisassembler(prog, monitor, dml); + Disassembler disassembler = Disassembler.getDisassembler(prog, monitor, m -> { + /* silent */}); while (!set.isEmpty()) { monitor.checkCanceled(); - AddressSet disset = disassembler.disassemble(set.getMinAddress(), set, true); + AddressSet disset = disassembler.disassemble(set.getMinAddress(), null, true); if (disset.isEmpty()) { // Stop on first error but discard error bookmark since // some plt sections are partly empty and must rely @@ -444,6 +627,8 @@ public class ElfDefaultGotPltMarkup { * then the base of the .so is most likely incorrect. Shift it! */ private Address UglyImageBaseCheck(Data data, Address imageBase) { + // TODO: Find sample - e.g., ARM .so - seems too late in import processing to change image base + // if any relocations have been applied. if (elf.e_machine() != ElfConstants.EM_ARM) { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java index 48acd52309..e34edbdb8a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java @@ -530,6 +530,11 @@ public class ElfHeader implements StructConverter, Writeable { } long relocTableOffset = relocTableLoadHeader.getOffset(relocTableAddr); + for (ElfRelocationTable relocTable : relocationTableList) { + if (relocTable.getFileOffset() == relocTableOffset) { + return; // skip reloc table previously parsed + } + } long tableEntrySize = relocEntrySizeType != null ? dynamicTable.getDynamicValue(relocEntrySizeType) : -1; long tableSize = dynamicTable.getDynamicValue(relocTableSizeType); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfLoadHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfLoadHelper.java index 7fedcbb31b..cff05fe218 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfLoadHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfLoadHelper.java @@ -21,6 +21,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.*; +import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Symbol; import ghidra.util.exception.InvalidInputException; @@ -188,4 +189,18 @@ public interface ElfLoadHelper { */ public AddressRange allocateLinkageBlock(int alignment, int size, String purpose); + /** + *

Get the original memory value at the specified address if a relocation was applied at the + * specified address (not containing). Current memory value will be returned if no relocation + * has been applied at specified address. The value size is either 8-bytes if {@link ElfHeader#is64Bit()}, + * otherwise it will be 4-bytes. This is primarily intended to inspect original bytes within + * the GOT which may have had relocations applied to them. + * @param addr memory address + * @param signExtend if true sign-extend to long, else treat as unsigned + * @return original bytes value + * @throws MemoryAccessException if memory read fails + */ + public long getOriginalValue(Address addr, boolean signExtend) + throws MemoryAccessException; + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfRelocationHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfRelocationHandler.java index 44142e1fff..4b35e3aa8a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfRelocationHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/ElfRelocationHandler.java @@ -17,6 +17,8 @@ package ghidra.app.util.bin.format.elf.relocation; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + import ghidra.app.util.bin.format.elf.*; import ghidra.app.util.importer.MessageLog; import ghidra.program.model.address.Address; @@ -125,7 +127,7 @@ abstract public class ElfRelocationHandler implements ExtensionPoint { public static void markAsUnhandled(Program program, Address relocationAddress, long type, long symbolIndex, String symbolName, MessageLog log) { - symbolName = symbolName == null ? "" : symbolName; + symbolName = StringUtils.isEmpty(symbolName) ? "" : symbolName; log.appendMsg("Unhandled Elf Relocation: Type = " + type + " (0x" + Long.toHexString(type) + ") at " + relocationAddress + " (Symbol = " + symbolName + ")"); BookmarkManager bookmarkManager = program.getBookmarkManager(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java index 70647aa43c..c338136c93 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java @@ -43,6 +43,7 @@ import ghidra.program.model.data.DataUtilities.ClearDataMode; import ghidra.program.model.lang.Register; import ghidra.program.model.listing.*; import ghidra.program.model.mem.*; +import ghidra.program.model.reloc.Relocation; import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.scalar.Scalar; import ghidra.program.model.symbol.*; @@ -911,6 +912,23 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { } } + @Override + public long getOriginalValue(Address addr, boolean signExtend) throws MemoryAccessException { + byte[] bytes; + int len = elf.is64Bit() ? 8 : 4; + Relocation relocation = program.getRelocationTable().getRelocation(addr); + if (relocation == null) { + bytes = new byte[len]; + memory.getBytes(addr, bytes); + } + else { + bytes = relocation.getBytes(); + } + DataConverter dataConverter = DataConverter.getInstance(elf.isBigEndian()); + return signExtend ? dataConverter.getSignedValue(bytes, len) + : dataConverter.getValue(bytes, len); + } + /** * Add reference to previously applied header structure (assumes markupElfHeader previously called) * @param componentName diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/extend/MIPS_ElfExtension.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/extend/MIPS_ElfExtension.java index 2e33262ce0..efba3dcb7b 100644 --- a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/extend/MIPS_ElfExtension.java +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/extend/MIPS_ElfExtension.java @@ -727,7 +727,8 @@ public class MIPS_ElfExtension extends ElfExtension { stubsBlock.getEnd(), monitor); } - private void fixupGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) { + private void fixupGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) + throws CancelledException { // see Wiki at https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT // see related doc at https://www.cr0.org/paper/mips.elf.external.resolution.txt @@ -764,6 +765,7 @@ public class MIPS_ElfExtension extends ElfExtension { // process local symbol got entries for (int i = 0; i < gotLocalEntryCount; i++) { + monitor.checkCanceled(); Address gotEntryAddr = adjustTableEntryIfNonZero(gotBaseAddress, i, imageShift, elfLoadHelper); Data pointerData = elfLoadHelper.createData(gotEntryAddr, PointerDataType.dataType); @@ -775,6 +777,7 @@ public class MIPS_ElfExtension extends ElfExtension { // process global/external symbol got entries int gotIndex = gotLocalEntryCount; for (int i = gotSymbolIndex; i < elfSymbols.length; i++) { + monitor.checkCanceled(); Address gotEntryAddr = adjustTableEntryIfNonZero(gotBaseAddress, gotIndex++, imageShift, elfLoadHelper); Data pointerData = elfLoadHelper.createData(gotEntryAddr, PointerDataType.dataType); @@ -795,7 +798,7 @@ public class MIPS_ElfExtension extends ElfExtension { } } - private void fixupMipsGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) { + private void fixupMipsGot(ElfLoadHelper elfLoadHelper, TaskMonitor monitor) throws CancelledException { ElfHeader elfHeader = elfLoadHelper.getElfHeader(); ElfDynamicTable dynamicTable = elfHeader.getDynamicTable(); @@ -830,6 +833,7 @@ public class MIPS_ElfExtension extends ElfExtension { // process local symbol got entries int gotEntryIndex = 1; for (int i = 0; i < gotSymbolIndex; i++) { + monitor.checkCanceled(); if (!elfSymbols[i].isFunction() || elfSymbols[i].getSectionHeaderIndex() != 0) { continue; } @@ -849,6 +853,7 @@ public class MIPS_ElfExtension extends ElfExtension { private Address adjustTableEntryIfNonZero(Address tableBaseAddr, int entryIndex, long adjustment, ElfLoadHelper elfLoadHelper) throws MemoryAccessException { + // TODO: record artificial relative relocation for reversion/export concerns boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit(); Memory memory = elfLoadHelper.getProgram().getMemory(); Address tableEntryAddr; @@ -871,6 +876,7 @@ public class MIPS_ElfExtension extends ElfExtension { private Address setTableEntryIfZero(Address tableBaseAddr, int entryIndex, long value, ElfLoadHelper elfLoadHelper) throws MemoryAccessException { + // TODO: record artificial relative relocation for reversion/export concerns boolean is64Bit = elfLoadHelper.getElfHeader().is64Bit(); Memory memory = elfLoadHelper.getProgram().getMemory(); Address tableEntryAddr; diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java index 8c0e6cab82..87d8e3ab3a 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC64_ElfExtension.java @@ -35,6 +35,9 @@ import ghidra.util.task.TaskMonitor; public class PowerPC64_ElfExtension extends ElfExtension { + private static final int PLT_ENTRY_SIZE = 8; // could be 16(local) or 24 w/ opd_api, 32 for VxWorks + private static final int PLT_HEAD_SIZE = 16; // could be 24 w/ obd_api, 32 for VxWorks + // Elf Dynamic Type Extensions public static final ElfDynamicType DT_PPC64_GLINK = new ElfDynamicType(0x70000000, "DT_PPC64_GLINK", "Specify the start of the .glink section", ElfDynamicValueType.ADDRESS); @@ -225,8 +228,7 @@ public class PowerPC64_ElfExtension extends ElfExtension { } Program program = elfLoadHelper.getProgram(); MemoryBlock pltBlock = program.getMemory().getBlock(pltSection.getNameAsString()); - // TODO: This is a band-aid since there are many PLT implementations and this assumes only one. - if (pltBlock == null || pltBlock.getSize() <= ElfConstants.PLT_ENTRY_SIZE) { + if (pltBlock == null) { return; } if (pltSection.isExecutable()) { @@ -243,14 +245,14 @@ public class PowerPC64_ElfExtension extends ElfExtension { // TODO: Uncertain - Address addr = pltBlock.getStart().add(ElfConstants.PLT_ENTRY_SIZE); + Address addr = pltBlock.getStart().add(PLT_HEAD_SIZE); try { while (addr.compareTo(pltBlock.getEnd()) < 0) { monitor.checkCanceled(); if (elfLoadHelper.createData(addr, PointerDataType.dataType) == null) { break; // stop early if failed to create a pointer } - addr = addr.addNoWrap(8); + addr = addr.addNoWrap(PLT_ENTRY_SIZE); } } catch (AddressOverflowException e) { diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/elf/extend/X86_32_ElfExtension.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/elf/extend/X86_32_ElfExtension.java index b4416e1d87..14e33fcbd2 100644 --- a/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/elf/extend/X86_32_ElfExtension.java +++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/util/bin/format/elf/extend/X86_32_ElfExtension.java @@ -27,8 +27,6 @@ import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class X86_32_ElfExtension extends ElfExtension { - - // TODO: Add extension types @Override public boolean canHandle(ElfHeader elf) { @@ -90,8 +88,7 @@ public class X86_32_ElfExtension extends ElfExtension { // MemoryBlock pltBlock = getBlockPLT(pltSection); MemoryBlock pltBlock = memory.getBlock(pltSection.getNameAsString()); - // TODO: This is a band-aid since there are many PLT implementations and this assumes only one. - if (pltBlock == null || pltBlock.getSize() <= ElfConstants.PLT_ENTRY_SIZE) { + if (pltBlock == null) { return; }