mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
402 lines
13 KiB
Java
402 lines
13 KiB
Java
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
|
|
package classrecovery;
|
|
|
|
import java.util.*;
|
|
|
|
import ghidra.program.model.data.*;
|
|
import ghidra.util.exception.CancelledException;
|
|
import ghidra.util.task.TaskMonitor;
|
|
|
|
class EditStructureUtils {
|
|
|
|
private EditStructureUtils() {
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to determine if the given containing struct has components at the given offset
|
|
* that are either the same, all undefined1's or equivalently sized undefineds as the components
|
|
* in the given possible newInternalStruct
|
|
* @param containingStruct the given structure to check
|
|
* @param offset the offset at the containing struct to check for equivalent components
|
|
* @param newInternalStruct the possible new internal struct to replace at the given offset in the
|
|
* given containing struct
|
|
* @param monitor task monitor
|
|
* @return true if components in the containing struct are replaceable with the possible new
|
|
* internal struct, false otherwise
|
|
* @throws CancelledException if cancelled
|
|
*/
|
|
static boolean hasReplaceableComponentsAtOffset(Structure containingStruct, int offset,
|
|
Structure newInternalStruct, TaskMonitor monitor) throws CancelledException {
|
|
|
|
DataTypeComponent[] newStructComponents = newInternalStruct.getComponents();
|
|
|
|
for (DataTypeComponent newStructComponent : newStructComponents) {
|
|
|
|
monitor.checkCanceled();
|
|
|
|
int structOffset = newStructComponent.getOffset();
|
|
|
|
DataTypeComponent currentComponentAtOffset =
|
|
containingStruct.getComponentAt(offset + structOffset);
|
|
|
|
DataType newStructCompDt = newStructComponent.getDataType();
|
|
DataType containingComDt = currentComponentAtOffset.getDataType();
|
|
|
|
// if component dts are equal continue
|
|
if (newStructCompDt.equals(containingComDt)) {
|
|
continue;
|
|
}
|
|
|
|
// if containing is all undefined1s at equivalent location then continue
|
|
if (hasEnoughUndefined1sAtOffset(containingStruct, offset + structOffset,
|
|
newStructCompDt.getLength(), monitor)) {
|
|
continue;
|
|
}
|
|
|
|
// otherwise if lengths not equal then return false
|
|
if (newStructCompDt.getLength() != containingComDt.getLength()) {
|
|
return false;
|
|
}
|
|
|
|
// component lengths are equal if it gets here
|
|
// if containing is not undefined then return false because the components would be
|
|
// incompatible types
|
|
|
|
if (!Undefined.isUndefined(containingComDt)) {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Method to determine if there are at least the given length of undefined size 1 components at the given offset in the given structure
|
|
* @param structure the given structure
|
|
* @param offset the given offset
|
|
* @param length the length of undefine size 1 components to check for starting at given offset
|
|
* @param monitor task monitor
|
|
* @return true if there are at least length undefined size 1 components at the given offset in the given structure
|
|
* @throws CancelledException if cancelled
|
|
*/
|
|
static boolean hasEnoughUndefined1sAtOffset(Structure structure, int offset, int length,
|
|
TaskMonitor monitor) throws CancelledException {
|
|
|
|
if (structure.getLength() < offset + length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = offset; i < offset + length; i++) {
|
|
monitor.checkCanceled();
|
|
DataTypeComponent component = structure.getComponentAt(i);
|
|
if (component == null) {
|
|
return false;
|
|
}
|
|
DataType dataType = component.getDataType();
|
|
if (dataType == DataType.DEFAULT) {
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Method to check that all components starting with the one at the given offset and encompassing
|
|
* all up to the given length from the given offset are an undefined data type. If so, clear them all and return true.
|
|
* If not, do nothing and return false.
|
|
* @param structure the given structure
|
|
* @param offset the given offset in the structure
|
|
* @param length the total length from the offset to hopefully clear
|
|
* @param monitor task monitor
|
|
* @return true if successfully cleared from offset to offset+length, false otherwise
|
|
* @throws CancelledException if cancelled
|
|
*/
|
|
static boolean clearLengthAtOffset(Structure structure, int offset, int length,
|
|
TaskMonitor monitor) throws CancelledException {
|
|
|
|
if (structure.getLength() < offset + length) {
|
|
return false;
|
|
}
|
|
|
|
List<Integer> offsetsToClear = new ArrayList<Integer>();
|
|
|
|
int endOfClear = offset + length;
|
|
|
|
while (offset < endOfClear) {
|
|
|
|
monitor.checkCanceled();
|
|
|
|
DataTypeComponent component = structure.getComponentContaining(offset);
|
|
|
|
offsetsToClear.add(component.getOffset());
|
|
offset = component.getOffset() + component.getLength();
|
|
|
|
}
|
|
|
|
if (offsetsToClear.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
Iterator<Integer> offsetIterator = offsetsToClear.iterator();
|
|
while (offsetIterator.hasNext()) {
|
|
Integer componentOffset = offsetIterator.next();
|
|
// need to get ordinal from component using offset because after clearing
|
|
// component size > 1, the index changes
|
|
DataTypeComponent component = structure.getComponentAt(componentOffset);
|
|
int index = component.getOrdinal();
|
|
structure.clearComponent(index);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Method to determine if data type is an undefined size 1 data type
|
|
* @param dataType the given data type
|
|
* @return true if given data type is undefined size 1, false otherwise
|
|
*/
|
|
static boolean isUndefined1(DataType dataType) {
|
|
|
|
if (Undefined.isUndefined(dataType) && dataType.getLength() == 1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to determine if there are at least the given length of undefined (any size) components
|
|
* at the given offset in the given structure. This is only valid for non-packed structures.
|
|
* @param structure the given structure
|
|
* @param offset the given offset
|
|
* @param length the total length of undefined components to check for starting at given offset
|
|
* @param monitor task monitor
|
|
* @return true if there are at least total length of undefined components at the given offset in the given structure
|
|
* @throws CancelledException if cancelled
|
|
* @throws IllegalArgumentException if a packed structure is passed in
|
|
*/
|
|
static boolean hasEnoughUndefinedsOfAnyLengthAtOffset(Structure structure, int offset,
|
|
int length, TaskMonitor monitor) throws CancelledException {
|
|
|
|
if (structure.isPackingEnabled()) {
|
|
throw new IllegalArgumentException(
|
|
"Packed structures are not supported by this method");
|
|
}
|
|
|
|
int endOfRange = offset + length;
|
|
|
|
if (offset < 0 || length <= 0 || structure.getLength() < endOfRange) {
|
|
return false;
|
|
}
|
|
|
|
while (offset < endOfRange) {
|
|
|
|
monitor.checkCanceled();
|
|
|
|
DataTypeComponent component = structure.getComponentContaining(offset);
|
|
|
|
DataType dataType = component.getDataType();
|
|
|
|
if (!Undefined.isUndefined(dataType)) {
|
|
return false;
|
|
}
|
|
|
|
offset = component.getOffset() + component.getLength();
|
|
|
|
}
|
|
|
|
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, 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 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 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 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 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,
|
|
boolean isGrowthAllowed, TaskMonitor monitor) throws CancelledException {
|
|
|
|
if (structureDataType.isPackingEnabled()) {
|
|
throw new IllegalArgumentException(
|
|
"Packed structures are not supported by this method");
|
|
}
|
|
|
|
DataTypeComponent component = structureDataType.getComponentContaining(offset);
|
|
|
|
// 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) {
|
|
if (isGrowthAllowed) {
|
|
return true;
|
|
}
|
|
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;
|
|
}
|
|
|
|
// no matter what size, if the data type at the offset is defined, return false
|
|
// so it is not replaced
|
|
if (component.getDataType() != DataType.DEFAULT) {
|
|
return false;
|
|
}
|
|
|
|
if (lengthToAdd > 1) {
|
|
DataTypeComponent nextDefinedComponent =
|
|
structureDataType.getDefinedComponentAtOrAfterOffset(offset + 1);
|
|
if (nextDefinedComponent == null) {
|
|
return true;
|
|
}
|
|
int available = nextDefinedComponent.getOffset() - offset;
|
|
return available >= lengthToAdd;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to retrieve the number of undefined size 1 components in the given structure before the given offset
|
|
* @param structure the given structure
|
|
* @param offset the given offset
|
|
* @param monitor task monitor
|
|
* @return the number of undefined size 1 components in the given structure before the given offset
|
|
* @throws CancelledException if cancelled
|
|
*/
|
|
static int getNumberOfUndefinedsBeforeOffset(Structure structure, int offset,
|
|
TaskMonitor monitor) throws CancelledException {
|
|
|
|
if (structure.getNumComponents() == 0) {
|
|
return -1;
|
|
}
|
|
|
|
int numUndefineds = 0;
|
|
int index = offset - 1;
|
|
|
|
while (index >= 0) {
|
|
monitor.checkCanceled();
|
|
DataTypeComponent component = structure.getComponentAt(index);
|
|
if (component != null && component.getDataType() == DataType.DEFAULT) {
|
|
index--;
|
|
numUndefineds++;
|
|
}
|
|
else {
|
|
return numUndefineds;
|
|
}
|
|
}
|
|
return numUndefineds;
|
|
}
|
|
|
|
/**
|
|
* Method to retrieve the number of undefined size 1 components starting at the given offset in the given structure
|
|
* @param structure the given structure
|
|
* @param offset the given offset
|
|
* @param monitor task monitor
|
|
* @return the number of undefined size 1 components starting at the given offset in the given structure
|
|
* @throws CancelledException if cancelled
|
|
*/
|
|
static int getNumberOfUndefinedsStartingAtOffset(Structure structure, int offset,
|
|
TaskMonitor monitor) throws CancelledException {
|
|
|
|
int numUndefineds = 0;
|
|
int index = offset;
|
|
|
|
while (index < structure.getLength()) {
|
|
monitor.checkCanceled();
|
|
DataTypeComponent component = structure.getComponentAt(index);
|
|
if (component.getDataType() == DataType.DEFAULT) {
|
|
index++;
|
|
numUndefineds++;
|
|
}
|
|
else {
|
|
return numUndefineds;
|
|
}
|
|
}
|
|
return numUndefineds;
|
|
}
|
|
|
|
|
|
}
|