mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
678 lines
19 KiB
Java
678 lines
19 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 java.util.Map.Entry;
|
|
|
|
import ghidra.program.model.address.Address;
|
|
import ghidra.program.model.data.*;
|
|
import ghidra.program.model.listing.Function;
|
|
import ghidra.program.model.symbol.Namespace;
|
|
import ghidra.util.exception.CancelledException;
|
|
import ghidra.util.task.TaskMonitor;
|
|
|
|
/**
|
|
* The Class that describes object oriented classes that exist in the program
|
|
*/
|
|
public class RecoveredClass {
|
|
|
|
private String name;
|
|
private CategoryPath classPath;
|
|
private Namespace classNamespace;
|
|
private DataTypeManager dataTypeManager;
|
|
|
|
private List<Address> vftableAddresses = new ArrayList<Address>();
|
|
private List<Function> allClassVirtualFunctions = new ArrayList<Function>();
|
|
|
|
private boolean hasParentClass = false;
|
|
private boolean hasChildClass = false;
|
|
private boolean hasVftable = true;
|
|
|
|
private boolean hasSingleInheritance = false;
|
|
private boolean hasMultipleInheritance = false;
|
|
private boolean hasMultipleVirtualInheritance = false;
|
|
|
|
private List<Function> constructorAndDestructorList = new ArrayList<Function>();
|
|
private List<Function> constructorList = new ArrayList<Function>();
|
|
private List<Function> inlinedConstructorList = new ArrayList<Function>();
|
|
private List<Function> destructorList = new ArrayList<Function>();
|
|
private List<Function> inlinedDestructorList = new ArrayList<Function>();
|
|
private List<Function> deletingDestructors = new ArrayList<Function>();
|
|
private List<Function> nonThisDestructors = new ArrayList<Function>();
|
|
private List<Function> cloneFunctions = new ArrayList<Function>();
|
|
private List<Function> indeterminateList = new ArrayList<Function>();
|
|
private List<Function> indeterminateInlineList = new ArrayList<Function>();
|
|
|
|
private Map<Address, List<Function>> vftableToVfunctionsMap =
|
|
new HashMap<Address, List<Function>>();
|
|
|
|
private List<RecoveredClass> classHierarchy = new ArrayList<RecoveredClass>();
|
|
private Map<RecoveredClass, List<RecoveredClass>> classHierarchyMap =
|
|
new HashMap<RecoveredClass, List<RecoveredClass>>();
|
|
|
|
private Map<Address, RecoveredClass> vftableToBaseClassMap =
|
|
new HashMap<Address, RecoveredClass>();
|
|
private Map<Integer, Address> orderToVftableMap = new HashMap<Integer, Address>();
|
|
|
|
private Map<Integer, Address> classOffsetToVftableMap = new HashMap<Integer, Address>();
|
|
|
|
private List<RecoveredClass> parentList = new ArrayList<RecoveredClass>();
|
|
private Map<RecoveredClass, Boolean> parentToBaseTypeMap =
|
|
new HashMap<RecoveredClass, Boolean>();
|
|
|
|
private List<RecoveredClass> childClasses = new ArrayList<RecoveredClass>();
|
|
|
|
private Address vbtableAddress = null;
|
|
private Structure vbtableStructure = null;
|
|
private int vbtableOffset = NONE;
|
|
|
|
private boolean inheritsVirtualAncestor = false;
|
|
private boolean isPublicClass = false;
|
|
private boolean isDiamondShaped = false;
|
|
|
|
private Structure existingClassStructure = null;
|
|
private Structure computedClassStructure = null;
|
|
private boolean hasExistingClassStructure = false;
|
|
private Function vbaseDestructor = null;
|
|
|
|
private String shortenedTemplateName = new String();
|
|
|
|
private static final int NONE = -1;
|
|
|
|
TaskMonitor monitor = TaskMonitor.DUMMY;
|
|
|
|
|
|
RecoveredClass(String name, CategoryPath classPath, Namespace classNamespace,
|
|
DataTypeManager dataTypeManager) {
|
|
this.name = name;
|
|
this.classPath = classPath;
|
|
this.classNamespace = classNamespace;
|
|
this.dataTypeManager = dataTypeManager;
|
|
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public List<Function> getVirtualFunctions(Address vftableAddress) {
|
|
return vftableToVfunctionsMap.get(vftableAddress);
|
|
}
|
|
|
|
public List<Function> getAllVirtualFunctions() {
|
|
return allClassVirtualFunctions;
|
|
}
|
|
|
|
public CategoryPath getClassPath() {
|
|
return classPath;
|
|
}
|
|
|
|
public Namespace getClassNamespace() {
|
|
return classNamespace;
|
|
}
|
|
|
|
public void setHasParentClass(boolean bool) {
|
|
hasParentClass = bool;
|
|
return;
|
|
}
|
|
|
|
public boolean hasParentClass() {
|
|
return hasParentClass;
|
|
}
|
|
|
|
public void setHasChildClass(boolean bool) {
|
|
hasChildClass = true;
|
|
}
|
|
|
|
public boolean hasChildClass() {
|
|
return hasChildClass;
|
|
}
|
|
|
|
public void addParent(RecoveredClass recoveredClass) {
|
|
if (!parentList.contains(recoveredClass)) {
|
|
parentList.add(recoveredClass);
|
|
}
|
|
}
|
|
|
|
public List<RecoveredClass> getParentList() {
|
|
return parentList;
|
|
}
|
|
|
|
public void addVftableAddress(Address address) {
|
|
vftableAddresses.add(address);
|
|
setHasVftable(true);
|
|
}
|
|
|
|
public List<Address> getVftableAddresses() {
|
|
return vftableAddresses;
|
|
}
|
|
|
|
public void addVftableVfunctionsMapping(Address vftAddress, List<Function> vfunctions) {
|
|
vftableToVfunctionsMap.put(vftAddress, vfunctions);
|
|
allClassVirtualFunctions.addAll(vfunctions);
|
|
}
|
|
|
|
public void addVftableToBaseClassMapping(Address vftAddress, RecoveredClass recoveredClass) {
|
|
vftableToBaseClassMap.put(vftAddress, recoveredClass);
|
|
}
|
|
|
|
public RecoveredClass getVftableBaseClass(Address address) {
|
|
return vftableToBaseClassMap.get(address);
|
|
}
|
|
|
|
public void addOrderToVftableMapping(Integer order, Address address) {
|
|
orderToVftableMap.put(order, address);
|
|
}
|
|
|
|
public Map<Integer, Address> getOrderToVftableMap() {
|
|
return orderToVftableMap;
|
|
}
|
|
|
|
public void addClassOffsetToVftableMapping(int offset, Address vftableAddress)
|
|
throws Exception {
|
|
|
|
// already have this mapping
|
|
if (classOffsetToVftableMap.get(offset) == vftableAddress) {
|
|
return;
|
|
}
|
|
|
|
if (!classOffsetToVftableMap.keySet().contains(offset)) {
|
|
|
|
// error if try to add same address to different offset
|
|
if (classOffsetToVftableMap.values().contains(vftableAddress)) {
|
|
throw new Exception(name + " trying to add same vftable address " +
|
|
vftableAddress.toString() + " to new offset " + offset);
|
|
}
|
|
|
|
classOffsetToVftableMap.put(offset, vftableAddress);
|
|
return;
|
|
}
|
|
|
|
// error if try to add different address to same offset
|
|
Address address = classOffsetToVftableMap.get(offset);
|
|
if (!address.equals(vftableAddress)) {
|
|
throw new Exception(name + " trying to add different vftable address (old: " +
|
|
vftableAddress.toString() + " new: " + address.toString() + ") to same offset " +
|
|
offset);
|
|
}
|
|
|
|
}
|
|
|
|
public Map<Integer, Address> getClassOffsetToVftableMap() {
|
|
return classOffsetToVftableMap;
|
|
}
|
|
|
|
public void addParentToBaseTypeMapping(RecoveredClass recoveredClass, Boolean isVirtualBase) {
|
|
|
|
if (!parentToBaseTypeMap.keySet().contains(recoveredClass)) {
|
|
parentToBaseTypeMap.put(recoveredClass, isVirtualBase);
|
|
}
|
|
}
|
|
|
|
public Map<RecoveredClass, Boolean> getParentToBaseTypeMap() {
|
|
return parentToBaseTypeMap;
|
|
}
|
|
|
|
public void setVbtableAddress(Address address) {
|
|
vbtableAddress = address;
|
|
}
|
|
|
|
public Address getVbtableAddress() {
|
|
return vbtableAddress;
|
|
}
|
|
|
|
public void setVbtableStructure(Structure structure) {
|
|
vbtableStructure = structure;
|
|
}
|
|
|
|
public Structure getVbtableStructure() {
|
|
return vbtableStructure;
|
|
}
|
|
|
|
public void setVbtableOffset(int offset) {
|
|
vbtableOffset = offset;
|
|
}
|
|
|
|
public int getVbtableOffset() {
|
|
return vbtableOffset;
|
|
}
|
|
|
|
public void setInheritsVirtualAncestor(boolean setting) {
|
|
inheritsVirtualAncestor = setting;
|
|
}
|
|
|
|
public boolean inheritsVirtualAncestor() {
|
|
return inheritsVirtualAncestor;
|
|
}
|
|
|
|
public void setIsPublicClass(boolean setting) {
|
|
isPublicClass = setting;
|
|
}
|
|
|
|
public boolean isPublicClass() {
|
|
return isPublicClass;
|
|
}
|
|
|
|
public void setIsDiamondShaped(boolean setting) {
|
|
isDiamondShaped = setting;
|
|
}
|
|
|
|
public boolean isDiamondShaped() {
|
|
return isDiamondShaped;
|
|
}
|
|
|
|
public void setHasVftable(boolean setting) {
|
|
hasVftable = setting;
|
|
}
|
|
|
|
public boolean hasVftable() {
|
|
return hasVftable;
|
|
}
|
|
|
|
public void setHasSingleInheritance(boolean hasSingleInheritanceSetting) {
|
|
hasSingleInheritance = hasSingleInheritanceSetting;
|
|
}
|
|
|
|
public boolean hasSingleInheritance() {
|
|
return hasSingleInheritance;
|
|
}
|
|
|
|
public void setHasMultipleInheritance(boolean hasMultipleInheritanceSetting) {
|
|
hasMultipleInheritance = hasMultipleInheritanceSetting;
|
|
}
|
|
|
|
public boolean hasMultipleInheritance() {
|
|
return hasMultipleInheritance;
|
|
}
|
|
|
|
public void setHasMultipleVirtualInheritance(boolean hasMultVirtSetting) {
|
|
hasMultipleVirtualInheritance = hasMultVirtSetting;
|
|
}
|
|
|
|
public boolean hasMultipleVirtualInheritance() {
|
|
return hasMultipleVirtualInheritance;
|
|
}
|
|
|
|
public void addConstructorDestructorList(List<Function> list) {
|
|
Iterator<Function> iterator = list.iterator();
|
|
while (iterator.hasNext()) {
|
|
|
|
Function function = iterator.next();
|
|
if (!constructorAndDestructorList.contains(function)) {
|
|
constructorAndDestructorList.add(function);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void removeFromConstructorDestructorList(Function function) {
|
|
if (constructorAndDestructorList.contains(function)) {
|
|
constructorAndDestructorList.remove(function);
|
|
}
|
|
}
|
|
|
|
public void addConstructor(Function function) {
|
|
if (!constructorList.contains(function)) {
|
|
constructorList.add(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void addDestructor(Function function) {
|
|
if (!destructorList.contains(function)) {
|
|
destructorList.add(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void addInlinedConstructor(Function function) {
|
|
if (!inlinedConstructorList.contains(function)) {
|
|
inlinedConstructorList.add(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void addInlinedDestructor(Function function) {
|
|
if (!inlinedDestructorList.contains(function)) {
|
|
inlinedDestructorList.add(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void addNonThisDestructor(Function function) {
|
|
if (!nonThisDestructors.contains(function)) {
|
|
nonThisDestructors.add(function);
|
|
}
|
|
}
|
|
|
|
public void addIndeterminateInline(Function function) {
|
|
if (!indeterminateInlineList.contains(function)) {
|
|
indeterminateInlineList.add(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void removeIndeterminateInline(Function function) {
|
|
if (indeterminateInlineList.contains(function)) {
|
|
indeterminateInlineList.remove(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void addIndeterminateConstructorOrDestructorList(List<Function> list) {
|
|
Iterator<Function> iterator = list.iterator();
|
|
while (iterator.hasNext()) {
|
|
Function function = iterator.next();
|
|
if (!indeterminateList.contains(function)) {
|
|
indeterminateList.add(function);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
public void removeIndeterminateConstructorOrDestructor(Function function) {
|
|
if (indeterminateList.contains(function)) {
|
|
indeterminateList.remove(function);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public List<Function> getConstructorOrDestructorFunctions() {
|
|
return constructorAndDestructorList;
|
|
}
|
|
|
|
public List<Function> getConstructorList() {
|
|
return constructorList;
|
|
}
|
|
|
|
public List<Function> getInlinedConstructorList() {
|
|
return inlinedConstructorList;
|
|
}
|
|
|
|
public List<Function> getDestructorList() {
|
|
return destructorList;
|
|
}
|
|
|
|
public List<Function> getInlinedDestructorList() {
|
|
return inlinedDestructorList;
|
|
}
|
|
|
|
public List<Function> getNonThisDestructors() {
|
|
return nonThisDestructors;
|
|
}
|
|
|
|
public List<Function> getIndeterminateList() {
|
|
return indeterminateList;
|
|
}
|
|
|
|
public List<Function> getIndeterminateInlineList() {
|
|
return indeterminateInlineList;
|
|
}
|
|
|
|
public void addChildClass(RecoveredClass recoveredClass) {
|
|
if (!childClasses.contains(recoveredClass)) {
|
|
childClasses.add(recoveredClass);
|
|
}
|
|
return;
|
|
}
|
|
|
|
public List<RecoveredClass> getChildClasses() {
|
|
return childClasses;
|
|
}
|
|
|
|
public void addExistingClassStructure(Structure classStructure) {
|
|
if (classStructure == null) {
|
|
return;
|
|
}
|
|
existingClassStructure = classStructure;
|
|
hasExistingClassStructure = true;
|
|
return;
|
|
}
|
|
|
|
public Structure getExistingClassStructure() {
|
|
return existingClassStructure;
|
|
}
|
|
|
|
public boolean hasExistingClassStructure() {
|
|
return hasExistingClassStructure;
|
|
}
|
|
|
|
public void updateClassMemberStructure(Structure structure) throws CancelledException {
|
|
|
|
// initialize by copying first structure
|
|
if (computedClassStructure == null) {
|
|
|
|
computedClassStructure =
|
|
new StructureDataType(classPath, name, structure.getLength(), dataTypeManager);
|
|
|
|
int numComponents = structure.getNumComponents();
|
|
for (int i = 0; i < numComponents; i++) {
|
|
DataTypeComponent component = structure.getComponent(i);
|
|
int offset = component.getOffset();
|
|
|
|
computedClassStructure.replaceAtOffset(offset, component.getDataType(),
|
|
component.getDataType().getLength(), component.getFieldName(),
|
|
component.getComment());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// update initial structure using all further structures
|
|
if (structure.getLength() > computedClassStructure.getLength()) {
|
|
computedClassStructure.growStructure(
|
|
structure.getLength() - computedClassStructure.getLength());
|
|
}
|
|
|
|
DataTypeComponent[] definedComponents = structure.getDefinedComponents();
|
|
for (DataTypeComponent newComponent : definedComponents) {
|
|
|
|
DataType newComponentDataType = newComponent.getDataType();
|
|
|
|
int offset = newComponent.getOffset();
|
|
|
|
DataTypeComponent currentComponent =
|
|
computedClassStructure.getComponentContaining(offset);
|
|
DataType currentComponentDataType = currentComponent.getDataType();
|
|
|
|
if (currentComponentDataType.equals(newComponentDataType)) {
|
|
continue;
|
|
}
|
|
|
|
int length = newComponentDataType.getLength();
|
|
String fieldName = newComponent.getFieldName();
|
|
String comment = newComponent.getComment();
|
|
|
|
// if new component is an existing class data type pointer then replace current item
|
|
// with a void pointer of same size if there is room
|
|
if (newComponentDataType instanceof Pointer &&
|
|
newComponentDataType.getName().equals(name + " *")) {
|
|
|
|
Pointer pointer =
|
|
new PointerDataType(VoidDataType.dataType, length, dataTypeManager);
|
|
|
|
if (EditStructureUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(
|
|
computedClassStructure, offset, pointer.getLength(), monitor) ||
|
|
length <= currentComponent.getLength()) {
|
|
|
|
computedClassStructure.replaceAtOffset(offset, pointer, length, fieldName,
|
|
comment);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// if the new component is a non-empty structure, check to see if the current
|
|
// structure has undefined or equivalent components and replace with new struct if so
|
|
if (newComponentDataType instanceof Structure) {
|
|
|
|
// if new component is any empty placeholder structure AND if the existing component
|
|
// is undefined then replace with undefined1 dt
|
|
if (newComponentDataType.isNotYetDefined()) {
|
|
if (Undefined.isUndefined(currentComponentDataType)) {
|
|
computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1,
|
|
fieldName, comment);
|
|
}
|
|
continue;
|
|
}
|
|
if (EditStructureUtils.hasReplaceableComponentsAtOffset(computedClassStructure,
|
|
offset, (Structure)newComponentDataType, monitor)) {
|
|
|
|
boolean successfulClear =
|
|
EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset,
|
|
length, monitor);
|
|
|
|
if (successfulClear) {
|
|
computedClassStructure.replaceAtOffset(offset, newComponentDataType, length,
|
|
fieldName, comment);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// if current component is undefined size 1 and new component is not undefined size 1
|
|
// and there are enough undefineds for it to fit, then replace it
|
|
if (EditStructureUtils.isUndefined1(currentComponentDataType) &&
|
|
!EditStructureUtils.isUndefined1(newComponentDataType)) {
|
|
if (EditStructureUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(
|
|
computedClassStructure,
|
|
offset, length, monitor)) {
|
|
boolean successfulClear =
|
|
EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset,
|
|
length, monitor);
|
|
|
|
if (successfulClear) {
|
|
computedClassStructure.replaceAtOffset(offset, newComponentDataType, length,
|
|
fieldName, comment);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// if new component is not an undefined data type and the current componenent(s)
|
|
// that make up new component length are all undefineds then clear and replace
|
|
// the current component(s) with the new one
|
|
if (Undefined.isUndefined(currentComponentDataType) &&
|
|
!Undefined.isUndefined(newComponentDataType)) {
|
|
|
|
if (EditStructureUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(
|
|
computedClassStructure,
|
|
offset, length, monitor)) {
|
|
boolean successfulClear =
|
|
EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset,
|
|
length, monitor);
|
|
|
|
if (successfulClear) {
|
|
computedClassStructure.replaceAtOffset(offset, newComponentDataType, length,
|
|
fieldName, comment);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// TODO: remove once FillOutStructCmd updates to put Undefined1 instead of undefined
|
|
// for saved offsets
|
|
public void updateClassMemberStructureUndefineds(NoisyStructureBuilder componentMap) {
|
|
|
|
Iterator<Entry<Long, DataType>> componentMapIterator = componentMap.iterator();
|
|
while (componentMapIterator.hasNext()) {
|
|
Entry<Long, DataType> next = componentMapIterator.next();
|
|
Long offset = next.getKey();
|
|
DataType dataType = next.getValue();
|
|
|
|
if (computedClassStructure.getLength() < (offset.intValue() + dataType.getLength())) {
|
|
continue;
|
|
}
|
|
|
|
if (EditStructureUtils.isUndefined1(dataType)) {
|
|
dataType = new Undefined1DataType();
|
|
DataTypeComponent component =
|
|
computedClassStructure.getComponentAt(offset.intValue());
|
|
|
|
if (component != null && !component.getDataType().equals(dataType)) {
|
|
computedClassStructure.replaceAtOffset(offset.intValue(), dataType,
|
|
dataType.getLength(), component.getFieldName(), component.getComment());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Structure getComputedClassStructure() {
|
|
return computedClassStructure;
|
|
}
|
|
|
|
public void addCloneFunction(Function function) {
|
|
if (!cloneFunctions.contains(function)) {
|
|
cloneFunctions.add(function);
|
|
}
|
|
}
|
|
|
|
public List<Function> getCloneFunctions() {
|
|
return cloneFunctions;
|
|
}
|
|
|
|
public void addDeletingDestructor(Function function) {
|
|
if (!deletingDestructors.contains(function)) {
|
|
deletingDestructors.add(function);
|
|
}
|
|
}
|
|
|
|
public List<Function> getDeletingDestructors() {
|
|
return deletingDestructors;
|
|
}
|
|
|
|
public void setVBaseDestructor(Function function) {
|
|
vbaseDestructor = function;
|
|
}
|
|
|
|
public Function getVBaseDestructor() {
|
|
return vbaseDestructor;
|
|
}
|
|
|
|
public void setClassHierarchy(List<RecoveredClass> list) {
|
|
classHierarchy.addAll(list);
|
|
return;
|
|
}
|
|
|
|
public List<RecoveredClass> getClassHierarchy() {
|
|
return classHierarchy;
|
|
}
|
|
|
|
public void addClassHierarchyMapping(RecoveredClass recoveredClass,
|
|
List<RecoveredClass> recoveredClasses) {
|
|
|
|
if (!classHierarchyMap.keySet().contains(recoveredClass)) {
|
|
classHierarchyMap.put(recoveredClass, recoveredClasses);
|
|
}
|
|
}
|
|
|
|
public Map<RecoveredClass, List<RecoveredClass>> getClassHierarchyMap() {
|
|
return classHierarchyMap;
|
|
}
|
|
|
|
public void addShortenedTemplatedName(String shortenedName) {
|
|
shortenedTemplateName = shortenedName;
|
|
}
|
|
|
|
public String getShortenedTemplateName() {
|
|
return shortenedTemplateName;
|
|
}
|
|
}
|
|
|