From 87d6ff4db38dfb600934e5d6886b621064acdb55 Mon Sep 17 00:00:00 2001 From: ghidra007 Date: Mon, 24 Jan 2022 21:34:12 +0000 Subject: [PATCH] GP-1670 Fixed bug in a class recovery helper class that in some cases was causing an exception when trying to replace a component in a structure. --- .../classrecovery/EditStructureUtils.java | 53 ++++++++--- .../classrecovery/RTTIGccClassRecoverer.java | 34 +++---- .../RTTIWindowsClassRecoverer.java | 88 +++++++------------ .../classrecovery/RecoveredClassHelper.java | 78 ++++++++-------- 4 files changed, 122 insertions(+), 131 deletions(-) diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java index a1dcb64b75..45e1936775 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/EditStructureUtils.java @@ -226,55 +226,68 @@ class EditStructureUtils { return true; } + /** * Method to add a field to the given structure. If the structure already has data * at the given offset, don't replace. If there is undefined data there then replace * it with the data type. If the structure empty, insert the data type at the given offset. * If the structure is not big enough and not-empty, grow it so there is room to replace. - * See {@link #canAdd(Structure, int, int, TaskMonitor)} for ensuring operation will be + * See {@link #canAdd(Structure, int, int, boolean, TaskMonitor)} for ensuring operation will be * successful. * @param structure the given structure * @param offset the offset to add a field * @param dataType the data type to add to the field at the given offset * @param fieldName the name of field * @param monitor task monitor - * @return the updated structure data type - * @throws IllegalArgumentException if issue inserting data type into structure + * @return true if the structure was updated or false if the data could not be added + * @throws IllegalArgumentException if issue inserting or replacing data type into structure * @throws CancelledException if cancelled */ - static Structure addDataTypeToStructure(Structure structure, int offset, - DataType dataType, String fieldName, TaskMonitor monitor) - throws CancelledException, IllegalArgumentException { - + static boolean addDataTypeToStructure(Structure structure, int offset, DataType dataType, + String fieldName, TaskMonitor monitor) throws CancelledException { + if (structure.isPackingEnabled()) { throw new IllegalArgumentException( "Packed structures are not supported by this method"); } + if (!canAdd(structure, offset, dataType.getLength(), true, monitor)) { + return false; + } + if (structure.isZeroLength() || offset >= structure.getLength()) { structure.insertAtOffset(offset, dataType, -1, fieldName, null); } else { + // if not enough room, grow the structure + int roomForData = structure.getLength() - (offset + dataType.getLength()); + if (roomForData < 0) { + structure.growStructure(-roomForData); + } structure.replaceAtOffset(offset, dataType, -1, fieldName, null); } - return structure; + return true; } + /** * Method to determine if the given structure has room at the given offset to have a component * of the given length added to it. This is only valid for non-packed structures. * @param structureDataType the given structure * @param offset the offset to check for available room * @param lengthToAdd the length of bytes wanted to add at the offset + * @param isGrowthAllowed Whether true or false, adding is only allowed if no collision will + * happen with existing defined components. If true, allows structure to be grown beyond its end + * if necessary. If false, it does not allow structure to be grown. * @param monitor task monitor * @return true if the given structure has room at the given offset to have a component of the - * given length added to it or if the offset is beyond the end of the structure so that the - * structure can be grown + * given length added to it or if the offset is beyond the end of the defined components in the + * the structure so that the structure can be grown * @throws CancelledException if cancelled * @throws IllegalArgumentException if a packed structure is passed in */ static boolean canAdd(Structure structureDataType, int offset, int lengthToAdd, - TaskMonitor monitor) throws CancelledException { + boolean isGrowthAllowed, TaskMonitor monitor) throws CancelledException { if (structureDataType.isPackingEnabled()) { throw new IllegalArgumentException( @@ -283,12 +296,24 @@ class EditStructureUtils { DataTypeComponent component = structureDataType.getComponentContaining(offset); - // structure not big enough to contain the offset so return true so it can be grown + // structure not big enough to contain the offset + // if growStructure flag is true, return true so structure can be grown + // if growStructure flag is false, return false since the offset does not exist so it would + // be impossible to add anything at that offset if (component == null) { - return true; + if (isGrowthAllowed) { + return true; + } + return false; } - // if the offset is in the middle of an internal component then return false + // if growStructure flag is false and if offset + lengthToAdd is greater than length of + // structure then return false + if (!isGrowthAllowed && (structureDataType.getLength() < (offset + lengthToAdd))) { + return false; + } + + // if the offset is in the middle of an internal component then return false if (component.getOffset() != offset) { return false; } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java index 2c419fda54..a121bef4e1 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIGccClassRecoverer.java @@ -2857,14 +2857,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress); // simple case the offset for vftablePtr is 0 - if (EditStructureUtils.canAdd(classStructureDataType, 0, - classVftablePointer.getLength(), - monitor)) { - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - 0, classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); - - } + // if can fit or grow structure, add the vftablePtr to it + EditStructureUtils.addDataTypeToStructure(classStructureDataType, 0, + classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); } // if single inheritance or multi non-virtual (wouldn't have called this method if // it were virtually inherited) put parent struct and data into class struct @@ -2905,13 +2900,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { " : structure should exist but doesn't."); } - if (EditStructureUtils.canAdd(classStructureDataType, parentOffset, - baseClassStructure.getLength(), monitor)) { - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - parentOffset, - baseClassStructure, baseClassStructure.getName(), monitor); - } + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + EditStructureUtils.addDataTypeToStructure(classStructureDataType, parentOffset, + baseClassStructure, baseClassStructure.getName(), monitor); } } @@ -2920,9 +2912,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { int dataOffset = getDataOffset(recoveredClass, classStructureDataType); int dataLen = UNKNOWN; if (dataOffset != NONE) { - dataLen = - EditStructureUtils.getNumberOfUndefinedsStartingAtOffset(classStructureDataType, - dataOffset, monitor); + dataLen = EditStructureUtils.getNumberOfUndefinedsStartingAtOffset( + classStructureDataType, dataOffset, monitor); } if (dataLen != UNKNOWN && dataLen > 0) { @@ -2931,9 +2922,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer { classStructureDataType, dataLen, dataOffset); if (recoveredClassDataStruct != null) { - classStructureDataType = EditStructureUtils.addDataTypeToStructure( - classStructureDataType, - dataOffset, recoveredClassDataStruct, "data", monitor); + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + EditStructureUtils.addDataTypeToStructure(classStructureDataType, dataOffset, + recoveredClassDataStruct, "data", monitor); } } diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java index 2f924d4427..deae839cb6 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RTTIWindowsClassRecoverer.java @@ -1212,7 +1212,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { /** * Method to get class inheritance flag from the RTTIClassHierarchyDescriptor structure * @param classNamespace the given class namespace - * @return the class inheritance flag + * @return the class inheritance flag or NONE if there isn't one * @throws CancelledException if cancelled * @throws MemoryAccessException if memory cannot be read * @throws AddressOutOfBoundsException if try reading memory out of bounds @@ -1432,7 +1432,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { recoveredClass.addParentToBaseTypeMapping(baseClass, false); } else { - if (vbaseOffset == NONE) { vbaseOffset = pdisp; } @@ -2247,7 +2246,6 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { * @return the created class structure data type * @throws Exception if invalid data creation */ - //NEW private Structure createClassStructureUsingRTTI(RecoveredClass recoveredClass, Map vfPointerDataTypes) throws Exception { @@ -2353,38 +2351,29 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { recoveredClass.getVftableAddresses().size() > 1 && recoveredClass.inheritsVirtualAncestor()) { - //NEW - int offsetOfVirtualParent = getSingleVirtualParentOffset(baseClass); + int virtParentOffset = getSingleVirtualParentOffset(baseClass); int dataLength; - if (offsetOfVirtualParent == NONE) { + if (virtParentOffset == NONE) { dataLength = baseClassStructure.getLength(); } else { - int lengthOfVirtualParent = - baseClassStructure.getLength() - offsetOfVirtualParent; - dataLength = baseClassStructure.getLength() - lengthOfVirtualParent; + int virtParentLength = baseClassStructure.getLength() - virtParentOffset; + dataLength = baseClassStructure.getLength() - virtParentLength; } - if (EditStructureUtils.canAdd(classStructureDataType, baseClassOffset, - dataLength, - monitor)) { - classStructureDataType = - addIndividualComponentsToStructure(classStructureDataType, - baseClassStructure, baseClassOffset, offsetOfVirtualParent); - } + // if there is room add the individual parts of the base class from the top of the + // structure up to but not including the single virtual parent offset within + // the class structure + addIndividualComponentsToStructure(classStructureDataType, baseClassStructure, + baseClassOffset, dataLength); continue; } - // else copy whole baseClass structure to the class Structure - if (EditStructureUtils.canAdd(classStructureDataType, baseClassOffset, - baseClassStructure.getLength(), monitor)) { - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - baseClassOffset, - baseClassStructure, baseClassStructure.getName(), monitor); - - } + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + EditStructureUtils.addDataTypeToStructure(classStructureDataType, baseClassOffset, + baseClassStructure, baseClassStructure.getName(), monitor); } else { @@ -2397,15 +2386,11 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { baseClassOffset = api.getInt(recoveredClass.getVbtableAddress().add(vdisp)) + pdisp; - if (EditStructureUtils.canAdd(classStructureDataType, baseClassOffset, - baseClassStructure.getLength(), monitor)) { + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + EditStructureUtils.addDataTypeToStructure(classStructureDataType, + baseClassOffset, baseClassStructure, baseClassStructure.getName(), monitor); - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - baseClassOffset, - baseClassStructure, baseClassStructure.getName(), monitor); - - } } }// end of base class array @@ -2430,13 +2415,10 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress); - if (EditStructureUtils.canAdd(classStructureDataType, offset.intValue(), - classVftablePointer.getLength(), monitor)) { - classStructureDataType = EditStructureUtils.addDataTypeToStructure( - classStructureDataType, - offset.intValue(), classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); - - } + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + EditStructureUtils.addDataTypeToStructure(classStructureDataType, + offset.intValue(), classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); } // add the vbtable structure for single inheritance/virt parent case @@ -2445,13 +2427,11 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { addVbtableToClassStructure(recoveredClass, classStructureDataType, false); } - //NEW int dataOffset = getDataOffset(recoveredClass, classStructureDataType); int dataLen = UNKNOWN; if (dataOffset != NONE) { - dataLen = - EditStructureUtils.getNumberOfUndefinedsStartingAtOffset(classStructureDataType, - dataOffset, monitor); + dataLen = EditStructureUtils.getNumberOfUndefinedsStartingAtOffset( + classStructureDataType, dataOffset, monitor); } if (dataLen != UNKNOWN && dataLen > 0) { @@ -2460,23 +2440,22 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { classStructureDataType, dataLen, dataOffset); if (recoveredClassDataStruct != null) { - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - dataOffset, recoveredClassDataStruct, - classStructureDataType.getName() + "_data", monitor); + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + EditStructureUtils.addDataTypeToStructure(classStructureDataType, dataOffset, + recoveredClassDataStruct, classStructureDataType.getName() + "_data", monitor); } } - //NEW: classStructureDataType = addClassVftables(classStructureDataType, recoveredClass, vfPointerDataTypes); - //NEW: classStructureDataType = addVbtableToClassStructure(recoveredClass, classStructureDataType, true); - if (classStructureDataType.getNumComponents() == classStructureDataType.getNumDefinedComponents()) { + if (classStructureDataType.getNumComponents() == classStructureDataType + .getNumDefinedComponents()) { classStructureDataType.setPackingEnabled(true); } @@ -2491,8 +2470,9 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { /** * Method to return the offset of the given class's single virtual parent * @param recoveredClass the given class - * @return the offset of the single virtual parent or null if there is not a single virtual parent - * or if there is no mapping in the offset map for that parent + * @return the offset in the given class structure of the classes single virtual parent or NONE + * if cannot retrieve an offset value or if there is not a single virtual parent for the given + * class. * @throws CancelledException if cancelled * @throws AddressOutOfBoundsException if trying to access an address that does not exist in program * @throws MemoryAccessException if trying to access memory that can't be accessed @@ -2502,7 +2482,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer { List virtualParentClasses = getVirtualParentClasses(recoveredClass); if (virtualParentClasses.size() != 1) { - return null; + return NONE; } Map parentOffsetMap = getBaseClassOffsetMap(recoveredClass); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java index 8c47f4986d..c5c75b6926 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/RecoveredClassHelper.java @@ -5489,36 +5489,39 @@ public class RecoveredClassHelper { } /** - * Method to add the given structcure component to the given structure at the given offset - * @param structureDataType the structure to add to - * @param structureToAdd the structure to add - * @param startOffset the starting offset where to add - * @param endOffset the ending offset of the added structure - * @return the updated structure + * Method to add the structure components from the given structureToAdd from the given starting + * offset to the given ending offset of the to the given structure at the given offset + * @param structure the structure to add to + * @param structureToAdd the structure to add a subset of components from to the given structure + * @param startOffset the starting offset of where to start adding in the container structure + * @param dataLength the total length of the data components to add + * @return true if the structure was updated or false if the components cannot be added * @throws CancelledException if cancelled */ - public Structure addIndividualComponentsToStructure(Structure structureDataType, - Structure structureToAdd, int startOffset, int endOffset) throws CancelledException { + public boolean addIndividualComponentsToStructure(Structure structure, Structure structureToAdd, + int startOffset, int dataLength) throws CancelledException { - DataTypeComponent[] definedComponents = structureToAdd.getDefinedComponents(); - for (int ii = 0; ii < definedComponents.length; ii++) { + // this check does not allow growing of structure. It only allows adding to the structure if + // the block of components to add will fit at the given offset + if (!EditStructureUtils.canAdd(structure, startOffset, dataLength, false, + monitor)) { + return false; + } + + for (DataTypeComponent dataTypeComponent : structureToAdd.getDefinedComponents()) { monitor.checkCanceled(); - DataTypeComponent dataTypeComponent = structureToAdd.getComponent(ii); - - int dataComponentOffset = dataTypeComponent.getOffset(); - if (endOffset != NONE && (dataComponentOffset + startOffset) >= endOffset) { - return structureDataType; + // only copy the components up to the given total dataLength to copy + if ((dataTypeComponent.getOffset() + dataTypeComponent.getLength()) > dataLength) { + break; } - String fieldname = dataTypeComponent.getFieldName(); - - structureDataType = EditStructureUtils.addDataTypeToStructure(structureDataType, - startOffset + dataComponentOffset, dataTypeComponent.getDataType(), fieldname, - monitor); + structure.replaceAtOffset(startOffset + dataTypeComponent.getOffset(), + dataTypeComponent.getDataType(), -1, dataTypeComponent.getFieldName(), + dataTypeComponent.getComment()); } - return structureDataType; + return true; } @@ -6193,7 +6196,7 @@ public class RecoveredClassHelper { * Method to retrieve the offset of the class data in the given structure * @param recoveredClass the given class * @param structure the given structure - * @return the offset of the class data in the given structure + * @return the offset of the class data in the given structure or NONE if there isn't one * @throws CancelledException if cancelled */ public int getDataOffset(RecoveredClass recoveredClass, Structure structure) @@ -8204,22 +8207,17 @@ public class RecoveredClassHelper { monitor.checkCanceled(); - // if enough empty bytes - add class vftable pointer - if (EditStructureUtils.canAdd(classStructureDataType, vftableOffset, - classVftablePointer.getLength(), monitor)) { - - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - vftableOffset, - classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); - + // if enough empty bytes or can grow the structure - add class vftable pointer + boolean addedToStructure = + EditStructureUtils.addDataTypeToStructure(classStructureDataType, vftableOffset, + classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor); + if (addedToStructure) { addedVftablePointer = true; continue; } // if already has a base class vftable pointer replace with main class vftablePtr // get the item at that location - //NEW: replaced with get containing // if offset is in the middle, get the top of the component DataTypeComponent currentComponent = classStructureDataType.getComponentContaining(vftableOffset); @@ -8298,16 +8296,12 @@ public class RecoveredClassHelper { DataType vbaseStructPointer = dataTypeManager.getPointer(vbtableStructure); - int dataLength = vbaseStructPointer.getLength(); - if (EditStructureUtils.canAdd(classStructureDataType, vbtableOffset, dataLength, - monitor)) { - - classStructureDataType = - EditStructureUtils.addDataTypeToStructure(classStructureDataType, - vbtableOffset, vbaseStructPointer, VBTABLE_PTR, monitor); - - } - else if (overwrite) { + // if it fits at offset or is at the end and class structure can be grown, + // copy the whole baseClass structure to the class Structure at the given offset + boolean addedToStructure = EditStructureUtils.addDataTypeToStructure( + classStructureDataType, vbtableOffset, vbaseStructPointer, VBTABLE_PTR, monitor); + if (!addedToStructure && overwrite && classStructureDataType + .getLength() >= (vbtableOffset + vbaseStructPointer.getLength())) { classStructureDataType.replaceAtOffset(vbtableOffset, vbaseStructPointer, vbaseStructPointer.getLength(), VBTABLE_PTR, ""); }