GP-972 Improvements to Gcc RTTI Class Recovery

This commit is contained in:
ghidra007 2021-06-10 14:19:33 -04:00 committed by ghidra1
parent c553971388
commit e1953ebb22
7 changed files with 652 additions and 254 deletions

View file

@ -680,7 +680,6 @@ public class GccRttiAnalysisScript extends GhidraScript {
/** /**
* Method to process the primary vtable for each "vtable" label * Method to process the primary vtable for each "vtable" label
* @return the vftable Address in the vtable
* @throws Exception if Data cannot be created * @throws Exception if Data cannot be created
*/ */
private void processVtables() throws Exception { private void processVtables() throws Exception {

View file

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// Script to apply any changes the user has made to class virtual function definitions, ie ones they // Script to apply any changes the user has made to recovered class virtual function definitions
// have edited in the data type manager. To run the script, put the cursor on any member of the // edited in the data type manager. To run the script, put the cursor on any member of the
// desired class in the listing then run the script. For each function definition in the given class // desired class in the listing then run the script. For each function definition in the given class
// that differs from the associated function signature in the listing, the script will update the // that differs from the associated function signature in the listing, the script will update the
// listing function signatures of any related virtual vunctions (ie parents and children) and update // listing function signatures of any related virtual functions belonging to parent and children
// related data types such as function definitions of the given class and related classes and also // classes. It will also update related data types including function definitions and vftable structures.
// field names in related vftable structures.
// Note: The script will not work if the vftable structures were not originally applied to // Note: The script will not work if the vftable structures were not originally applied to
// the vftables using the RecoverClassesFromRTTIScript. // the vftables using the RecoverClassesFromRTTIScript.
// At some point, the Ghidra API will be updated to do this automatically instead of needing the script to do so. // At some point, the Ghidra API will be updated to do this automatically instead of needing the
// script to do so.
//@category C++ //@category C++
import java.util.List; import java.util.List;

View file

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// Script to apply any changes the user has made to class virtual function signatures, ie ones they // Script to apply any changes the user has made to recovered class virtual function signatures
// have edited in the listing. To run the script, put the cursor on any member of the desired class in // edited in the listing. To run the script, put the cursor on any member of the desired class in
// the listing then run the script. For each function signature in the given class that differs from // the listing then run the script. For each function signature in the given class that differs from
// the associated function definition in the data type manager, the script will update the listing // the associated function definition in the data type manager, the script will update the listing
// function signatures of any related virtual vunctions (ie parents and children) and update related // function signatures of any related virtual functions belonging to parents and children classes.
// data types such as function definitions of the given class and related classes and also field names // It will also update related data types including function definitions and vftable structures.
// in related vftable structures.
// Note: The script will not work if the vftable structures were not originally applied to // Note: The script will not work if the vftable structures were not originally applied to
// the vftables using the RecoverClassesFromRTTIScript. // the vftables using the RecoverClassesFromRTTIScript.
// At some point, the Ghidra API will be updated to do this automatically instead of needing the script to do so. // At some point, the Ghidra API will be updated to do this automatically instead of needing the
// script to do so.
//@category C++ //@category C++
import java.util.List; import java.util.List;

View file

@ -35,18 +35,20 @@
// this script and default vfunctions named by this script are likely to change in the future // this script and default vfunctions named by this script are likely to change in the future
// once an official design for Object Oriented representation is determined. // once an official design for Object Oriented representation is determined.
// NOTE: Windows class recovery is more complete and tested than gcc class recovery, which is still // NOTE: Windows class recovery is more complete and tested than gcc class recovery, which is still
// in early stages of development. Gcc class data types have not been recovered yet but if the program // in early stages of development. Gcc class data types are only recovered for classes without multiple or
// has DWARF, there will be some amount of data recovered by the DWARF analyzer in the DWARF data folder. // virtual inheritance but if the program contains DWARF, there will be some amount of data recovered
// by the DWARF analyzer.
// NOTE: For likely the best results, run this script on freshly analyzed programs. No testing has been // NOTE: For likely the best results, run this script on freshly analyzed programs. No testing has been
// done on user marked-up programs. // done on user marked-up programs.
// NOTE: After running this script if you edit function signatures in the listing for a particular // NOTE: After running this script if you edit function signatures in the listing for a particular
// class and wish to update the corresponding class data (function definition data types, vftable // class and wish to update the corresponding class data function definition data types (vftable
// structure field names, ...) then you can run the ApplyClassFunctionSignatureUpdatesScript.java // structure field names, ...) then you can run the ApplyClassFunctionSignatureUpdatesScript.java
// to have it do so for you. // to have it do so for you. See that script's description for more info.
// Conversely, if you update a particular class's function definitions in the data type manager and // Conversely, if you update a particular class's function definitions in the data type manager and
// wish to have related function signatures in the listing updated, as well as other data types that // wish to have related function signatures in the listing updated, as well as other data types that
// are related, then run the ApplyClassFunctionDefinitionsUpdatesScript.java to do so. At some point, // are related, then run the ApplyClassFunctionDefinitionsUpdatesScript.java to do so. See that script's
// the Ghidra API will be updated to do this all automatically instead of needing the scripts to do so. // description for more info. At some point, the Ghidra API will be updated to do the updates
// automatically instead of needing the mentioned scripts to do so.
//@category C++ //@category C++
@ -152,6 +154,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (isWindows()) { if (isWindows()) {
isPDBLoaded = isPDBLoadedInProgram(); isPDBLoaded = isPDBLoadedInProgram();
nameVfunctions = !isPDBLoaded; nameVfunctions = !isPDBLoaded;
recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram, recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram,
@ -159,7 +162,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor); USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor);
} }
else if (isGcc()) { else if (isGcc()) {
// for now assume gcc has named vfunctions until a way to check is developed
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,
@ -176,7 +179,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return; return;
} }
// possibly more picky subtype than just checking windows/gcc
if (!recoverClassesFromRTTI.isValidProgramType()) { if (!recoverClassesFromRTTI.isValidProgramType()) {
println("This script will not work on this program type"); println("This script will not work on this program type");
return; return;
@ -465,9 +467,41 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
*/ */
private boolean isGcc() { private boolean isGcc() {
isGcc = boolean isELF = currentProgram.getExecutableFormat().contains("ELF");
if (!isELF) {
return false;
}
boolean isCompilerSpecGcc =
currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase( currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase(
"gcc"); "gcc");
if (isCompilerSpecGcc) {
return true;
}
MemoryBlock commentBlock = currentProgram.getMemory().getBlock(".comment");
if (commentBlock == null) {
return false;
}
if (!commentBlock.isLoaded()) {
return false;
}
// check memory bytes in block for GCC: bytes
byte[] gccBytes = { (byte) 0x47, (byte) 0x43, (byte) 0x43, (byte) 0x3a };
byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
Address found = currentProgram.getMemory().findBytes(commentBlock.getStart(),
commentBlock.getEnd(), gccBytes, maskBytes, true, monitor);
if (found == null) {
isGcc = false;
}
else {
isGcc = true;
}
return isGcc; return isGcc;
} }

View file

@ -51,6 +51,10 @@ import ghidra.util.task.TaskMonitor;
public class RTTIGccClassRecoverer extends RTTIClassRecoverer { public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private static final String VTABLE_LABEL = "vtable"; private static final String VTABLE_LABEL = "vtable";
private static final String CLASS_VTABLE_PTR_FIELD_EXT = "vftablePtr";
private static final int NONE = -1;
private static final int UNKNOWN = -2;
private static final boolean DEBUG = false;
Map<RecoveredClass, Address> classToTypeinfoMap = new HashMap<RecoveredClass, Address>(); Map<RecoveredClass, Address> classToTypeinfoMap = new HashMap<RecoveredClass, Address>();
Address class_type_info_vtable = null; Address class_type_info_vtable = null;
@ -115,6 +119,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
figureOutClassDataMembers(recoveredClasses); figureOutClassDataMembers(recoveredClasses);
createAndApplyClassStructures(recoveredClasses);
return recoveredClasses; return recoveredClasses;
} }
catch (CancelledException e) { catch (CancelledException e) {
@ -132,31 +138,50 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private boolean isGcc() { private boolean isGcc() {
boolean isGcc = boolean isELF = program.getExecutableFormat().contains("ELF");
if (!isELF) {
return false;
}
boolean isCompilerSpecGcc =
program.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase("gcc"); program.getCompilerSpec().getCompilerSpecID().getIdAsString().equalsIgnoreCase("gcc");
return isGcc; if (isCompilerSpecGcc) {
return true;
}
MemoryBlock commentBlock = program.getMemory().getBlock(".comment");
if (commentBlock == null) {
return false;
}
if (!commentBlock.isLoaded()) {
return false;
}
// check memory bytes in block for GCC: bytes
byte[] gccBytes = { (byte) 0x47, (byte) 0x43, (byte) 0x43, (byte) 0x3a };
byte[] maskBytes = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
Address found = program.getMemory().findBytes(commentBlock.getStart(),
commentBlock.getEnd(),
gccBytes, maskBytes, true, monitor);
if (found == null) {
return false;
}
return true;
} }
/** /**
* Method to check for at least one special RTTI vtable * Method to check for at least one special RTTI vtable
* @return * @return true if the program has at least one special vtable, false if none
* @throws CancelledException * @throws CancelledException if cancelled
*/ */
private boolean hasSpecialVtable() throws CancelledException { private boolean hasSpecialVtable() throws CancelledException {
boolean hasSpecialVtable = createSpecialVtables(); boolean hasSpecialVtable = createSpecialVtables();
return hasSpecialVtable; return hasSpecialVtable;
// class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info");
// si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info");
// vmi_class_type_info_vtable =
// findSpecialVtable("__cxxabiv1", "__vmi_class_type_info");
// if (class_type_info_vtable == null && si_class_type_info_vtable == null &&
// vmi_class_type_info_vtable == null) {
//
// return false;
// }
// return true;
} }
private Address findSpecialVtable(String namespace, String name) throws CancelledException { private Address findSpecialVtable(String namespace, String name) throws CancelledException {
@ -209,19 +234,12 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private List<RecoveredClass> processGccRTTI() throws CancelledException, Exception { private List<RecoveredClass> processGccRTTI() throws CancelledException, Exception {
// attempt to find the three special typeinfo structs and vtables
// boolean continueProcessing = findSpecialTypeinfosAndVtables();
// if (!continueProcessing) {
// return null;
// }
// 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
//TODO: *****change to not be a script - make another class and call from here and from script
//runScript("GccRttiAnalysisScript.java");
createGccRttiData(); createGccRttiData();
// find all typeinfo symbols and get their class namespace and create RecoveredClass object // find all typeinfo symbols and get their class namespace and create RecoveredClass object
List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet( List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet(
program.getAddressFactory().getAddressSet(), "typeinfo", true); program.getAddressFactory().getAddressSet(), "typeinfo", true);
@ -232,7 +250,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// process vtables and create classes for the vtables that have no typeinfo // process vtables and create classes for the vtables that have no typeinfo
List<Symbol> vftableSymbols = findVftablesFromVtables(); List<Symbol> vftableSymbols = findVftablesFromVtables();
if (DEBUG) {
Msg.debug(this, "Found " + vftableSymbols.size() + "vftableSymbols"); Msg.debug(this, "Found " + vftableSymbols.size() + "vftableSymbols");
}
List<RecoveredClass> recoveredClasses = List<RecoveredClass> recoveredClasses =
recoverClassesFromVftables(vftableSymbols, true, true); recoverClassesFromVftables(vftableSymbols, true, true);
@ -260,7 +280,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
// add properties and parents to each class // add properties and parents to each class
if (DEBUG) {
Msg.debug(this, "Found " + typeinfoSymbols.size() + " typeinfo symbols"); Msg.debug(this, "Found " + typeinfoSymbols.size() + " typeinfo symbols");
}
Iterator<Symbol> typeinfoIterator = typeinfoSymbols.iterator(); Iterator<Symbol> typeinfoIterator = typeinfoSymbols.iterator();
while (typeinfoIterator.hasNext()) { while (typeinfoIterator.hasNext()) {
@ -283,74 +305,106 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// 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) {
// this shoudln't be null at this point // this shoudln't be null at this point
if (DEBUG) {
Msg.debug(this, "Shouldn't be a null class here: " + classNamespace.getName()); Msg.debug(this, "Shouldn't be a null class here: " + classNamespace.getName());
}
recoveredClass = addNoVftableClass(classNamespace); recoveredClass = addNoVftableClass(classNamespace);
recoveredClasses.add(recoveredClass); recoveredClasses.add(recoveredClass);
} }
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) { if (specialTypeinfoRef == null) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"No special typeinfo reference found. Cannot process typeinfo struct at " + "No special typeinfo reference found. Cannot process typeinfo struct at " +
typeinfoAddress.toString()); typeinfoAddress.toString());
}
continue; continue;
} }
if (!isSpecialTypeinfo(specialTypeinfoRef)) { if (!isSpecialTypeinfo(specialTypeinfoRef)) {
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (!hasExternalBlock()) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"Special typeinfo reference is not equal to one of the three special type infos. Cannot process typeinfo struct at " + "Special typeinfo reference is not equal to one of the three special type infos. Cannot process typeinfo struct at " +
typeinfoAddress.toString()); typeinfoAddress.toString());
}
continue;
}
// use referenced vtable symbol name instead since when in EXTERNAL block
// since can't get at the typeinfo ref in that block
if (!isSpecialVtable(specialTypeinfoRef)) {
if (DEBUG) {
Msg.debug(this,
"Special typeinfo reference is not equal to one of the three special type infos. Cannot process typeinfo struct at " +
typeinfoAddress.toString());
}
continue; continue;
} }
if (specialTypeinfoRef.equals(class_type_info)) { }
if (specialTypeinfoRef.equals(class_type_info) ||
specialTypeinfoRef.equals(class_type_info_vtable)) {
nonInheritedGccClasses.add(recoveredClass); nonInheritedGccClasses.add(recoveredClass);
continue; continue;
} }
// per docs those on this list are // per docs those on this list are
// classes containing only a single, public, non-virtual base at offset zero // classes containing only a single, public, non-virtual base at offset zero
if (specialTypeinfoRef.equals(si_class_type_info)) { if (specialTypeinfoRef.equals(si_class_type_info) ||
specialTypeinfoRef.equals(si_class_type_info_vtable)) {
singleInheritedGccClasses.add(recoveredClass); singleInheritedGccClasses.add(recoveredClass);
RecoveredClass parentClass = getSiClassParent(typeinfoAddress); RecoveredClass parentClass = getSiClassParent(typeinfoAddress);
if (parentClass != null) { if (parentClass != null) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
recoveredClass.getName() + " adding parent " + parentClass.getName()); recoveredClass.getName() + " adding parent " + parentClass.getName());
}
updateClassWithParent(parentClass, recoveredClass); updateClassWithParent(parentClass, recoveredClass);
if (!recoveredClasses.contains(parentClass)) { if (!recoveredClasses.contains(parentClass)) {
if (DEBUG) {
Msg.debug(this, recoveredClass.getName() + Msg.debug(this, recoveredClass.getName() +
" adding parent I didn't know about" + parentClass.getName()); " adding an unknown parent " + parentClass.getName());
}
recoveredClasses.add(parentClass); recoveredClasses.add(parentClass);
} }
} }
else { else {
if (DEBUG) {
Msg.debug(this, "Could not get si parent from typeinfoAddress " + Msg.debug(this, "Could not get si parent from typeinfoAddress " +
typeinfoAddress.toString()); typeinfoAddress.toString());
} }
}
continue; continue;
} }
if (specialTypeinfoRef.equals(vmi_class_type_info)) { if (specialTypeinfoRef.equals(vmi_class_type_info) ||
specialTypeinfoRef.equals(vmi_class_type_info_vtable)) {
multiInheritedGccClasses.add(recoveredClass); multiInheritedGccClasses.add(recoveredClass);
recoveredClass.setHasMultipleInheritance(true); recoveredClass.setHasMultipleInheritance(true);
recoveredClass.setHasParentClass(true);
// TODO: get info out of the flags in the typeinfo for this class
// 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 (!recoveredClasses.containsAll(parents)) { if (!recoveredClasses.containsAll(parents)) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"missing some parents from " + recoveredClass.getName() + " on list"); "missing some parents from " + recoveredClass.getName() + " on list");
} }
}
// newNonVftableClasses = // newNonVftableClasses =
// addMissingClasses(parents, newNonVftableClasses, recoveredClasses); // addMissingClasses(parents, newNonVftableClasses, recoveredClasses);
} }
@ -366,9 +420,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
Msg.debug(this, "Processing class " + recoveredClass.getName()); Msg.debug(this, "Processing class " + recoveredClass.getName());
List<Address> vftableAddresses = recoveredClass.getVftableAddresses(); List<Address> vftableAddresses = recoveredClass.getVftableAddresses();
if (vftableAddresses.isEmpty()) {
Msg.debug(this, recoveredClass.getName() + " has no vftable");
}
Iterator<Address> vftableAddressIterator = vftableAddresses.iterator(); Iterator<Address> vftableAddressIterator = vftableAddresses.iterator();
while (vftableAddressIterator.hasNext()) { while (vftableAddressIterator.hasNext()) {
@ -382,9 +433,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
Msg.debug(this, "Gets to create called function map.");
createCalledFunctionMap(recoveredClasses); createCalledFunctionMap(recoveredClasses);
Msg.debug(this, "Gets to assign c/ds.");
assignConstructorsAndDestructorsUsingExistingName(recoveredClasses); assignConstructorsAndDestructorsUsingExistingName(recoveredClasses);
return recoveredClasses; return recoveredClasses;
@ -393,9 +443,14 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
private void createGccRttiData() throws CancelledException, Exception { private void createGccRttiData() throws CancelledException, Exception {
// find the three special vtables and replace the incorrectly made array with // TODO: find typeinfo's using other means besides their names since in static case they
// data types found in vtable // aren't named
//createSpecialVtables(); // 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 // find all typeinfo symbols and get their class namespace and create RecoveredClass object
List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet( List<Symbol> typeinfoSymbols = extraUtils.getListOfSymbolsInAddressSet(
@ -410,7 +465,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
/** /**
* Method to process the primary vtable for each "vtable" label * Method to process the primary vtable for each "vtable" label
* @return the vftable Address in the vtable
* @throws Exception if Data cannot be created * @throws Exception if Data cannot be created
*/ */
private void processVtables() throws Exception { private void processVtables() throws Exception {
@ -459,8 +513,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// find the special type info ref // find the special type info ref
Address typeinfoAddress = findNextTypeinfoRef(vtableAddress); Address typeinfoAddress = findNextTypeinfoRef(vtableAddress);
if (typeinfoAddress == null) { if (typeinfoAddress == null) {
if (DEBUG) {
Msg.debug(this, vtableNamespace.getName() + Msg.debug(this, vtableNamespace.getName() +
" vtable has no typeinfo ref after vtable at " + vtableAddress.toString()); " vtable has no typeinfo ref after vtable at " + vtableAddress.toString());
}
return; return;
} }
@ -764,29 +820,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return false; return false;
} }
/**
* Method to find the (up to three) special gcc vtables and replace the incorrectly made array with the
* correct data types. Also creates a type info symbol at the correct offset in the table.
* @throws CancelledException if cancelled
*/
private void createSpecialVtablesOld() throws CancelledException {
if (class_type_info_vtable != null) {
class_type_info = createSpecialVtable(class_type_info_vtable);
}
if (si_class_type_info_vtable != null) {
si_class_type_info = createSpecialVtable(si_class_type_info_vtable);
}
if (vmi_class_type_info_vtable != null) {
vmi_class_type_info = createSpecialVtable(vmi_class_type_info_vtable);
}
return;
}
/** /**
* Method to replace the array incorrectly placed at special vftable with longs followed by * Method to replace the array incorrectly placed at special vftable with longs followed by
* typeinfo label * typeinfo label
@ -893,22 +926,39 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
if (isSpecialTypeinfo(typeinfoAddress)) { if (isSpecialTypeinfo(typeinfoAddress)) {
continue; 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;
}
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) { if (specialTypeinfoRef == null) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"No special typeinfo reference found. Cannot process typeinfo struct at " + "No special typeinfo reference found. Cannot process typeinfo struct at " +
typeinfoAddress.toString()); typeinfoAddress.toString());
}
continue; continue;
} }
if (!isSpecialTypeinfo(specialTypeinfoRef)) { if (!isSpecialTypeinfo(specialTypeinfoRef)) {
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (!hasExternalBlock()) {
continue; continue;
} }
// use referenced vtable symbol name instead since when in EXTERNAL block
// since can't get at the typeinfo ref in that block
if (!isSpecialVtable(specialTypeinfoRef)) {
continue;
}
}
try { try {
// 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)) {
api.clearListing(typeinfoAddress, api.clearListing(typeinfoAddress,
typeinfoAddress.add(classTypeInfoStructure.getLength())); typeinfoAddress.add(classTypeInfoStructure.getLength()));
api.createData(typeinfoAddress, classTypeInfoStructure); api.createData(typeinfoAddress, classTypeInfoStructure);
@ -916,7 +966,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
// create a "single inheritance" struct here // create a "single inheritance" struct here
if (specialTypeinfoRef.equals(si_class_type_info)) { if (specialTypeinfoRef.equals(si_class_type_info) ||
specialTypeinfoRef.equals(si_class_type_info_vtable)) {
api.clearListing(typeinfoAddress, api.clearListing(typeinfoAddress,
typeinfoAddress.add(siClassTypeInfoStructure.getLength() - 1)); typeinfoAddress.add(siClassTypeInfoStructure.getLength() - 1));
api.createData(typeinfoAddress, siClassTypeInfoStructure); api.createData(typeinfoAddress, siClassTypeInfoStructure);
@ -924,7 +975,8 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
// create a "virtual multip inheritance" struct here // create a "virtual multip inheritance" struct here
if (specialTypeinfoRef.equals(vmi_class_type_info)) { if (specialTypeinfoRef.equals(vmi_class_type_info) ||
specialTypeinfoRef.equals(vmi_class_type_info_vtable)) {
// get num base classes // get num base classes
int offsetOfNumBases = 2 * defaultPointerSize + 4; int offsetOfNumBases = 2 * defaultPointerSize + 4;
@ -1060,22 +1112,31 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
extraUtils.getAddress(typeinfoAddress, inheritanceTypeFlagOffset); extraUtils.getAddress(typeinfoAddress, inheritanceTypeFlagOffset);
if (inheritanceFlagAddress == null) { if (inheritanceFlagAddress == null) {
//TODO: throw exception if (DEBUG) {
Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() + Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() +
" plus offset " + inheritanceTypeFlagOffset); " plus offset " + inheritanceTypeFlagOffset);
}
return parentClassList; return parentClassList;
} }
// TODO: process the inheritance flag // process the multiple inheritance flag
try { try {
int inheritanceFlagValue = api.getInt(inheritanceFlagAddress); int inheritanceFlagValue = api.getInt(inheritanceFlagAddress);
Msg.debug(this, if (inheritanceFlagValue == 0) {
inheritanceFlagAddress.toString() + " inheritanceFlag = " + inheritanceFlagValue); recoveredClass.hasMultipleInheritance();
}
// TODO: add flags to class for non-diamond repeated and diamond shape types
if (DEBUG) {
Msg.debug(this, inheritanceFlagAddress.toString() + " inheritanceFlag = " +
inheritanceFlagValue);
}
} }
catch (MemoryAccessException e) { catch (MemoryAccessException e) {
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);
@ -1083,7 +1144,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
!baseClassArrayData.getBaseDataType().getName().startsWith( !baseClassArrayData.getBaseDataType().getName().startsWith(
"BaseClassTypeInfoStructure")) { "BaseClassTypeInfoStructure")) {
// TODO: throw exception if (DEBUG) {
Msg.debug(this, "Could not acess baseClassArray at " +
typeinfoStructure.toString() + baseClassArrayOffset);
}
return parentClassList; return parentClassList;
} }
@ -1103,8 +1167,11 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentRefAddress); RecoveredClass parentClass = getParentClassFromParentTypeInfoRef(parentRefAddress);
if (parentClass != null) { if (parentClass != null) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
recoveredClass.getName() + " adding vmi parent " + parentClass.getName()); recoveredClass.getName() + " adding vmi parent " + parentClass.getName());
}
updateClassWithParent(parentClass, recoveredClass); updateClassWithParent(parentClass, recoveredClass);
parentClassList.add(parentClass); parentClassList.add(parentClass);
} }
@ -1142,7 +1209,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
} }
if (DEBUG) {
Msg.debug(this, recoveredClass.getName() + " has " + numParents + " parents"); Msg.debug(this, recoveredClass.getName() + " has " + numParents + " parents");
}
return parentClassList; return parentClassList;
} }
@ -1158,8 +1228,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
Address parentTypeinfoRef = extraUtils.getAddress(typeinfoAddress, offset); Address parentTypeinfoRef = extraUtils.getAddress(typeinfoAddress, offset);
if (parentTypeinfoRef == null) { if (parentTypeinfoRef == null) {
if (DEBUG) {
Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() + Msg.debug(this, "ERROR: Could not access address " + typeinfoAddress.toString() +
" plus offset " + offset); " plus offset " + offset);
}
return null; return null;
} }
@ -1253,72 +1325,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return true; return true;
} }
/**
* Method to find the (up to three) special gcc typeinfos and vtables
* @return true if all found tables have a typeinfo symbol created successfully
* @throws CancelledException if cancelled
*/
private boolean findSpecialTypeinfosAndVtables() throws CancelledException {
class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__class_type_info");
if (class_type_info_vtable == null) {
Msg.debug(this,
"***** __class_type_info vtable not found --> no classes without parents");
}
else {
Symbol class_type_info_symbol =
getSymbolInNamespaces("__cxxabiv1", "__class_type_info", "typeinfo");
if (class_type_info_symbol == null) {
Msg.debug(this,
"__class_type_info typeinfo not found -- cannot continue gcc rtti processing");
return false;
}
class_type_info = class_type_info_symbol.getAddress();
}
si_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__si_class_type_info");
if (si_class_type_info_vtable == null) {
Msg.debug(this, "__si_class_type_info vtable not found --> no single parent classes");
}
else {
Symbol si_class_type_info_symbol =
getSymbolInNamespaces("__cxxabiv1", "__si_class_type_info", "typeinfo");
if (si_class_type_info_symbol == null) {
Msg.debug(this,
"__si_class_type_info typeinfo not found -- cannot continue gcc rtti processing");
return false;
}
si_class_type_info = si_class_type_info_symbol.getAddress();
}
vmi_class_type_info_vtable = findSpecialVtable("__cxxabiv1", "__vmi_class_type_info");
if (vmi_class_type_info_vtable == null) {
Msg.debug(this, "__vmi_class_type_info vtable not found --> no multi-parent classes");
}
else {
Symbol vmi_class_type_info_symbol =
getSymbolInNamespaces("__cxxabiv1", "__vmi_class_type_info", "typeinfo");
if (vmi_class_type_info_symbol == null) {
Msg.debug(this,
"__vmi_class_type_info typeinfo not found -- cannot continue gcc rtti processing");
return false;
}
vmi_class_type_info = vmi_class_type_info_symbol.getAddress();
}
if (class_type_info_vtable == null && si_class_type_info_vtable == null &&
vmi_class_type_info_vtable == null) {
Msg.debug(this,
"Since there are no class typeinfo tables this program does not appear to have RTTI.");
return false;
}
return true;
}
// TODO: don't delete - its call is commented out waiting for more work above // TODO: don't delete - its call is commented out waiting for more work above
private List<RecoveredClass> addMissingClasses(List<RecoveredClass> subset, private List<RecoveredClass> addMissingClasses(List<RecoveredClass> subset,
List<RecoveredClass> newList, List<RecoveredClass> classList) List<RecoveredClass> newList, List<RecoveredClass> classList)
@ -1467,15 +1473,17 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// find the special type info ref // find the special type info ref
Address typeinfoAddress = findNextTypeinfoRef(vtableAddress); Address typeinfoAddress = findNextTypeinfoRef(vtableAddress);
if (typeinfoAddress == null) { if (typeinfoAddress == null) {
if (DEBUG) {
Msg.debug(this, vtableAddress.toString() + " " + vtableNamespace.getName() + Msg.debug(this, vtableAddress.toString() + " " + vtableNamespace.getName() +
" vtable has no typeinfo ref"); " vtable has no typeinfo ref");
}
continue; continue;
} }
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) {
//TODO: print to see if any happen addNoVftableClass(vtableNamespace);
// if so should also add to no vftable class // if so should also add to no vftable class
continue; continue;
} }
@ -1493,12 +1501,21 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
/** /**
* Method to check if given typeinfo is one of the three special ones * Method to check if given typeinfo is one of the three special ones
* @param typeinfoAddress the given typeinfo address * @param address the given typeinfo address
* @return true if it is a special one, false otherwise * @return true if it is a special one, false otherwise
*/ */
private boolean isSpecialTypeinfo(Address typeinfoAddress) { private boolean isSpecialTypeinfo(Address address) {
if (typeinfoAddress.equals(class_type_info) || typeinfoAddress.equals(si_class_type_info) || if (address.equals(class_type_info) || address.equals(si_class_type_info) ||
typeinfoAddress.equals(vmi_class_type_info)) { address.equals(vmi_class_type_info)) {
return true;
}
return false;
}
private boolean isSpecialVtable(Address address) {
if (address.equals(class_type_info_vtable) ||
address.equals(si_class_type_info_vtable) ||
address.equals(vmi_class_type_info_vtable)) {
return true; return true;
} }
return false; return false;
@ -1521,6 +1538,14 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
if (isSpecialTypeinfo(typeinfoAddress)) { if (isSpecialTypeinfo(typeinfoAddress)) {
continue; 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;
}
// TODO: make sure it is a valid typeinfo
Namespace classNamespace = typeinfoSymbol.getParentNamespace(); Namespace classNamespace = typeinfoSymbol.getParentNamespace();
@ -1545,23 +1570,36 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress); Address specialTypeinfoRef = extraUtils.getSingleReferencedAddress(typeinfoAddress);
if (specialTypeinfoRef == null) { if (specialTypeinfoRef == null) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"No special typeinfo reference found. Cannot process typeinfo struct at " + "No special typeinfo reference found. Cannot process typeinfo struct at " +
typeinfoAddress.toString()); typeinfoAddress.toString());
}
continue; continue;
} }
if (!isSpecialTypeinfo(specialTypeinfoRef)) { if (!isSpecialTypeinfo(specialTypeinfoRef)) {
// check for EXTERNAL block and look for specialTypeinfoRef there
// if fix works, put external block error message and to contact us
if (!hasExternalBlock()) {
continue; continue;
} }
// use referenced vtable symbol name instead since when in EXTERNAL block
// since can't get at the typeinfo ref in that block
if (!isSpecialVtable(specialTypeinfoRef)) {
continue;
}
}
// 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)
// ??? it isn't clear whether these are always public or not // ??? it isn't clear whether these are always public or not
if (specialTypeinfoRef.equals(class_type_info)) { if (specialTypeinfoRef.equals(class_type_info) ||
specialTypeinfoRef.equals(class_type_info_vtable)) {
// //TODO: make this a method - addGccNoInhClass // //TODO: make this a method - addGccNoInhClass
//nonInheritedClasses.add(recoveredClass);
recoveredClass.setHasSingleInheritance(true); recoveredClass.setHasSingleInheritance(true);
recoveredClass.setHasParentClass(false); recoveredClass.setHasParentClass(false);
recoveredClass.setInheritsVirtualAncestor(false); recoveredClass.setInheritsVirtualAncestor(false);
@ -1572,8 +1610,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// per docs those on this list are // per docs those on this list are
// classes containing only a single, public, non-virtual base at offset zero // classes containing only a single, public, non-virtual base at offset zero
// update: it isn't clear if never inherit virtual - may have found example // update: it isn't clear if never inherit virtual - may have found example
if (specialTypeinfoRef.equals(si_class_type_info)) { if (specialTypeinfoRef.equals(si_class_type_info) ||
// singleInheritedClasses.add(recoveredClass); specialTypeinfoRef.equals(si_class_type_info_vtable)) {
// TODO: make this a method and pull the part out of add parents that handles the // TODO: make this a method and pull the part out of add parents that handles the
// single parent one // single parent one
recoveredClass.setHasSingleInheritance(true); recoveredClass.setHasSingleInheritance(true);
@ -1583,11 +1622,10 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
continue; continue;
} }
if (specialTypeinfoRef.equals(vmi_class_type_info)) { if (specialTypeinfoRef.equals(vmi_class_type_info) ||
specialTypeinfoRef.equals(vmi_class_type_info_vtable)) {
// multiInheritedClasses.add(recoveredClass);
recoveredClass.setHasMultipleInheritance(true); recoveredClass.setHasMultipleInheritance(true);
recoveredClass.setHasParentClass(true);
} }
} }
return recoveredClasses; return recoveredClasses;
@ -1833,5 +1871,246 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return false; return false;
} }
/**
* Method to call create and apply class structures method starting with top parent classes
* and non-virtual classes then the children and their children until all classes are processed.
* @param recoveredClasses List of classes
* @throws CancelledException when cancelled
* @throws Exception if issue creating data
*/
private void createAndApplyClassStructures(List<RecoveredClass> recoveredClasses)
throws CancelledException, Exception {
List<RecoveredClass> listOfClasses = new ArrayList<RecoveredClass>(recoveredClasses);
Iterator<RecoveredClass> recoveredClassIterator = recoveredClasses.iterator();
// first process all the classes with no parents
while (recoveredClassIterator.hasNext()) {
monitor.checkCanceled();
RecoveredClass recoveredClass = recoveredClassIterator.next();
if (recoveredClass.hasMultipleInheritance()) {
continue;
}
if (recoveredClass.hasParentClass()) {
continue;
}
if (!recoveredClass.hasVftable()) {
createClassStructureWhenNoParentOrVftable(recoveredClass);
listOfClasses.remove(recoveredClass);
continue;
}
processDataTypes(recoveredClass);
listOfClasses.remove(recoveredClass);
}
// now process the classes that have all parents processed
// continue looping until all classes are processed
int numLoops = 0;
while (!listOfClasses.isEmpty()) {
monitor.checkCanceled();
// put in stop gap measure in case some classes never get all
// parents processed for some reason
if (numLoops == 100) {
return;
}
numLoops++;
recoveredClassIterator = recoveredClasses.iterator();
while (recoveredClassIterator.hasNext()) {
RecoveredClass recoveredClass = recoveredClassIterator.next();
monitor.checkCanceled();
if (!listOfClasses.contains(recoveredClass)) {
continue;
}
if (!allAncestorDataHasBeenCreated(recoveredClass)) {
continue;
}
processDataTypes(recoveredClass);
listOfClasses.remove(recoveredClass);
}
}
}
/**
* Method to create all the class data types for the current class, name all the class functions, and put them all into the class namespace
* @param recoveredClass current class
* @throws CancelledException when cancelled
* @throws Exception naming exception
*/
private void processDataTypes(RecoveredClass recoveredClass)
throws CancelledException, Exception {
// can't handle gcc multi and/or virtual class data types yet
if (multiInheritedGccClasses.contains(recoveredClass) ||
recoveredClass.inheritsVirtualAncestor()) {
return;
}
// shouldn't happen if we get to this point since it should be all single or no inherited classes
// but check anyway
if (recoveredClass.getVftableAddresses().size() > 1) {
return;
}
if (!recoveredClass.hasVftable()) {
createSimpleClassStructure(recoveredClass, null);
// return in this case because if there is no vftable for a class the script cannot
// identify any member functions so there is no need to process the rest of this method
return;
}
// create pointers to empty vftable structs so they can be added to the class data type
// then filled in later
Map<Address, DataType> vfPointerDataTypes = createEmptyVfTableStructs(recoveredClass);
// create current class structure and add pointer to vftable, all parent member data strutures, and class member data structure
Structure classStruct = null;
classStruct = createSimpleClassStructure(recoveredClass, vfPointerDataTypes);
// TODO: check for DWARF -- if none add c/d/etc to class
// if (!isDWARFLoaded) {
//
// // Now that we have a class data type
// // name constructor and destructor functions and put into the class namespace
// addConstructorsToClassNamespace(recoveredClass, classStruct);
// addDestructorsToClassNamespace(recoveredClass);
// addNonThisDestructorsToClassNamespace(recoveredClass);
// addVbaseDestructorsToClassNamespace(recoveredClass);
// addVbtableToClassNamespace(recoveredClass);
//
// // add secondary label on functions with inlined constructors or destructors
// createInlinedConstructorComments(recoveredClass);
// createInlinedDestructorComments(recoveredClass);
// createIndeterminateInlineComments(recoveredClass);
//
// // add label on constructor destructor functions that could not be determined which were which
// createIndeterminateLabels(recoveredClass);
// }
// This is done after the class structure is created and added to the dtmanager
// because if done before the class structures are created
// then empty classes will get auto-created in the wrong place
// when the vfunctions are put in the class
fillInAndApplyVftableStructAndNameVfunctions(recoveredClass, vfPointerDataTypes);
}
private Structure createSimpleClassStructure(RecoveredClass recoveredClass,
Map<Address, DataType> vfPointerDataTypes) throws Exception {
String className = recoveredClass.getName();
CategoryPath classPath = recoveredClass.getClassPath();
// get either existing structure if prog has a structure created by pdb or computed structure
// from decompiled construtor(s) info
Structure classStructure;
if (recoveredClass.hasExistingClassStructure()) {
classStructure = recoveredClass.getExistingClassStructure();
}
else {
classStructure = recoveredClass.getComputedClassStructure();
}
int structLen = 0;
if (classStructure != null) {
structLen = addAlignment(classStructure.getLength());
}
Structure classStructureDataType =
new StructureDataType(classPath, className, structLen, dataTypeManager);
List<RecoveredClass> parentList = recoveredClass.getParentList();
// shouldn't happen but check anyway
if (parentList.size() > 1) {
return classStructureDataType;
}
// if no inheritance - add pointer to class vftable structure
if (nonInheritedGccClasses.contains(recoveredClass) && vfPointerDataTypes != null) {
// the size was checked before calling this method so we know there is one and only
// one for this simple case
Address vftableAddress = recoveredClass.getVftableAddresses().get(0);
DataType classVftablePointer = vfPointerDataTypes.get(vftableAddress);
// simple case the offset for vftablePtr is 0
if (structUtils.canAdd(classStructureDataType, 0, classVftablePointer.getLength(),
monitor)) {
classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType,
0, classVftablePointer, CLASS_VTABLE_PTR_FIELD_EXT, monitor);
}
}
// if single inheritance put parent and data
else if (singleInheritedGccClasses.contains(recoveredClass)) {
RecoveredClass baseClass = parentList.get(0);
Structure baseClassStructure = getClassStructureFromDataTypeManager(baseClass);
// simple case - base class at offset 0
if (structUtils.canAdd(classStructureDataType, 0, baseClassStructure.getLength(),
monitor)) {
classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType,
0, baseClassStructure, baseClassStructure.getName(), monitor);
}
}
// figure out class data, if any, create it and add to class structure
int dataOffset = getDataOffset(recoveredClass, classStructureDataType);
int dataLen = UNKNOWN;
if (dataOffset != NONE) {
dataLen = structUtils.getNumberOfUndefinedsStartingAtOffset(classStructureDataType,
dataOffset, monitor);
}
if (dataLen != UNKNOWN && dataLen > 0) {
Structure recoveredClassDataStruct = createClassMemberDataStructure(recoveredClass,
classStructureDataType, dataLen, dataOffset);
if (recoveredClassDataStruct != null) {
classStructureDataType = structUtils.addDataTypeToStructure(classStructureDataType,
dataOffset, recoveredClassDataStruct, "data", monitor);
}
}
if (classStructureDataType.getNumComponents() == classStructureDataType.getNumDefinedComponents()) {
classStructureDataType.setPackingEnabled(true);
}
classStructureDataType.setDescription(createParentStringBuffer(recoveredClass).toString());
classStructureDataType = (Structure) dataTypeManager.addDataType(classStructureDataType,
DataTypeConflictHandler.DEFAULT_HANDLER);
return classStructureDataType;
}
private boolean hasExternalBlock() {
MemoryBlock externalBlock = program.getMemory().getBlock("EXTERNAL");
if (externalBlock == null) {
return false;
}
return true;
}
} }

View file

@ -485,7 +485,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
for (int i = 0; i < numBaseClasses; i++) { for (int i = 0; i < numBaseClasses; i++) {
monitor.checkCanceled(); monitor.checkCanceled();
//TODO: extraUtils.getReferencedAddress(address, getIboIf64bit);
Address baseClassDescriptorAddress = getReferencedAddress(address.add(i * 4)); Address baseClassDescriptorAddress = getReferencedAddress(address.add(i * 4));
Data baseClassDescriptor = extraUtils.getDataAt(baseClassDescriptorAddress); Data baseClassDescriptor = extraUtils.getDataAt(baseClassDescriptorAddress);
@ -572,6 +572,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
private Address createClassHierarchyDescriptor(Address address, Namespace classNamespace) private Address createClassHierarchyDescriptor(Address address, Namespace classNamespace)
throws CancelledException, MemoryAccessException, InvalidInputException, Exception { throws CancelledException, MemoryAccessException, InvalidInputException, Exception {
//TODO: extraUtils.getReferencedAddress(address, getIboIf64bit);
Address classHierarchyDescriptorAddress = getReferencedAddress(address); Address classHierarchyDescriptorAddress = getReferencedAddress(address);
Data classHierarchyStructure = extraUtils.getDataAt(classHierarchyDescriptorAddress); Data classHierarchyStructure = extraUtils.getDataAt(classHierarchyDescriptorAddress);
@ -652,6 +653,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
int numBaseClasses = extraUtils.getInt(classHierarchyDescriptorAddress.add(8)); int numBaseClasses = extraUtils.getInt(classHierarchyDescriptorAddress.add(8));
//TODO: extraUtils.getReferencedAddress(address, getIboIf64bit);
Address baseClassArrayAddress = Address baseClassArrayAddress =
getReferencedAddress(classHierarchyDescriptorAddress.add(12)); getReferencedAddress(classHierarchyDescriptorAddress.add(12));
@ -1295,8 +1297,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
*/ */
private void setClassInheritanceType(RecoveredClass recoveredClass, int inheritanceType) { private void setClassInheritanceType(RecoveredClass recoveredClass, int inheritanceType) {
// TODO: update the single virtual case from here too - may need to update class // TODO: add multi-repeated base inh flag?
// TODO: ?? add multi-repeate base inh flag? do I care other than to give info to user?
if ((inheritanceType & CHD_MULTINH) == 0) { if ((inheritanceType & CHD_MULTINH) == 0) {
recoveredClass.setHasSingleInheritance(true); recoveredClass.setHasSingleInheritance(true);
@ -1305,11 +1306,11 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
if ((inheritanceType & CHD_VIRTINH) == 0) { if ((inheritanceType & CHD_VIRTINH) == 0) {
recoveredClass.setInheritsVirtualAncestor(false); recoveredClass.setInheritsVirtualAncestor(false);
} }
// else { // Flag indicates single inheritance virtual ancestor for class " +
// // TODO: might have to update the single virt inh here
// println("Flag indicates single inheritance virtual ancestor for class " +
// recoveredClass.getName()); // recoveredClass.getName());
// } else {
recoveredClass.setInheritsVirtualAncestor(true);
}
} }
else { else {
recoveredClass.setHasSingleInheritance(false); recoveredClass.setHasSingleInheritance(false);
@ -1317,10 +1318,11 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
if ((inheritanceType & CHD_VIRTINH) == 0) { if ((inheritanceType & CHD_VIRTINH) == 0) {
recoveredClass.setHasMultipleVirtualInheritance(false); recoveredClass.setHasMultipleVirtualInheritance(false);
} }
// Flag indicates multiple inheritance virtual ancestor for class " +
// recoveredClass.getName());
else { else {
recoveredClass.setHasMultipleVirtualInheritance(true); recoveredClass.setHasMultipleVirtualInheritance(true);
// println("Flag indicates multiple inheritance virtual ancestor for class " +
// recoveredClass.getName());
} }
} }
//TODO: CHD_AMBIGUOUS = 0x00000004; //TODO: CHD_AMBIGUOUS = 0x00000004;

View file

@ -41,6 +41,7 @@ import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd;
import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd.OffsetPcodeOpPair; import ghidra.app.plugin.core.decompile.actions.FillOutStructureCmd.OffsetPcodeOpPair;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference; import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.util.NamespaceUtils;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.flatapi.FlatProgramAPI; import ghidra.program.flatapi.FlatProgramAPI;
@ -85,6 +86,8 @@ public class RecoveredClassUtils {
private static int MIN_OPERATOR_NEW_REFS = 10; private static int MIN_OPERATOR_NEW_REFS = 10;
private static final boolean DEBUG = false;
private Map<Address, RecoveredClass> vftableToClassMap = new HashMap<Address, RecoveredClass>(); private Map<Address, RecoveredClass> vftableToClassMap = new HashMap<Address, RecoveredClass>();
// map from vftable references to the vftables they point to // map from vftable references to the vftables they point to
@ -1012,7 +1015,9 @@ public class RecoveredClassUtils {
List<RecoveredClass> functionClasses = getClasses(function); List<RecoveredClass> functionClasses = getClasses(function);
if (functionClasses == null) { if (functionClasses == null) {
if (DEBUG) {
Msg.debug(this, "no function to class map for " + function.getEntryPoint()); Msg.debug(this, "no function to class map for " + function.getEntryPoint());
}
return true; return true;
} }
Iterator<RecoveredClass> functionClassesIterator = functionClasses.iterator(); Iterator<RecoveredClass> functionClassesIterator = functionClasses.iterator();
@ -1098,8 +1103,10 @@ public class RecoveredClassUtils {
List<Address> vftableReferenceList = getVftableReferences(function); List<Address> vftableReferenceList = getVftableReferences(function);
if (vftableReferenceList == null) { if (vftableReferenceList == null) {
if (DEBUG) {
Msg.debug(this, "In update maps: function to class map doesn't exist for " + Msg.debug(this, "In update maps: function to class map doesn't exist for " +
function.getEntryPoint().toString()); function.getEntryPoint().toString());
}
return; return;
} }
Collections.sort(vftableReferenceList); Collections.sort(vftableReferenceList);
@ -1113,10 +1120,11 @@ public class RecoveredClassUtils {
RecoveredClass vftableClass = getVftableClass(vftableAddress); RecoveredClass vftableClass = getVftableClass(vftableAddress);
if (!vftableClass.equals(recoveredClass)) { if (!vftableClass.equals(recoveredClass)) {
Msg.debug(this, if (DEBUG) {
"updating struct for " + recoveredClass.getName() + Msg.debug(this, "updating struct for " + recoveredClass.getName() +
" but first vftable in function " + function.getEntryPoint().toString() + " but first vftable in function " + function.getEntryPoint().toString() +
" is in class " + vftableClass.getName()); " is in class " + vftableClass.getName());
}
return; return;
} }
@ -1196,8 +1204,10 @@ public class RecoveredClassUtils {
return; return;
} }
if (DEBUG) {
Msg.debug(this, "Could not find variable pointing to vftable in " + Msg.debug(this, "Could not find variable pointing to vftable in " +
function.getEntryPoint().toString()); function.getEntryPoint().toString());
}
} }
@ -2046,9 +2056,11 @@ public class RecoveredClassUtils {
} }
if (vftableAddresses.size() != classOffsetToVftableMap.size()) { if (vftableAddresses.size() != classOffsetToVftableMap.size()) {
if (DEBUG) {
Msg.debug(this, recoveredClass.getName() + " has " + vftableAddresses.size() + Msg.debug(this, recoveredClass.getName() + " has " + vftableAddresses.size() +
" vftables but " + classOffsetToVftableMap.size() + " offset to vftable maps"); " vftables but " + classOffsetToVftableMap.size() + " offset to vftable maps");
} }
}
List<Integer> offsetList = new ArrayList<Integer>(classOffsetToVftableMap.keySet()); List<Integer> offsetList = new ArrayList<Integer>(classOffsetToVftableMap.keySet());
@ -2717,20 +2729,19 @@ public class RecoveredClassUtils {
// Get class name from class vftable is in // Get class name from class vftable is in
Namespace vftableNamespace = vftableSymbol.getParentNamespace(); Namespace vftableNamespace = vftableSymbol.getParentNamespace();
if (vftableNamespace.equals(globalNamespace)) { if (vftableNamespace.equals(globalNamespace)) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"vftable is in the global namespace, ie not in a class namespace, so cannot process"); "vftable is in the global namespace, ie not in a class namespace, so cannot process");
}
continue; continue;
} }
SymbolType namespaceType = vftableNamespace.getSymbol().getSymbolType(); // promote any non-class namespaces in the vftableNamespace path to class namespaces
if (namespaceType != SymbolType.CLASS) { boolean success = promoteNamespaces(vftableNamespace.getSymbol());
// if it is a namespace but not a class we need to promote it to a class namespace if (!success) {
if (namespaceType == SymbolType.NAMESPACE) { if (DEBUG) {
Msg.debug(this, "Unable to promote all non-class namespaces for " +
//vftableNamespace = promoteToClassNamespace(vftableNamespace); vftableNamespace.getName(true));
// else just leave the old one as a namepace
} }
} }
@ -2808,6 +2819,60 @@ public class RecoveredClassUtils {
return recoveredClasses; return recoveredClasses;
} }
private boolean promoteNamespaces(Symbol symbol) throws CancelledException {
Namespace namespace = symbol.getParentNamespace();
while (!namespace.isGlobal()) {
monitor.checkCanceled();
SymbolType namespaceType = namespace.getSymbol().getSymbolType();
if (namespaceType != SymbolType.CLASS) {
// if it is a namespace but not a class we need to promote it to a class namespace
if (namespaceType == SymbolType.NAMESPACE) {
namespace = promoteToClassNamespace(namespace);
if (namespace == null) {
return false;
}
if (DEBUG) {
Msg.debug(this,
"Promoted namespace " + namespace.getName() + " to a class namespace");
}
}
}
else {
namespace = namespace.getParentNamespace();
}
}
return true;
}
/**
* Method to promote the namespace is a class namespace.
* @return true if namespace is (now) a class namespace or false if it could not be promoted.
*/
private Namespace promoteToClassNamespace(Namespace namespace) {
try {
Namespace newClass = NamespaceUtils.convertNamespaceToClass(namespace);
SymbolType symbolType = newClass.getSymbol().getSymbolType();
if (symbolType == SymbolType.CLASS) {
return newClass;
}
if (DEBUG) {
Msg.debug(this,
"Could not promote " + namespace.getName() + " to a class namespace");
}
return null;
}
catch (InvalidInputException e) {
Msg.debug(this, "Could not promote " + namespace.getName() +
" to a class namespace because " + e.getMessage());
return null;
}
}
/** /**
* Method to create mapping to possible constructor/destructor functions * Method to create mapping to possible constructor/destructor functions
* @param referencesToVftable list of references to a particular vftable * @param referencesToVftable list of references to a particular vftable
@ -3397,10 +3462,12 @@ public class RecoveredClassUtils {
if (symbolsByNameAtAddress.size() == 0) { if (symbolsByNameAtAddress.size() == 0) {
AddLabelCmd lcmd = new AddLabelCmd(address, name, namespace, SourceType.ANALYSIS); AddLabelCmd lcmd = new AddLabelCmd(address, name, namespace, SourceType.ANALYSIS);
if (!lcmd.applyTo(program)) { if (!lcmd.applyTo(program)) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"ERROR: Could not add new symbol " + name + " to " + address.toString()); "ERROR: Could not add new symbol " + name + " to " + address.toString());
} }
} }
}
//put the same name one in the namespace //put the same name one in the namespace
else { else {
Iterator<Symbol> iterator = symbolsByNameAtAddress.iterator(); Iterator<Symbol> iterator = symbolsByNameAtAddress.iterator();
@ -3491,8 +3558,10 @@ public class RecoveredClassUtils {
AddLabelCmd lcmd = AddLabelCmd lcmd =
new AddLabelCmd(function.getEntryPoint(), name, namespace, SourceType.ANALYSIS); new AddLabelCmd(function.getEntryPoint(), name, namespace, SourceType.ANALYSIS);
if (!lcmd.applyTo(program)) { if (!lcmd.applyTo(program)) {
if (DEBUG) {
Msg.debug(this, "ERROR: Could not add new function label " + name + " to " + Msg.debug(this, "ERROR: Could not add new function label " + name + " to " +
function.getEntryPoint().toString()); function.getEntryPoint().toString());
}
return; return;
} }
@ -3501,11 +3570,13 @@ public class RecoveredClassUtils {
SetLabelPrimaryCmd scmd = SetLabelPrimaryCmd scmd =
new SetLabelPrimaryCmd(function.getEntryPoint(), name, namespace); new SetLabelPrimaryCmd(function.getEntryPoint(), name, namespace);
if (!scmd.applyTo(program)) { if (!scmd.applyTo(program)) {
if (DEBUG) {
Msg.debug(this, "ERROR: Could not make function label " + name + Msg.debug(this, "ERROR: Could not make function label " + name +
" primary at " + function.getEntryPoint().toString()); " primary at " + function.getEntryPoint().toString());
} }
} }
} }
}
//put the same name one in the namespace //put the same name one in the namespace
else { else {
Iterator<Symbol> iterator = symbolsByNameAtAddress.iterator(); Iterator<Symbol> iterator = symbolsByNameAtAddress.iterator();
@ -3756,6 +3827,7 @@ public class RecoveredClassUtils {
} }
else { else {
if (DEBUG) {
Msg.debug(this, "ERROR: " + function.getEntryPoint().toString() + Msg.debug(this, "ERROR: " + function.getEntryPoint().toString() +
" Could not replace parameter " + i + " with undefined pointer."); " Could not replace parameter " + i + " with undefined pointer.");
} }
@ -3763,6 +3835,7 @@ public class RecoveredClassUtils {
} }
} }
} }
}
/** /**
* Method to find incorrect empty structure return type * Method to find incorrect empty structure return type
@ -4818,10 +4891,12 @@ public class RecoveredClassUtils {
//TODO: remove after testing //TODO: remove after testing
if (!classVftableRef.equals(otherWayRef)) { if (!classVftableRef.equals(otherWayRef)) {
if (DEBUG) {
Msg.debug(this, recoveredClass.getName() + " function " + Msg.debug(this, recoveredClass.getName() + " function " +
destructorFunction.getEntryPoint().toString() + " first ref: " + destructorFunction.getEntryPoint().toString() + " first ref: " +
classVftableRef.toString() + " other way ref: " + otherWayRef.toString()); classVftableRef.toString() + " other way ref: " + otherWayRef.toString());
} }
}
String markupString = classNamespace.getName(true) + "::~" + className; String markupString = classNamespace.getName(true) + "::~" + className;
api.setPreComment(classVftableRef, "inlined destructor: " + markupString); api.setPreComment(classVftableRef, "inlined destructor: " + markupString);
@ -4857,11 +4932,13 @@ public class RecoveredClassUtils {
} }
//TODO: remove after testing //TODO: remove after testing
if (!classVftableRef.equals(otherWayRef)) { if (!classVftableRef.equals(otherWayRef)) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
recoveredClass.getName() + " function " + recoveredClass.getName() + " function " +
functionContainingInline.getEntryPoint().toString() + " first ref: " + functionContainingInline.getEntryPoint().toString() + " first ref: " +
classVftableRef.toString() + " other way ref: " + otherWayRef.toString()); classVftableRef.toString() + " other way ref: " + otherWayRef.toString());
} }
}
String markupString = "inlined constructor or destructor (approx location) for " + String markupString = "inlined constructor or destructor (approx location) for " +
classNamespace.getName(true); classNamespace.getName(true);
@ -5070,12 +5147,13 @@ public class RecoveredClassUtils {
return; return;
} }
if (!vftableReference.equals(otherWayRef)) { if (!vftableReference.equals(otherWayRef)) {
Msg.debug(this, if (DEBUG) {
recoveredClass.getName() + " function " + Msg.debug(this, recoveredClass.getName() + " function " +
virtualFunction.getEntryPoint().toString() + " first ref: " + virtualFunction.getEntryPoint().toString() + " first ref: " +
vftableReference.toString() + " other way ref (with ances): " + vftableReference.toString() + " other way ref (with ances): " +
otherWayRef.toString()); otherWayRef.toString());
} }
}
List<Function> possibleParentDestructors = getPossibleParentDestructors(virtualFunction); List<Function> possibleParentDestructors = getPossibleParentDestructors(virtualFunction);
@ -5108,8 +5186,10 @@ public class RecoveredClassUtils {
recoveredClass.addDeletingDestructor(virtualFunction); recoveredClass.addDeletingDestructor(virtualFunction);
if (recoveredClass.getDestructorList().contains(virtualFunction)) { if (recoveredClass.getDestructorList().contains(virtualFunction)) {
if (DEBUG) {
Msg.debug(this, "Already created vfunction as a destructor"); Msg.debug(this, "Already created vfunction as a destructor");
} }
}
recoveredClass.removeFromConstructorDestructorList(virtualFunction); recoveredClass.removeFromConstructorDestructorList(virtualFunction);
recoveredClass.removeIndeterminateConstructorOrDestructor(virtualFunction); recoveredClass.removeIndeterminateConstructorOrDestructor(virtualFunction);
recoveredClass.addInlinedDestructor(virtualFunction); recoveredClass.addInlinedDestructor(virtualFunction);
@ -6053,8 +6133,10 @@ public class RecoveredClassUtils {
Function operatorDeleteFunction = Function operatorDeleteFunction =
findOperatorDeleteUsingKnownDeletingDestructors(recoveredClasses); findOperatorDeleteUsingKnownDeletingDestructors(recoveredClasses);
if (operatorDeleteFunction == null) { if (operatorDeleteFunction == null) {
if (DEBUG) {
Msg.debug(this, Msg.debug(this,
"Could not find operator delete function. Cannot process more deleting destructors."); "Could not find operator delete function. Cannot process more deleting destructors.");
}
return; return;
} }
@ -6645,7 +6727,9 @@ public class RecoveredClassUtils {
} }
// if they ever don't match return // if they ever don't match return
else if (!possiblePureCall.equals(sameFunction)) { else if (!possiblePureCall.equals(sameFunction)) {
if (DEBUG) {
Msg.debug(this, "Could not identify pure call. "); Msg.debug(this, "Could not identify pure call. ");
}
return; return;
} }
} }