GP-1044 Fixed promotion of namespaces to only verified classes and added creation of typeinfo structs in stripped gcc binaries

This commit is contained in:
ghidra007 2021-06-16 14:59:01 -04:00 committed by ghidra1
parent cde02a91eb
commit 020df708e2
5 changed files with 503 additions and 191 deletions

View file

@ -163,6 +163,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
else if (isGcc()) { else if (isGcc()) {
boolean runGcc = askYesNo("GCC Class Recovery Still Under Development",
"I understand that gcc class recovery is still under development and my results will be incomplete but want to run this anyway.");
if (!runGcc) {
return;
}
nameVfunctions = true; nameVfunctions = true;
recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation, recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation,
state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
@ -545,25 +550,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return true; return true;
} }
//TODO: call this before create data in debug mode from script
private void findClassesWithErrors(List<RecoveredClass> recoveredClasses)
throws CancelledException {
Iterator<RecoveredClass> iterator = recoveredClasses.iterator();
while (iterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = iterator.next();
if (hasConstructorDestructorDiscrepancy(recoveredClass)) {
println(recoveredClass.getName() + " has function on both c and d lists");
}
}
}
/** /**
* Method to analyze the program changes with the decompiler parameter ID analyzer * Method to analyze the program changes with the decompiler parameter ID analyzer
* @param set the set of addresses to analyze * @param set the set of addresses to analyze

View file

@ -131,25 +131,25 @@ public class RTTIClassRecoverer extends RecoveredClassUtils {
/** /**
* Method to promote the namespace is a class namespace. * Method to promote the namespace is a class namespace.
* @param vftableNamespace the namespace for the vftable * @param namespace the namespace for the vftable
* @return true if namespace is (now) a class namespace or false if it could not be promoted. * @return true if namespace is (now) a class namespace or false if it could not be promoted.
*/ */
public Namespace promoteToClassNamespace(Namespace vftableNamespace) { public Namespace promoteToClassNamespace(Namespace namespace) {
try { try {
Namespace newClass = NamespaceUtils.convertNamespaceToClass(vftableNamespace); Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace);
SymbolType symbolType = newClass.getSymbol().getSymbolType(); SymbolType symbolType = newClass.getSymbol().getSymbolType();
if (symbolType == SymbolType.CLASS) { if (symbolType == SymbolType.CLASS) {
return newClass; return newClass;
} }
Msg.debug(this, Msg.debug(this,
"Could not promote " + vftableNamespace.getName() + " to a class namespace"); "Could not promote " + namespace.getName() + " to a class namespace");
return null; return null;
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
Msg.debug(this, "Could not promote " + vftableNamespace.getName() + Msg.debug(this, "Could not promote " + namespace.getName() +
" to a class namespace because " + e.getMessage()); " to a class namespace because " + e.getMessage());
return null; return null;
} }

View file

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package classrecovery; package classrecovery;
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* *
@ -34,18 +35,18 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ghidra.app.cmd.label.DemanglerCmd; import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.util.demangler.*;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.flatapi.FlatProgramAPI; import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.*;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class RTTIGccClassRecoverer extends RTTIClassRecoverer { public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
@ -70,8 +71,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool, public RTTIGccClassRecoverer(Program program, ProgramLocation location, PluginTool tool,
FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates,
boolean nameVfunctions, boolean nameVfunctions, TaskMonitor monitor) {
TaskMonitor monitor) {
super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions, super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions,
monitor); monitor);
@ -98,7 +98,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
@Override @Override
public List<RecoveredClass> createRecoveredClasses() { public List<RecoveredClass> createRecoveredClasses() {
try { try {
List<RecoveredClass> recoveredClasses = processGccRTTI(); List<RecoveredClass> recoveredClasses = processGccRTTI();
@ -111,7 +110,11 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
createClassHierarchyListAndMapForGcc(recoveredClasses); createClassHierarchyListAndMapForGcc(recoveredClasses);
//TODO: check for dwarf
//boolean isDwarfLoaded = isDwarfLoaded();
assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); assignConstructorsAndDestructorsUsingExistingName(recoveredClasses);
//else
// find c/ds other way
createVftableOrderMap(recoveredClasses); createVftableOrderMap(recoveredClasses);
@ -132,8 +135,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return null; return null;
} }
} }
private boolean isGcc() { private boolean isGcc() {
@ -163,8 +164,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
Address found = program.getMemory().findBytes(commentBlock.getStart(), Address found = program.getMemory().findBytes(commentBlock.getStart(),
commentBlock.getEnd(), commentBlock.getEnd(), gccBytes, maskBytes, true, monitor);
gccBytes, maskBytes, true, monitor);
if (found == null) { if (found == null) {
return false; return false;
} }
@ -234,7 +234,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private List<RecoveredClass> processGccRTTI() throws CancelledException, Exception { private List<RecoveredClass> processGccRTTI() throws CancelledException, Exception {
// create rtti vtables and typeinfo structs // create rtti vtables and typeinfo structs
// find the three special vtables and replace the incorrectly made array with // find the three special vtables and replace the incorrectly made array with
// data types found in vtable // data types found in vtable
@ -266,7 +265,15 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
} }
// TODO: are all recovered classes in the map? // check map for classes that are not in the recoveredClass list
Set<Namespace> keySet = namespaceToClassMap.keySet();
for (Namespace namespace : keySet) {
monitor.checkCanceled();
RecoveredClass recoveredClass = namespaceToClassMap.get(namespace);
if (!recoveredClasses.contains(recoveredClass)) {
recoveredClasses.add(recoveredClass);
}
}
// update the recoveredClass list with the typeinfo classes that do not have vtables // update the recoveredClass list with the typeinfo classes that do not have vtables
Set<RecoveredClass> typeinfoClasses = classToTypeinfoMap.keySet(); Set<RecoveredClass> typeinfoClasses = classToTypeinfoMap.keySet();
@ -308,9 +315,14 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
if (DEBUG) { 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 = addNoVftableClass(classNamespace); recoveredClass = createNewClass(classNamespace, false);
recoveredClasses.add(recoveredClass); recoveredClasses.add(recoveredClass);
} }
else {
if (!recoveredClasses.contains(recoveredClass)) {
recoveredClasses.add(recoveredClass);
}
}
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) { if (specialTypeinfoRef == null) {
@ -392,21 +404,22 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
recoveredClass.setHasMultipleInheritance(true); recoveredClass.setHasMultipleInheritance(true);
if (recoveredClass.inheritsVirtualAncestor()) { if (recoveredClass.inheritsVirtualAncestor()) {
recoveredClass.setHasMultipleVirtualInheritance(true); recoveredClass.setHasMultipleVirtualInheritance(true);
} }
List<RecoveredClass> parents = addGccClassParents(recoveredClass, typeinfoAddress); List<RecoveredClass> 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);
}
}
if (!recoveredClasses.containsAll(parents)) {
if (DEBUG) {
Msg.debug(this,
"missing some parents from " + recoveredClass.getName() + " on list");
}
}
// newNonVftableClasses =
// addMissingClasses(parents, newNonVftableClasses, recoveredClasses);
} }
} }
@ -417,7 +430,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
RecoveredClass recoveredClass = recoveredClassIterator.next(); RecoveredClass recoveredClass = recoveredClassIterator.next();
Msg.debug(this, "Processing class " + recoveredClass.getName()); Msg.debug(this, "Processing class " + recoveredClass.getClassNamespace().getName(true));
List<Address> vftableAddresses = recoveredClass.getVftableAddresses(); List<Address> vftableAddresses = recoveredClass.getVftableAddresses();
@ -443,21 +456,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private void createGccRttiData() throws CancelledException, Exception { private void createGccRttiData() throws CancelledException, Exception {
// TODO: find typeinfo's using other means besides their names since in static case they
// aren't named
// TODO: either check for DWARF and do below if dwarf and another method to find them first
// if not dwarf or combine and either way they are found - immediately call the create typeinfo struct
// but for one at a time.
// remove the ones in the list that are in the EXTERNAL (space or any non-loaded sapce)
// but keep the special ones on symbol list or maybe removet hem here too then don't have
// to keep skipping them later
// find all typeinfo symbols and get their class namespace and create RecoveredClass object
List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), "typeinfo", true);
// create the appropriate type of type info struct at the various typeinfo symbol locations // create the appropriate type of type info struct at the various typeinfo symbol locations
createTypeinfoStructs(typeinfoSymbols); createTypeinfoStructs();
// process vtables and create classes for the vtables that have no typeinfo // process vtables and create classes for the vtables that have no typeinfo
processVtables(); processVtables();
@ -663,8 +663,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return false; return false;
} }
// TODO: maybe print a warning if the first item is not all zeros bc usually they are -- but pass
// it even then
return true; return true;
} }
@ -906,7 +904,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
} }
private void createTypeinfoStructs(List<Symbol> typeinfoSymbols) throws CancelledException { private void createTypeinfoStructs() throws CancelledException {
StructureDataType classTypeInfoStructure = createClassTypeInfoStructure(); StructureDataType classTypeInfoStructure = createClassTypeInfoStructure();
StructureDataType siClassTypeInfoStructure = StructureDataType siClassTypeInfoStructure =
@ -914,23 +912,28 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
StructureDataType baseClassTypeInfoStructure = StructureDataType baseClassTypeInfoStructure =
createBaseClassTypeInfoStructure(classTypeInfoStructure); createBaseClassTypeInfoStructure(classTypeInfoStructure);
Iterator<Symbol> typeinfoIterator = typeinfoSymbols.iterator(); List<Address> typeinfoAddresses;
while (typeinfoIterator.hasNext()) { // get dwarf option
boolean isDwarfLoaded = isDwarfLoaded();
monitor.checkCanceled(); // if dwarf get using symbols
if (isDwarfLoaded) {
Symbol typeinfoSymbol = typeinfoIterator.next(); typeinfoAddresses = getTypeinfoAddressesUsingSymbols();
Address typeinfoAddress = typeinfoSymbol.getAddress();
// skip the typeinfo symbols from the three special typeinfos
if (isSpecialTypeinfo(typeinfoAddress)) {
continue;
} }
// check for EXTERNAL block and look for specialTypeinfoRef there else {
// if fix works, put external block error message and to contact us // if not, get using ref to specials
if (hasExternalBlock() && isSpecialVtable(typeinfoAddress)) { if (hasExternalRelocationRefs()) {
continue; typeinfoAddresses = getTypeinfoAddressesUsingRelocationTable();
} }
else {
typeinfoAddresses = getTypeinfoAddressesUsingSpecialTypeinfos();
}
}
if (typeinfoAddresses.isEmpty()) {
return;
}
for (Address typeinfoAddress : typeinfoAddresses) {
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) { if (specialTypeinfoRef == null) {
@ -955,32 +958,87 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
} }
try { Data newStructure = null;
// create a "no inheritance" struct here // create a "no inheritance" struct here
if (specialTypeinfoRef.equals(class_type_info) || if (specialTypeinfoRef.equals(class_type_info) ||
specialTypeinfoRef.equals(class_type_info_vtable)) { specialTypeinfoRef.equals(class_type_info_vtable)) {
api.clearListing(typeinfoAddress,
typeinfoAddress.add(classTypeInfoStructure.getLength())); newStructure = applyTypeinfoStructure(classTypeInfoStructure, typeinfoAddress);
api.createData(typeinfoAddress, classTypeInfoStructure);
continue;
} }
// create a "single inheritance" struct here // create a "single inheritance" struct here
if (specialTypeinfoRef.equals(si_class_type_info) || else if (specialTypeinfoRef.equals(si_class_type_info) ||
specialTypeinfoRef.equals(si_class_type_info_vtable)) { specialTypeinfoRef.equals(si_class_type_info_vtable)) {
api.clearListing(typeinfoAddress,
typeinfoAddress.add(siClassTypeInfoStructure.getLength() - 1)); newStructure = applyTypeinfoStructure(siClassTypeInfoStructure, typeinfoAddress);
api.createData(typeinfoAddress, siClassTypeInfoStructure);
continue;
} }
// create a "virtual multip inheritance" struct here // create a "virtual multip inheritance" struct here
if (specialTypeinfoRef.equals(vmi_class_type_info) || else if (specialTypeinfoRef.equals(vmi_class_type_info) ||
specialTypeinfoRef.equals(vmi_class_type_info_vtable)) { specialTypeinfoRef.equals(vmi_class_type_info_vtable)) {
Structure vmiClassTypeinfoStructure =
getOrCreateVmiTypeinfoStructure(typeinfoAddress, baseClassTypeInfoStructure);
if (vmiClassTypeinfoStructure != null) {
newStructure = applyTypeinfoStructure(vmiClassTypeinfoStructure, typeinfoAddress);
}
}
if (newStructure == null) {
Msg.debug(this, "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) {
typeinfoSymbol = createDemangledTypeinfoSymbol(typeinfoAddress);
if (typeinfoSymbol == null) {
Msg.debug(this, "Could not create demangled typeinfo symbol at " +
typeinfoAddress.toString());
}
}
if (typeinfoSymbol != null && typeinfoSymbol.getName().equals("typeinfo")) {
promoteToClassNamespace(typeinfoSymbol.getParentNamespace());
continue;
}
}
}
private Data applyTypeinfoStructure(Structure typeInfoStructure, Address typeinfoAddress)
throws CancelledException, AddressOutOfBoundsException {
api.clearListing(typeinfoAddress, typeinfoAddress.add(typeInfoStructure.getLength() - 1));
Data newStructure;
try {
newStructure = api.createData(typeinfoAddress, typeInfoStructure);
}
catch (Exception e) {
newStructure = null;
}
if (newStructure == null) {
Msg.debug(this,
"Could not create " + typeInfoStructure.getName() + " at " + typeinfoAddress);
}
return newStructure;
}
private Structure getOrCreateVmiTypeinfoStructure(Address typeinfoAddress,
StructureDataType baseClassTypeInfoStructure) {
// get num base classes // get num base classes
int offsetOfNumBases = 2 * defaultPointerSize + 4; int offsetOfNumBases = 2 * defaultPointerSize + 4;
int numBases = api.getInt(typeinfoAddress.add(offsetOfNumBases)); int numBases;
try {
numBases = api.getInt(typeinfoAddress.add(offsetOfNumBases));
}
catch (MemoryAccessException | AddressOutOfBoundsException e) {
return null;
}
// get or create the vmiClassTypeInfoStruct // get or create the vmiClassTypeInfoStruct
Structure vmiClassTypeinfoStructure = Structure vmiClassTypeinfoStructure =
@ -990,17 +1048,281 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
vmiClassTypeinfoStructure = vmiClassTypeinfoStructure =
createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases); createVmiClassTypeInfoStructure(baseClassTypeInfoStructure, numBases);
} }
api.clearListing(typeinfoAddress, return vmiClassTypeinfoStructure;
typeinfoAddress.add(vmiClassTypeinfoStructure.getLength() - 1));
api.createData(typeinfoAddress, vmiClassTypeinfoStructure);
}
}
catch (Exception e) {
Msg.debug(this, "ERROR: Could not apply structure to " + typeinfoAddress);
}
} }
private Symbol createDemangledTypeinfoSymbol(Address typeinfoAddress) {
String mangledTypeinfo = getTypeinfoName(typeinfoAddress);
if (mangledTypeinfo == null) {
Msg.debug(this, "Could not get typeinfo string from " + typeinfoAddress.toString());
return null;
}
if (mangledTypeinfo.startsWith("*")) {
mangledTypeinfo = mangledTypeinfo.substring(1);
}
mangledTypeinfo = "_Z" + mangledTypeinfo;
DemanglerOptions options = new DemanglerOptions();
options.setDemangleOnlyKnownPatterns(false);
options.setApplySignature(false);
options.setDoDisassembly(false);
DemangledObject demangled = DemanglerUtil.demangle(mangledTypeinfo);
if (demangled == null) {
Msg.debug(this, "Could not demangle typeinfo string at " + typeinfoAddress.toString());
return null;
}
String namespaceString = demangled.getNamespaceString();
Namespace classNamespace = createTypeinfoClassNamespace(namespaceString);
Msg.debug(this, typeinfoAddress.toString() + " " + namespaceString);
if (classNamespace == null) {
Msg.debug(this,
typeinfoAddress.toString() +
"Could not create a class namespace for demangled namespace string " +
namespaceString);
return null;
}
// create the new typeinfo symbol in the demangled namespace
try {
Symbol newSymbol = symbolTable.createLabel(typeinfoAddress, "typeinfo", classNamespace,
SourceType.ANALYSIS);
return newSymbol;
}
catch (InvalidInputException e) {
Msg.error(this,
typeinfoAddress.toString() + " invalid input exception " + e.getMessage());
return null;
}
catch (IllegalArgumentException e) {
Msg.debug(this,
typeinfoAddress.toString() + " illegal argument exception " + e.getMessage());
return null;
}
}
private Namespace createTypeinfoClassNamespace(String namespaceString) {
int indexOfColons = namespaceString.indexOf("::");
Namespace namespace = globalNamespace;
while (indexOfColons != -1) {
String namespaceName = namespaceString.substring(0, indexOfColons);
Namespace newNamespace = getOrCreateNamespace(namespaceName, namespace);
if (newNamespace == null) {
return null;
}
namespace = newNamespace;
namespaceString = namespaceString.substring(indexOfColons + 2);
indexOfColons = namespaceString.indexOf("::");
}
// the substring after the last :: is the class namespace we want to return
Namespace classNamespace = getOrCreateNamespace(namespaceString, namespace);
if (classNamespace == null) {
return null;
}
if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) {
classNamespace = promoteToClassNamespace(classNamespace);
}
return classNamespace;
}
private Namespace getOrCreateNamespace(String namespaceName, Namespace parentNamespace) {
Namespace namespace = symbolTable.getNamespace(namespaceName, parentNamespace);
if (namespace == null) {
try {
namespace = symbolTable.createNameSpace(parentNamespace, namespaceName,
SourceType.ANALYSIS);
}
catch (DuplicateNameException e) {
// shouldn't happen since it only gets here if the symbol didn't exist in the first place
}
catch (InvalidInputException e) {
return null;
}
}
return namespace;
}
private String getTypeinfoName(Address address) {
Data dataAt = api.getDataAt(address);
if (dataAt == null) {
return null;
}
if (!(dataAt.getBaseDataType() instanceof Structure)) {
return null;
}
Structure typeinfoStructure = (Structure) dataAt.getBaseDataType();
if (!typeinfoStructure.getName().contains("ClassTypeInfoStructure")) {
return null;
}
DataTypeComponent typeinfoNameComponent = typeinfoStructure.getComponent(1);
DataType typeinfoNameDatatype = typeinfoNameComponent.getDataType();
if (!(typeinfoNameDatatype instanceof Pointer)) {
return null;
}
Address stringReference =
extraUtils.getSingleReferencedAddress(address.add(typeinfoNameComponent.getOffset()));
Data stringData = api.getDataAt(stringReference);
if (stringData == null) {
return null;
}
int stringLen = stringData.getLength();
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), stringReference);
StringDataType sdt = new StringDataType();
String str;
try {
str = (String) sdt.getValue(buf, sdt.getDefaultSettings(), stringLen);
}
catch (AddressOutOfBoundsException e) {
return null;
}
return str;
}
/**
* Method to get a list typeinfo addresses using symbols
* @return a list of non-special typeinfo addresses that have "typeinfo" symbols
* @throws CancelledException if cancelled
*/
private List<Address> getTypeinfoAddressesUsingSymbols() throws CancelledException {
List<Address> typeinfoAddresses = new ArrayList<Address>();
List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), "typeinfo", true);
Iterator<Symbol> typeinfoIterator = typeinfoSymbols.iterator();
while (typeinfoIterator.hasNext()) {
monitor.checkCanceled();
Symbol typeinfoSymbol = typeinfoIterator.next();
Address typeinfoAddress = typeinfoSymbol.getAddress();
// skip the typeinfo symbols from the three special typeinfos
if (isSpecialTypeinfo(typeinfoAddress)) {
continue;
}
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (hasExternalBlock() && isSpecialVtable(typeinfoAddress)) {
continue;
}
typeinfoAddresses.add(typeinfoAddress);
}
return typeinfoAddresses;
}
/**
* Method to get a list typeinfo addresses using relocation table info
* @return a list of typeinfo addresses
* @throws CancelledException if cancelled
*/
private List<Address> getTypeinfoAddressesUsingRelocationTable() throws CancelledException {
List<Address> typeinfoAddresses = new ArrayList<Address>();
Iterator<Bookmark> bookmarksIterator =
program.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR);
while (bookmarksIterator.hasNext()) {
monitor.checkCanceled();
Bookmark bookmark = bookmarksIterator.next();
if (bookmark.getCategory().equals("EXTERNAL Relocation") &&
bookmarkContainsSpecialTypeinfoName(bookmark.getComment())) {
typeinfoAddresses.add(bookmark.getAddress());
}
}
return typeinfoAddresses;
}
private boolean bookmarkContainsSpecialTypeinfoName(String bookmarkComment) {
if (bookmarkComment.contains("class_type_info")) {
return true;
}
if (bookmarkComment.contains("si_class_type_info")) {
return true;
}
if (bookmarkComment.contains("vmi_class_type_info")) {
return true;
}
return false;
}
/**
* Method to check to see if there are any EXTERNAL block relocations
* @return true if there are any EXTERNAL block relocations in the program, false otherwise
* @throws CancelledException if cancelled
*/
private boolean hasExternalRelocationRefs() throws CancelledException {
// if no external block then there won't be any refernces to special typeinfos in the external
// block so return empty list
if (!hasExternalBlock()) {
return false;
}
Iterator<Bookmark> bookmarksIterator =
program.getBookmarkManager().getBookmarksIterator(BookmarkType.ERROR);
while (bookmarksIterator.hasNext()) {
monitor.checkCanceled();
Bookmark bookmark = bookmarksIterator.next();
if (bookmark.getCategory().equals("EXTERNAL Relocation")) {
return true;
}
}
return false;
}
private List<Address> getTypeinfoAddressesUsingSpecialTypeinfos() throws CancelledException {
List<Address> specialTypeinfoRefs = new ArrayList<Address>();
Reference[] refsToClassTypeinfo = api.getReferencesTo(class_type_info);
for (Reference ref : refsToClassTypeinfo) {
monitor.checkCanceled();
specialTypeinfoRefs.add(ref.getFromAddress());
}
Reference[] refsToSiClassTypeinfo = api.getReferencesTo(si_class_type_info);
for (Reference ref : refsToSiClassTypeinfo) {
monitor.checkCanceled();
specialTypeinfoRefs.add(ref.getFromAddress());
}
Reference[] refsToVmiClassTypeinfo = api.getReferencesTo(vmi_class_type_info);
for (Reference ref : refsToVmiClassTypeinfo) {
monitor.checkCanceled();
specialTypeinfoRefs.add(ref.getFromAddress());
}
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;
} }
private StructureDataType createClassTypeInfoStructure() { private StructureDataType createClassTypeInfoStructure() {
@ -1136,7 +1458,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
Msg.debug(this, "couldn't get int at address " + inheritanceFlagAddress.toString()); Msg.debug(this, "couldn't get int at address " + inheritanceFlagAddress.toString());
} }
int baseClassArrayOffset = defaultPointerSize * 3; int baseClassArrayOffset = defaultPointerSize * 3;
Data baseClassArrayData = typeinfoStructure.getComponentAt(baseClassArrayOffset); Data baseClassArrayData = typeinfoStructure.getComponentAt(baseClassArrayOffset);
@ -1155,8 +1476,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
for (int i = 0; i < numParents; i++) { for (int i = 0; i < numParents; i++) {
// get parent from pointer to parent typeinfo // get parent from pointer to parent typeinfo
Address parentRefAddress = Address parentRefAddress = extraUtils.getAddress(typeinfoAddress,
extraUtils.getAddress(typeinfoAddress,
baseClassArrayOffset + (i * 2 * defaultPointerSize)); baseClassArrayOffset + (i * 2 * defaultPointerSize));
if (parentRefAddress == null) { if (parentRefAddress == null) {
Msg.debug(this, "Could not access address " + typeinfoAddress.toString() + Msg.debug(this, "Could not access address " + typeinfoAddress.toString() +
@ -1461,7 +1781,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// previously // previously
RecoveredClass classWithNoTypeinfoStruct = getClass(vtableNamespace); RecoveredClass classWithNoTypeinfoStruct = getClass(vtableNamespace);
if (classWithNoTypeinfoStruct == null) { if (classWithNoTypeinfoStruct == null) {
addNoVftableClass(vtableNamespace); createNewClass(vtableNamespace, false);
continue; continue;
} }
@ -1483,7 +1803,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
Address vftableAddress = extraUtils.getAddress(typeinfoAddress, defaultPointerSize); Address vftableAddress = extraUtils.getAddress(typeinfoAddress, defaultPointerSize);
// no valid address here so continue // no valid address here so continue
if (vftableAddress == null) { if (vftableAddress == null) {
addNoVftableClass(vtableNamespace); createNewClass(vtableNamespace, false);
// if so should also add to no vftable class // if so should also add to no vftable class
continue; continue;
} }
@ -1513,8 +1833,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
private boolean isSpecialVtable(Address address) { private boolean isSpecialVtable(Address address) {
if (address.equals(class_type_info_vtable) || if (address.equals(class_type_info_vtable) || address.equals(si_class_type_info_vtable) ||
address.equals(si_class_type_info_vtable) ||
address.equals(vmi_class_type_info_vtable)) { address.equals(vmi_class_type_info_vtable)) {
return true; return true;
} }
@ -1546,15 +1865,13 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// TODO: make sure it is a valid typeinfo // TODO: make sure it is a valid typeinfo
Namespace classNamespace = typeinfoSymbol.getParentNamespace(); Namespace classNamespace = typeinfoSymbol.getParentNamespace();
RecoveredClass recoveredClass = getClass(classNamespace); RecoveredClass recoveredClass = getClass(classNamespace);
// we don't know yet if this class has vftable so just add without for now // we don't know yet if this class has vftable so just add without for now
if (recoveredClass == null) { if (recoveredClass == null) {
recoveredClass = addNoVftableClass(classNamespace); recoveredClass = createNewClass(classNamespace, false);
recoveredClasses.add(recoveredClass); recoveredClasses.add(recoveredClass);
classToTypeinfoMap.put(recoveredClass, typeinfoAddress); classToTypeinfoMap.put(recoveredClass, typeinfoAddress);
@ -1592,7 +1909,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
// per docs those on this list // per docs those on this list
// have no bases (ie parents), and is also a base type for the other two class type // have no bases (ie parents), and is also a base type for the other two class type
// representations ie (si and vmi) // representations ie (si and vmi)
@ -2113,4 +2429,3 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
} }

View file

@ -47,6 +47,7 @@ import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.HighVariable; import ghidra.program.model.pcode.HighVariable;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.Msg;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -937,11 +938,14 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace();
if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) { if (classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) {
// println("RTTI_Class_Hierarchy_Descriptor at " + classNamespace = promoteToClassNamespace(classNamespace);
// classHierarchyDescriptorAddress.toString() + if(classNamespace.getSymbol().getSymbolType() != SymbolType.CLASS) {
// " is not in a class namespace. Cannot process."); Msg.debug(this,
classHierarchyDescriptorAddress.toString() + " Could not promote " +
classNamespace.getName(true) + " to a class namespace.");
continue; continue;
} }
}
List<Symbol> vftableSymbolsInNamespace = List<Symbol> vftableSymbolsInNamespace =
getVftablesInNamespace(vftableSymbols, classNamespace); getVftablesInNamespace(vftableSymbols, classNamespace);
@ -975,12 +979,12 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
List<RecoveredClass> classesWithVftablesInNamespace = List<RecoveredClass> classesWithVftablesInNamespace =
recoverClassesFromVftables(vftableSymbolsInNamespace, false, false); recoverClassesFromVftables(vftableSymbolsInNamespace, false, false);
if (classesWithVftablesInNamespace.size() == 0) { if (classesWithVftablesInNamespace.size() == 0) {
//println("No class recovered for namespace " + classNamespace.getName()); Msg.debug(this,"No class recovered for namespace " + classNamespace.getName());
continue; continue;
} }
if (classesWithVftablesInNamespace.size() > 1) { if (classesWithVftablesInNamespace.size() > 1) {
// println("Unexpected multiple classes recovered for namespace " + Msg.debug(this,"Unexpected multiple classes recovered for namespace " +
// classNamespace.getName()); classNamespace.getName());
continue; continue;
} }
@ -1232,7 +1236,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
// if the namespace isn't in the map then it is a class // if the namespace isn't in the map then it is a class
// without a vftable and a new RecoveredClass object needs to be created // without a vftable and a new RecoveredClass object needs to be created
if (getClass(pointedToNamespace) == null) { if (getClass(pointedToNamespace) == null) {
addNoVftableClass(pointedToNamespace); createNewClass(pointedToNamespace, false);
} }
RecoveredClass pointedToClass = getClass(pointedToNamespace); RecoveredClass pointedToClass = getClass(pointedToNamespace);

View file

@ -2672,26 +2672,29 @@ public class RecoveredClassUtils {
return false; return false;
} }
/** /**
* Method to add class with no vftable to the namespace map * Method to create a new recovered class object and add it to the namespaceToClassMap
* @param namespace the namespace to put the new class in * @param namespace the namespace to put the new class in
* @return the recovered class\ * @param hasVftable true if class has at least one vftable, false otherwise
* @return the RecoveredClass object
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
public RecoveredClass addNoVftableClass(Namespace namespace) throws CancelledException { public RecoveredClass createNewClass(Namespace namespace, boolean hasVftable)
throws CancelledException {
String className = namespace.getName(); String className = namespace.getName();
String classNameWithNamespace = namespace.getName(true); String classNameWithNamespace = namespace.getName(true);
CategoryPath classPath =
extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath, CategoryPath classPath = extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath,
classNameWithNamespace); classNameWithNamespace);
RecoveredClass nonVftableClass = RecoveredClass newClass =
new RecoveredClass(className, classPath, namespace, dataTypeManager); new RecoveredClass(className, classPath, namespace, dataTypeManager);
nonVftableClass.setHasVftable(false); newClass.setHasVftable(hasVftable);
updateNamespaceToClassMap(namespace, nonVftableClass); updateNamespaceToClassMap(namespace, newClass);
return nonVftableClass; return newClass;
} }
@ -2736,23 +2739,6 @@ public class RecoveredClassUtils {
continue; continue;
} }
// promote any non-class namespaces in the vftableNamespace path to class namespaces
boolean success = promoteNamespaces(vftableNamespace.getSymbol());
if (!success) {
if (DEBUG) {
Msg.debug(this, "Unable to promote all non-class namespaces for " +
vftableNamespace.getName(true));
}
}
String className = vftableNamespace.getName();
String classNameWithNamespace = vftableNamespace.getName(true);
// Create Data Type Manager Category for given class
CategoryPath classPath =
extraUtils.createDataTypeCategoryPath(classDataTypesCategoryPath,
classNameWithNamespace);
// get only the functions from the ones that are not already processed structures // get only the functions from the ones that are not already processed structures
// return null if not an unprocessed table // return null if not an unprocessed table
List<Function> virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol, List<Function> virtualFunctions = getFunctionsFromVftable(vftableAddress, vftableSymbol,
@ -2764,24 +2750,24 @@ public class RecoveredClassUtils {
} }
// Check to see if already have an existing RecoveredClass object for the // Check to see if already have an existing RecoveredClass object for the
// class associated with the current vftable. If so, it indicates multi-inheritance // class associated with the current vftable.
RecoveredClass recoveredClass = getClass(vftableNamespace); RecoveredClass recoveredClass = getClass(vftableNamespace);
if (recoveredClass == null) { if (recoveredClass == null) {
// Create a RecoveredClass object for the current class // Create a RecoveredClass object for the current class
recoveredClass = recoveredClass = createNewClass(vftableNamespace, true);
new RecoveredClass(className, classPath, vftableNamespace, dataTypeManager);
recoveredClass.addVftableAddress(vftableAddress); recoveredClass.addVftableAddress(vftableAddress);
recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions);
// add recovered class to map
updateNamespaceToClassMap(vftableNamespace, recoveredClass);
// add it to the running list of RecoveredClass objects // add it to the running list of RecoveredClass objects
recoveredClasses.add(recoveredClass); recoveredClasses.add(recoveredClass);
} }
else { else {
recoveredClass.addVftableAddress(vftableAddress); recoveredClass.addVftableAddress(vftableAddress);
recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions); recoveredClass.addVftableVfunctionsMapping(vftableAddress, virtualFunctions);
if (!recoveredClasses.contains(recoveredClass)) {
recoveredClasses.add(recoveredClass);
}
} }
@ -2795,8 +2781,6 @@ public class RecoveredClassUtils {
Map<Address, Function> vftableReferenceToFunctionMapping = Map<Address, Function> vftableReferenceToFunctionMapping =
createVftableReferenceToFunctionMapping(referencesToVftable); createVftableReferenceToFunctionMapping(referencesToVftable);
// add this smaller mapping set to the global map
//vftableRefToFunctionMap.putAll(vftableReferenceToFunctionMapping);
//vftableReferenceToFunctionMapping //vftableReferenceToFunctionMapping
List<Function> possibleConstructorDestructorsForThisClass = List<Function> possibleConstructorDestructorsForThisClass =
@ -2819,25 +2803,38 @@ public class RecoveredClassUtils {
return recoveredClasses; return recoveredClasses;
} }
private boolean promoteNamespaces(Symbol symbol) throws CancelledException { public void promoteClassNamespaces(List<RecoveredClass> recoveredClasses)
throws CancelledException {
Iterator<RecoveredClass> classIterator = recoveredClasses.iterator();
while (classIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = classIterator.next();
Namespace classNamespace = recoveredClass.getClassNamespace();
promoteNamespaces(classNamespace);
}
}
private boolean promoteNamespaces(Namespace namespace) throws CancelledException {
Namespace namespace = symbol.getParentNamespace();
while (!namespace.isGlobal()) { while (!namespace.isGlobal()) {
monitor.checkCanceled(); monitor.checkCanceled();
SymbolType namespaceType = namespace.getSymbol().getSymbolType(); SymbolType namespaceType = namespace.getSymbol().getSymbolType();
if (namespaceType != SymbolType.CLASS) { // if it is a namespace but not a class and it is in our namespace map (which makes
// if it is a namespace but not a class we need to promote it to a class namespace // it a valid class) we need to promote it to a class namespace
if (namespaceType == SymbolType.NAMESPACE) { if (namespaceType != SymbolType.CLASS && namespaceType == SymbolType.NAMESPACE &&
namespaceToClassMap.get(namespace) != null) {
namespace = promoteToClassNamespace(namespace); namespace = promoteToClassNamespace(namespace);
if (namespace == null) { if (namespace == null) {
return false; return false;
} }
if (DEBUG) { //if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"Promoted namespace " + namespace.getName() + " to a class namespace"); "Promoted namespace " + namespace.getName(true) + " to a class namespace");
} //}
}
} }
else { else {
namespace = namespace.getParentNamespace(); namespace = namespace.getParentNamespace();
@ -2852,11 +2849,20 @@ public class RecoveredClassUtils {
*/ */
private Namespace promoteToClassNamespace(Namespace namespace) { private Namespace promoteToClassNamespace(Namespace namespace) {
SymbolType symbolType = namespace.getSymbol().getSymbolType();
if (symbolType == SymbolType.CLASS) {
return namespace;
}
if (symbolType != SymbolType.NAMESPACE) {
return namespace;
}
try { try {
Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace); Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace);
SymbolType symbolType = newClass.getSymbol().getSymbolType(); SymbolType newSymbolType = newClass.getSymbol().getSymbolType();
if (symbolType == SymbolType.CLASS) { if (newSymbolType == SymbolType.CLASS) {
return newClass; return newClass;
} }
if (DEBUG) { if (DEBUG) {
@ -3246,7 +3252,8 @@ public class RecoveredClassUtils {
if (parentClasses.isEmpty()) { if (parentClasses.isEmpty()) {
throw new Exception( throw new Exception(
recoveredClass.getName() + " should not have an empty class hierarchy"); recoveredClass.getClassNamespace().getName(true) +
" should not have an empty class hierarchy");
} }
// if size one it only includes self // if size one it only includes self