GP-2679 Gcc class recovery improvements. Created classes for Vtable,

Typeinfo, etc. Reworked vtable, const vtable and vtt discovery. Now
works for static gcc and for mingw/cygwin programs.
This commit is contained in:
ghidra007 2023-02-13 17:35:47 +00:00
parent 269ea1ae7a
commit 1d14222f37
14 changed files with 4937 additions and 2131 deletions

View file

@ -34,7 +34,7 @@
// NOTE: As this is a prototype script, the location, names, and design of data types created by // NOTE: As this is a prototype script, the location, names, and design of data types created by
// 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 are only recovered for classes without // in early stages of development. Gcc class data types are only recovered for classes without
// virtual inheritance but if the program contains DWARF, there will be some amount of data recovered // virtual inheritance but if the program contains DWARF, there will be some amount of data recovered
// by the DWARF analyzer. // by the DWARF analyzer.
@ -54,10 +54,20 @@
import java.io.File; import java.io.File;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.*; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import classrecovery.*; import classrecovery.DecompilerScriptUtils;
import classrecovery.RTTIClassRecoverer;
import classrecovery.RTTIGccClassRecoverer;
import classrecovery.RTTIWindowsClassRecoverer;
import classrecovery.RecoveredClass;
import classrecovery.RecoveredClassHelper;
import generic.theme.GThemeDefaults.Colors.Palette; import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.app.decompiler.DecompInterface; import ghidra.app.decompiler.DecompInterface;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
@ -71,15 +81,32 @@ import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProvid
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProviderFactory; import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProviderFactory;
import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.app.util.bin.format.pdb.PdbParserConstants;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.ElfLoader; import ghidra.app.util.opinion.PeLoader;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.*; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Parameter;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.GhidraProgramUtilities; import ghidra.program.util.GhidraProgramUtilities;
import ghidra.service.graph.*; import ghidra.service.graph.AttributedEdge;
import ghidra.service.graph.AttributedGraph;
import ghidra.service.graph.AttributedVertex;
import ghidra.service.graph.GraphDisplay;
import ghidra.service.graph.GraphDisplayOptions;
import ghidra.service.graph.GraphDisplayOptionsBuilder;
import ghidra.service.graph.GraphDisplayProvider;
import ghidra.service.graph.GraphType;
import ghidra.service.graph.GraphTypeBuilder;
import ghidra.service.graph.VertexShape;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.GraphException; import ghidra.util.exception.GraphException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -140,10 +167,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
private static final String DESTRUCTOR_BOOKMARK = "DESTRUCTOR"; private static final String DESTRUCTOR_BOOKMARK = "DESTRUCTOR";
private static final String INDETERMINATE_BOOKMARK = "INDETERMINATE"; private static final String INDETERMINATE_BOOKMARK = "INDETERMINATE";
boolean programHasRTTIApplied = false;
boolean hasDebugSymbols; boolean hasDebugSymbols = false;
boolean isGcc = false;
boolean isWindows = false;
String ghidraVersion = null; String ghidraVersion = null;
DecompilerScriptUtils decompilerUtils; DecompilerScriptUtils decompilerUtils;
@ -161,12 +186,13 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
public void run() throws Exception { public void run() throws Exception {
String errorMsg = validate(); String errorMsg = validate();
if (!errorMsg.isEmpty()) { if (!errorMsg.isEmpty()) {
println(errorMsg); println(errorMsg);
return; return;
} }
if (isWindows()) { if (!isGcc() && isWindows()) {
if (!isRttiAnalyzed()) { if (!isRttiAnalyzed()) {
println("Running the RTTIAnalyzer..."); println("Running the RTTIAnalyzer...");
@ -192,11 +218,27 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS,
nameVfunctions, hasDebugSymbols, monitor); nameVfunctions, hasDebugSymbols, monitor);
} }
else if (isGcc()) { else if (isPE() && isGcc()){
println("Program is a gcc compiled PE.");
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;
}
//run fixup old elf relocations script
runScript("FixElfExternalOffsetDataRelocationScript.java");
recoverClassesFromRTTI =
new RTTIGccClassRecoverer(currentProgram, currentLocation, state.getTool(), this,
BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS,
nameVfunctions, hasDebugSymbols, monitor);
}
else if (isGcc()) {
println("Before popping up dialog..");
printTime();
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.");
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) { if (!runGcc) {
return; return;
} }
@ -376,8 +418,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
if (!isGcc() && !isWindows()) { if (!isGcc() && !isWindows()) {
return ("This script only handles Windows and gcc programs"); return ("This script only handles Windows PE and Gcc programs");
} }
defaultPointerSize = currentProgram.getDefaultPointerSize(); defaultPointerSize = currentProgram.getDefaultPointerSize();
@ -438,7 +479,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
AttributedGraph g = new AttributedGraph("Recovered Classes Graph", graphType); AttributedGraph g = new AttributedGraph("Recovered Classes Graph", graphType);
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
AttributedVertex classVertex = AttributedVertex classVertex =
g.addVertex(recoveredClass.getClassPath().getPath(), recoveredClass.getName()); g.addVertex(recoveredClass.getClassPath().getPath(), recoveredClass.getName());
@ -470,7 +511,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
recoveredClass.getParentToBaseTypeMap(); recoveredClass.getParentToBaseTypeMap();
for (RecoveredClass parent : parents) { for (RecoveredClass parent : parents) {
monitor.checkCanceled(); monitor.checkCancelled();
AttributedVertex parentVertex = AttributedVertex parentVertex =
g.addVertex(parent.getClassPath().getPath(), parent.getName()); g.addVertex(parent.getClassPath().getPath(), parent.getName());
@ -541,11 +582,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
List<RecoveredClass> classHierarchyList = recoveredClass.getClassHierarchy(); List<RecoveredClass> classHierarchyList = recoveredClass.getClassHierarchy();
for (RecoveredClass currentClass : classHierarchyList) { for (RecoveredClass currentClass : classHierarchyList) {
monitor.checkCanceled(); monitor.checkCancelled();
println(currentClass.getName()); println(currentClass.getName());
} }
@ -568,14 +609,25 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return false; return false;
} }
/**
* Method to check if executable format is PE
*/
private boolean isPE() {
if (!PeLoader.PE_NAME.equals(currentProgram.getExecutableFormat())) {
return false;
}
return true;
}
/** /**
* Method to set the global variable isGcc * Method to set the global variable isGcc
*/ */
private boolean isGcc() { private boolean isGcc() {
if (!ElfLoader.ELF_NAME.equals(currentProgram.getExecutableFormat())) { boolean isGcc;
return false;
}
boolean isCompilerSpecGcc = currentProgram.getCompilerSpec() boolean isCompilerSpecGcc = currentProgram.getCompilerSpec()
.getCompilerSpecID() .getCompilerSpecID()
@ -585,6 +637,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return true; return true;
} }
String compiler = currentProgram.getCompiler();
if(compiler != null && compiler.contains("gcc")) {
return true;
}
MemoryBlock commentBlock = currentProgram.getMemory().getBlock(".comment"); MemoryBlock commentBlock = currentProgram.getMemory().getBlock(".comment");
if (commentBlock == null) { if (commentBlock == null) {
return false; return false;
@ -611,6 +668,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return isGcc; return isGcc;
} }
/** /**
* Method to set the global variable isWindows * Method to set the global variable isWindows
*/ */
@ -618,7 +676,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
String compilerID = String compilerID =
currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase(); currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase();
isWindows = compilerID.contains("windows"); boolean isWindows = compilerID.contains("windows");
return isWindows; return isWindows;
} }
@ -741,7 +799,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append(" : "); stringBuffer.append(" : ");
int lastColon = stringBuffer.lastIndexOf(":"); int lastColon = stringBuffer.lastIndexOf(":");
for (RecoveredClass parentClass : classHierarchyMap.keySet()) { for (RecoveredClass parentClass : classHierarchyMap.keySet()) {
monitor.checkCanceled(); monitor.checkCancelled();
if (classHierarchyMap.size() == 1) { if (classHierarchyMap.size() == 1) {
//stringBuffer.append(" : "); //stringBuffer.append(" : ");
getSimpleClassHierarchyString(stringBuffer, parentClass); getSimpleClassHierarchyString(stringBuffer, parentClass);
@ -752,7 +810,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
//int lastColon = stringBuffer.lastIndexOf(":"); //int lastColon = stringBuffer.lastIndexOf(":");
for (int i = 0; i <= lastColon; i++) { for (int i = 0; i <= lastColon; i++) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append(" "); stringBuffer.append(" ");
} }
getSimpleClassHierarchyString(stringBuffer, parentClass); getSimpleClassHierarchyString(stringBuffer, parentClass);
@ -775,7 +833,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks(); MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
for (MemoryBlock block : blocks) { for (MemoryBlock block : blocks) {
monitor.checkCanceled(); monitor.checkCancelled();
if (block.isInitialized()) { if (block.isInitialized()) {
dataAddresses.add(block.getStart(), block.getEnd()); dataAddresses.add(block.getStart(), block.getEnd());
@ -793,7 +851,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
bookmarkFunctionsOnList(recoveredClass.getConstructorList(), CONSTRUCTOR_BOOKMARK); bookmarkFunctionsOnList(recoveredClass.getConstructorList(), CONSTRUCTOR_BOOKMARK);
} }
} }
@ -807,7 +865,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
bookmarkFunctionsOnList(recoveredClass.getDestructorList(), DESTRUCTOR_BOOKMARK); bookmarkFunctionsOnList(recoveredClass.getDestructorList(), DESTRUCTOR_BOOKMARK);
bookmarkFunctionsOnList(recoveredClass.getNonThisDestructors(), DESTRUCTOR_BOOKMARK); bookmarkFunctionsOnList(recoveredClass.getNonThisDestructors(), DESTRUCTOR_BOOKMARK);
} }
@ -822,7 +880,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
List<RecoveredClass> recoveredClasses) throws CancelledException { List<RecoveredClass> recoveredClasses) throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
bookmarkFunctionsOnList(recoveredClass.getIndeterminateList(), INDETERMINATE_BOOKMARK); bookmarkFunctionsOnList(recoveredClass.getIndeterminateList(), INDETERMINATE_BOOKMARK);
} }
} }
@ -841,7 +899,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
for (Function function : functions) { for (Function function : functions) {
monitor.checkCanceled(); monitor.checkCancelled();
Address address = function.getEntryPoint(); Address address = function.getEntryPoint();
recoverClassesFromRTTI.bookmarkAddress(address, comment); recoverClassesFromRTTI.bookmarkAddress(address, comment);
} }
@ -902,7 +960,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
private void printClassDefinitions(List<RecoveredClass> recoveredClasses) private void printClassDefinitions(List<RecoveredClass> recoveredClasses)
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
if (!recoveredClass.hasParentClass()) { if (!recoveredClass.hasParentClass()) {
println(createClassDefinitionString(recoveredClass).toString()); println(createClassDefinitionString(recoveredClass).toString());
} }
@ -912,7 +970,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
private void outputClassDefinitions(List<RecoveredClass> recoveredClasses, PrintWriter out) private void outputClassDefinitions(List<RecoveredClass> recoveredClasses, PrintWriter out)
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
if (!recoveredClass.hasParentClass()) { if (!recoveredClass.hasParentClass()) {
out.append(createClassDefinitionString(recoveredClass)); out.append(createClassDefinitionString(recoveredClass));
} }
@ -928,7 +986,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
*/ */
private void printClassInfo(List<RecoveredClass> recoveredClasses) throws CancelledException { private void printClassInfo(List<RecoveredClass> recoveredClasses) throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
if (!recoveredClass.hasParentClass()) { if (!recoveredClass.hasParentClass()) {
println(createClassInfoString(recoveredClass).toString()); println(createClassInfoString(recoveredClass).toString());
} }
@ -938,12 +996,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
private void printClassParents(List<RecoveredClass> recoveredClasses) private void printClassParents(List<RecoveredClass> recoveredClasses)
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
String printString = new String("\n" + recoveredClass.getName() + "\n"); String printString = new String("\n" + recoveredClass.getName() + "\n");
if (recoveredClass.hasParentClass()) { if (recoveredClass.hasParentClass()) {
List<RecoveredClass> parentList = recoveredClass.getParentList(); List<RecoveredClass> parentList = recoveredClass.getParentList();
for (RecoveredClass parent : parentList) { for (RecoveredClass parent : parentList) {
monitor.checkCanceled(); monitor.checkCancelled();
printString = printString.concat("\t" + parent.getName() + "\n"); printString = printString.concat("\t" + parent.getName() + "\n");
} }
} }
@ -963,7 +1021,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
StringBuffer wholeBuffer = new StringBuffer(); StringBuffer wholeBuffer = new StringBuffer();
wholeBuffer.append("\r\n"); wholeBuffer.append("\r\n");
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
if (!recoveredClass.hasChildClass()) { if (!recoveredClass.hasChildClass()) {
StringBuffer stringBuffer = new StringBuffer(); StringBuffer stringBuffer = new StringBuffer();
@ -990,7 +1048,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
wholeBuffer.append("\r\n"); wholeBuffer.append("\r\n");
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
if (!recoveredClass.hasChildClass()) { if (!recoveredClass.hasChildClass()) {
StringBuffer stringBuffer = new StringBuffer(); StringBuffer stringBuffer = new StringBuffer();
wholeBuffer.append( wholeBuffer.append(
@ -1011,7 +1069,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
if (!recoveredClass.hasParentClass()) { if (!recoveredClass.hasParentClass()) {
out.append(createClassInfoString(recoveredClass).toString()); out.append(createClassInfoString(recoveredClass).toString());
} }
@ -1070,7 +1128,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
int total = 0; int total = 0;
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
List<Function> constructorList = recoveredClass.getConstructorOrDestructorFunctions(); List<Function> constructorList = recoveredClass.getConstructorOrDestructorFunctions();
total += constructorList.size(); total += constructorList.size();
} }
@ -1088,7 +1146,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
int total = 0; int total = 0;
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
List<Function> inlineList = recoveredClass.getInlinedConstructorList(); List<Function> inlineList = recoveredClass.getInlinedConstructorList();
total += inlineList.size(); total += inlineList.size();
} }
@ -1102,7 +1160,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
*/ */
private void printAddresses(List<Address> addresses) throws CancelledException { private void printAddresses(List<Address> addresses) throws CancelledException {
for (Address element : addresses) { for (Address element : addresses) {
monitor.checkCanceled(); monitor.checkCancelled();
println(element.toString()); println(element.toString());
} }
} }
@ -1117,7 +1175,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
PrintWriter out) throws CancelledException { PrintWriter out) throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
out.append(printClassParentsandChildren(recoveredClass)); out.append(printClassParentsandChildren(recoveredClass));
} }
} }
@ -1131,7 +1189,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
throws CancelledException { throws CancelledException {
for (RecoveredClass recoveredClass : recoveredClasses) { for (RecoveredClass recoveredClass : recoveredClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
println(printClassParentsandChildren(recoveredClass).toString()); println(printClassParentsandChildren(recoveredClass).toString());
} }
} }
@ -1157,7 +1215,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (recoveredClass.hasParentClass()) { if (recoveredClass.hasParentClass()) {
Set<RecoveredClass> keySet = recoveredClass.getClassHierarchyMap().keySet(); Set<RecoveredClass> keySet = recoveredClass.getClassHierarchyMap().keySet();
for (RecoveredClass parent : keySet) { for (RecoveredClass parent : keySet) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + parent.getName() + "\r\n"); stringBuffer.append("\t" + parent.getName() + "\r\n");
} }
} }
@ -1168,7 +1226,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (recoveredClass.hasChildClass()) { if (recoveredClass.hasChildClass()) {
List<RecoveredClass> childClasses = recoveredClass.getChildClasses(); List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
for (RecoveredClass element : childClasses) { for (RecoveredClass element : childClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + element.getName() + "\r\n"); stringBuffer.append("\t" + element.getName() + "\r\n");
} }
@ -1209,7 +1267,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
recoveredClass.getParentToBaseTypeMap(); recoveredClass.getParentToBaseTypeMap();
Set<RecoveredClass> ancestors = parentToBaseTypeMap.keySet(); Set<RecoveredClass> ancestors = parentToBaseTypeMap.keySet();
for (RecoveredClass ancestor : ancestors) { for (RecoveredClass ancestor : ancestors) {
monitor.checkCanceled(); monitor.checkCancelled();
if (directParents.contains(ancestor)) { if (directParents.contains(ancestor)) {
Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor); Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor);
@ -1232,7 +1290,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (recoveredClass.hasChildClass()) { if (recoveredClass.hasChildClass()) {
List<RecoveredClass> childClasses = recoveredClass.getChildClasses(); List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
for (RecoveredClass element : childClasses) { for (RecoveredClass element : childClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + element.getName() + "\r\n"); stringBuffer.append("\t" + element.getName() + "\r\n");
} }
@ -1243,7 +1301,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append("constructor(s):\r\n"); stringBuffer.append("constructor(s):\r\n");
List<Function> constructorList = recoveredClass.getConstructorList(); List<Function> constructorList = recoveredClass.getConstructorList();
for (Function constructorFunction : constructorList) { for (Function constructorFunction : constructorList) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + constructorFunction.getName() + " " + stringBuffer.append("\t" + constructorFunction.getName() + " " +
constructorFunction.getEntryPoint().toString() + "\r\n"); constructorFunction.getEntryPoint().toString() + "\r\n");
} }
@ -1254,7 +1312,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (inlinedConstructorList.size() > 0) { if (inlinedConstructorList.size() > 0) {
stringBuffer.append("inlined constructor(s):\r\n"); stringBuffer.append("inlined constructor(s):\r\n");
for (Function inlinedConstructorFunction : inlinedConstructorList) { for (Function inlinedConstructorFunction : inlinedConstructorList) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + inlinedConstructorFunction.getName() + " " + stringBuffer.append("\t" + inlinedConstructorFunction.getName() + " " +
inlinedConstructorFunction.getEntryPoint().toString() + "\r\n"); inlinedConstructorFunction.getEntryPoint().toString() + "\r\n");
} }
@ -1265,7 +1323,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append("destructor(s):\r\n"); stringBuffer.append("destructor(s):\r\n");
List<Function> destructorList = recoveredClass.getDestructorList(); List<Function> destructorList = recoveredClass.getDestructorList();
for (Function destructorFunction : destructorList) { for (Function destructorFunction : destructorList) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + destructorFunction.getName() + " " + stringBuffer.append("\t" + destructorFunction.getName() + " " +
destructorFunction.getEntryPoint().toString() + "\r\n"); destructorFunction.getEntryPoint().toString() + "\r\n");
} }
@ -1276,7 +1334,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (inlinedDestructorList.size() > 0) { if (inlinedDestructorList.size() > 0) {
stringBuffer.append("inlined destructor(s):\r\n"); stringBuffer.append("inlined destructor(s):\r\n");
for (Function inlinedDestructorFunction : inlinedDestructorList) { for (Function inlinedDestructorFunction : inlinedDestructorList) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + inlinedDestructorFunction.getName() + " " + stringBuffer.append("\t" + inlinedDestructorFunction.getName() + " " +
inlinedDestructorFunction.getEntryPoint().toString() + "\r\n"); inlinedDestructorFunction.getEntryPoint().toString() + "\r\n");
} }
@ -1287,7 +1345,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (indeterminateList.size() > 0) { if (indeterminateList.size() > 0) {
stringBuffer.append("\r\nindeterminate constructor(s) or destructor(s):\r\n"); stringBuffer.append("\r\nindeterminate constructor(s) or destructor(s):\r\n");
for (Function indeterminateFunction : indeterminateList) { for (Function indeterminateFunction : indeterminateList) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + indeterminateFunction.getName() + " " + stringBuffer.append("\t" + indeterminateFunction.getName() + " " +
indeterminateFunction.getEntryPoint().toString() + "\r\n"); indeterminateFunction.getEntryPoint().toString() + "\r\n");
} }
@ -1299,7 +1357,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append("member function(s):\r\n"); stringBuffer.append("member function(s):\r\n");
List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions(); List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions();
for (Function vfunction : virtualFunctions) { for (Function vfunction : virtualFunctions) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append( stringBuffer.append(
"\t" + vfunction.getName() + " " + vfunction.getEntryPoint().toString() + "\r\n"); "\t" + vfunction.getName() + " " + vfunction.getEntryPoint().toString() + "\r\n");
} }
@ -1318,7 +1376,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents(); DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents();
for (int i = 0; i < numDefinedComponents; i++) { for (int i = 0; i < numDefinedComponents; i++) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + definedComponents[i].getDataType() + " " + stringBuffer.append("\t" + definedComponents[i].getDataType() + " " +
definedComponents[i].getFieldName() + "\r\n"); definedComponents[i].getFieldName() + "\r\n");
@ -1330,7 +1388,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (recoveredClass.hasChildClass()) { if (recoveredClass.hasChildClass()) {
List<RecoveredClass> childClasses = recoveredClass.getChildClasses(); List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
for (RecoveredClass element : childClasses) { for (RecoveredClass element : childClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append(createClassInfoString(element)); stringBuffer.append(createClassInfoString(element));
} }
} }
@ -1377,7 +1435,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
else { else {
for (int i = autoParamCount - 1; i < paramCount; i++) { for (int i = autoParamCount - 1; i < paramCount; i++) {
monitor.checkCanceled(); monitor.checkCancelled();
Parameter param = function.getParameter(i); Parameter param = function.getParameter(i);
stringBuffer.append(param.getDataType().getDisplayName() + " " + param.getName()); stringBuffer.append(param.getDataType().getDisplayName() + " " + param.getName());
if (i == paramCount) { if (i == paramCount) {
@ -1412,7 +1470,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append("constructor(s):\r\n"); stringBuffer.append("constructor(s):\r\n");
List<Function> constructorList = recoveredClass.getConstructorList(); List<Function> constructorList = recoveredClass.getConstructorList();
for (Function constructorFunction : constructorList) { for (Function constructorFunction : constructorList) {
monitor.checkCanceled(); monitor.checkCancelled();
String functionSignatureString = getFunctionSignatureString(constructorFunction, true); String functionSignatureString = getFunctionSignatureString(constructorFunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
@ -1423,7 +1481,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append("\r\ndestructor(s):\r\n"); stringBuffer.append("\r\ndestructor(s):\r\n");
List<Function> destructorList = recoveredClass.getDestructorList(); List<Function> destructorList = recoveredClass.getDestructorList();
for (Function destructorFunction : destructorList) { for (Function destructorFunction : destructorList) {
monitor.checkCanceled(); monitor.checkCancelled();
String functionSignatureString = getFunctionSignatureString(destructorFunction, true); String functionSignatureString = getFunctionSignatureString(destructorFunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
stringBuffer.append("\r\n"); stringBuffer.append("\r\n");
@ -1434,7 +1492,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (indeterminateList.size() > 0) { if (indeterminateList.size() > 0) {
stringBuffer.append("\r\nindeterminate constructor or destructor function(s):\r\n"); stringBuffer.append("\r\nindeterminate constructor or destructor function(s):\r\n");
for (Function indeterminateFunction : indeterminateList) { for (Function indeterminateFunction : indeterminateList) {
monitor.checkCanceled(); monitor.checkCancelled();
String functionSignatureString = String functionSignatureString =
getFunctionSignatureString(indeterminateFunction, true); getFunctionSignatureString(indeterminateFunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
@ -1446,7 +1504,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
stringBuffer.append("\r\nmember function(s):\r\n"); stringBuffer.append("\r\nmember function(s):\r\n");
List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions(); List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions();
for (Function vfunction : virtualFunctions) { for (Function vfunction : virtualFunctions) {
monitor.checkCanceled(); monitor.checkCancelled();
String functionSignatureString = getFunctionSignatureString(vfunction, true); String functionSignatureString = getFunctionSignatureString(vfunction, true);
stringBuffer.append(functionSignatureString); stringBuffer.append(functionSignatureString);
stringBuffer.append("\r\n"); stringBuffer.append("\r\n");
@ -1466,7 +1524,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents(); DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents();
for (int i = 0; i < numDefinedComponents; i++) { for (int i = 0; i < numDefinedComponents; i++) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append("\t" + definedComponents[i].getDataType() + " " + stringBuffer.append("\t" + definedComponents[i].getDataType() + " " +
definedComponents[i].getFieldName() + "\r\n"); definedComponents[i].getFieldName() + "\r\n");
} }
@ -1477,7 +1535,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (recoveredClass.hasChildClass()) { if (recoveredClass.hasChildClass()) {
List<RecoveredClass> childClasses = recoveredClass.getChildClasses(); List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
for (RecoveredClass element : childClasses) { for (RecoveredClass element : childClasses) {
monitor.checkCanceled(); monitor.checkCancelled();
stringBuffer.append(createClassDefinitionString(element)); stringBuffer.append(createClassDefinitionString(element));
} }
} }
@ -1485,4 +1543,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return stringBuffer; return stringBuffer;
} }
private void printTime() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
println(dtf.format(now));
}
} }

View file

@ -0,0 +1,64 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
public class BaseTypeinfo {
GccTypeinfo baseTypeinfo;
int baseOrder;
boolean isPublic;
boolean isVirtual;
long offset;
public BaseTypeinfo(GccTypeinfo baseTypeinfo, int baseOrder, boolean isPublic, boolean isVirtual, long offset){
this.baseTypeinfo = baseTypeinfo;
this.baseOrder = baseOrder;
this.isPublic = isPublic;
this.isVirtual = isVirtual;
this.offset = offset;
}
public GccTypeinfo getBaseTypeinfo() {
return baseTypeinfo;
}
public boolean isPublicBase() {
return isPublic;
}
public boolean isVirtualBase() {
return isVirtual;
}
public long getOffset() {
return offset;
}
public boolean isClassObjectOffset() {
if(isVirtual) {
return false;
}
return true;
}
public boolean isVbaseOffset() {
if(isVirtual) {
return true;
}
return false;
}
}

View file

@ -49,13 +49,17 @@ class EditStructureUtils {
for (DataTypeComponent newStructComponent : newStructComponents) { for (DataTypeComponent newStructComponent : newStructComponents) {
monitor.checkCanceled(); monitor.checkCancelled();
int structOffset = newStructComponent.getOffset(); int structOffset = newStructComponent.getOffset();
DataTypeComponent currentComponentAtOffset = DataTypeComponent currentComponentAtOffset =
containingStruct.getComponentAt(offset + structOffset); containingStruct.getComponentAt(offset + structOffset);
if(currentComponentAtOffset == null) {
continue;
}
DataType newStructCompDt = newStructComponent.getDataType(); DataType newStructCompDt = newStructComponent.getDataType();
DataType containingComDt = currentComponentAtOffset.getDataType(); DataType containingComDt = currentComponentAtOffset.getDataType();
@ -104,7 +108,7 @@ class EditStructureUtils {
} }
for (int i = offset; i < offset + length; i++) { for (int i = offset; i < offset + length; i++) {
monitor.checkCanceled(); monitor.checkCancelled();
DataTypeComponent component = structure.getComponentAt(i); DataTypeComponent component = structure.getComponentAt(i);
if (component == null) { if (component == null) {
return false; return false;
@ -142,7 +146,7 @@ class EditStructureUtils {
while (offset < endOfClear) { while (offset < endOfClear) {
monitor.checkCanceled(); monitor.checkCancelled();
DataTypeComponent component = structure.getComponentContaining(offset); DataTypeComponent component = structure.getComponentContaining(offset);
@ -209,7 +213,7 @@ class EditStructureUtils {
while (offset < endOfRange) { while (offset < endOfRange) {
monitor.checkCanceled(); monitor.checkCancelled();
DataTypeComponent component = structure.getComponentContaining(offset); DataTypeComponent component = structure.getComponentContaining(offset);
@ -357,7 +361,7 @@ class EditStructureUtils {
int index = offset - 1; int index = offset - 1;
while (index >= 0) { while (index >= 0) {
monitor.checkCanceled(); monitor.checkCancelled();
DataTypeComponent component = structure.getComponentAt(index); DataTypeComponent component = structure.getComponentAt(index);
if (component != null && component.getDataType() == DataType.DEFAULT) { if (component != null && component.getDataType() == DataType.DEFAULT) {
index--; index--;
@ -385,7 +389,7 @@ class EditStructureUtils {
int index = offset; int index = offset;
while (index < structure.getLength()) { while (index < structure.getLength()) {
monitor.checkCanceled(); monitor.checkCancelled();
DataTypeComponent component = structure.getComponentAt(index); DataTypeComponent component = structure.getComponentAt(index);
if (component.getDataType() == DataType.DEFAULT) { if (component.getDataType() == DataType.DEFAULT) {
index++; index++;

View file

@ -16,19 +16,52 @@
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. //DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
package classrecovery; package classrecovery;
import java.util.*; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.plugin.core.analysis.ReferenceAddressPair; import ghidra.app.plugin.core.analysis.ReferenceAddressPair;
import ghidra.app.util.PseudoDisassembler; import ghidra.app.util.PseudoDisassembler;
import ghidra.program.flatapi.FlatProgramAPI; import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.block.CodeBlock; import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.block.IsolatedEntrySubModel; import ghidra.program.model.block.IsolatedEntrySubModel;
import ghidra.program.model.data.*; import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.listing.*; import ghidra.program.model.data.DataType;
import ghidra.program.model.mem.*; import ghidra.program.model.data.IBO32DataType;
import ghidra.program.model.symbol.*; import ghidra.program.model.data.LongDataType;
import ghidra.program.model.data.Pointer;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.Undefined4DataType;
import ghidra.program.model.data.Undefined8DataType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -222,7 +255,17 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
return null; return null;
} }
Address functionAddress = referencesFrom.get(0); Address functionAddress = referencesFrom.get(0);
Register lowBitCodeMode = currentProgram.getRegister("LowBitCodeMode");
if(lowBitCodeMode != null) {
long longValue = functionAddress.getOffset();
longValue = longValue & ~0x1;
functionAddress = functionAddress.getNewAddress(longValue);
}
Function function = getFunctionAt(functionAddress); Function function = getFunctionAt(functionAddress);
if (function == null) { if (function == null) {
// try to create function // try to create function
@ -597,13 +640,21 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
try { try {
if (addressSize == 32) { if (addressSize == 32) {
long offset32 = getInt(address); long offset32 = getInt(address);
return address.getNewAddress(offset32); Address newAddr = address.getNewAddress(offset32);
if(currentProgram.getMemory().contains(newAddr)) {
return newAddr;
}
return null;
} }
else if (addressSize == 64) { else if (addressSize == 64) {
long offset64 = getLong(address); long offset64 = getLong(address);
return address.getNewAddress(offset64); Address newAddr = address.getNewAddress(offset64);
if(currentProgram.getMemory().contains(newAddr)) {
return newAddr;
}
return null;
} }
else { else {
@ -616,6 +667,18 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
} }
public long getLongValueAt(Address address) {
MemBuffer buf = new DumbMemBufferImpl(currentProgram.getMemory(), address);
LongDataType longDT = new LongDataType();
Scalar value =
(Scalar) longDT.getValue(buf, longDT.getDefaultSettings(), defaultPointerSize);
return value.getSignedValue();
}
/** /**
* Method to return a list of symbols with the given name and namespace. * Method to return a list of symbols with the given name and namespace.
* @param symbolName the symbol name to retrieve * @param symbolName the symbol name to retrieve

View file

@ -0,0 +1,179 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.Namespace;
public class GccTypeinfo extends Typeinfo {
private static final String CLASS_TYPEINFO_NAMESPACE = "__class_type_info";
private static final String SI_CLASS_TYPEINFO_NAMESPACE = "__si_class_type_info";
private static final String VMI_CLASS_TYPEINFO_NAMESPACE = "__vmi_class_type_info";
boolean isSpecialTypeinfo;
GccTypeinfo inheritedSpecialTypeinfo = null;
Address vtableAddress;
boolean inProgramMemory;
String mangledNamespaceString = null;
long inheritanceFlagValue;
List<BaseTypeinfo> baseTypeinfos = new ArrayList<BaseTypeinfo>();
public GccTypeinfo(Address address, Namespace classNamespace, boolean isSpecialTypeinfo, boolean inProgramMemory){
super(address, classNamespace);
this.isSpecialTypeinfo = isSpecialTypeinfo;
this.inProgramMemory = inProgramMemory;
}
public boolean isSpecialTypeinfo() {
return isSpecialTypeinfo;
}
public boolean isInProgramMemory() {
return inProgramMemory;
}
public void setInheritedSpecialTypeinfo(GccTypeinfo specialTypeinfo) {
inheritedSpecialTypeinfo = specialTypeinfo;
}
public GccTypeinfo getInheritedSpecialTypeinfo() {
return inheritedSpecialTypeinfo;
}
public void setVtableAddress(Address address) {
vtableAddress = address;
}
public Address getVtableAddress() {
return vtableAddress;
}
public void setMangledNamespaceString(String string) {
mangledNamespaceString = string;
}
public String getMangledNamespaceString() {
return mangledNamespaceString;
}
public void addBaseTypeinfo(GccTypeinfo baseTypeinfo, int order, boolean isPublic, boolean isVirtual, long offset) {
baseTypeinfos.add(new BaseTypeinfo(baseTypeinfo, order, isPublic, isVirtual, offset));
}
public List<BaseTypeinfo> getBaseTypeinfos(){
return baseTypeinfos;
}
public List<BaseTypeinfo> getAllBaseTypeinfos(){
Set<BaseTypeinfo> bases = new HashSet<BaseTypeinfo>();
bases.addAll(getBaseTypeinfos());
List<BaseTypeinfo> basesList = new ArrayList<BaseTypeinfo>(bases);
for(BaseTypeinfo base : basesList) {
bases.addAll(base.getBaseTypeinfo().getBaseTypeinfos());
}
return new ArrayList<BaseTypeinfo>(bases);
}
public int getNumDirectVirtualBases() {
int numVirtualBases = 0;
for(BaseTypeinfo baseTypeinfo : baseTypeinfos) {
if(baseTypeinfo.isVirtualBase()) {
numVirtualBases++;
}
}
return numVirtualBases;
}
public int getNumAllVirtualBases() {
int numVirtualBases = 0;
List<BaseTypeinfo> allBaseTypeinfos = getAllBaseTypeinfos();
for(BaseTypeinfo baseTypeinfo : allBaseTypeinfos) {
if(baseTypeinfo.isVirtualBase()) {
numVirtualBases++;
}
}
return numVirtualBases;
}
public List<GccTypeinfo> getDirectBases(){
List<GccTypeinfo> bases = new ArrayList<GccTypeinfo> ();
for(BaseTypeinfo baseTypeinfo : baseTypeinfos) {
bases.add(baseTypeinfo.getBaseTypeinfo());
}
return bases;
}
public List<GccTypeinfo> getAllBases(){
List<BaseTypeinfo> allBaseTypeinfos = getAllBaseTypeinfos();
Set<GccTypeinfo> bases = new HashSet<GccTypeinfo>();
for(BaseTypeinfo base : allBaseTypeinfos) {
bases.add(base.getBaseTypeinfo());
}
return new ArrayList<GccTypeinfo>(bases);
}
public void addInheritanceFlagValue(long flagValue) {
inheritanceFlagValue = flagValue;
}
public long getInheritanceFlagValue() {
return inheritanceFlagValue;
}
public boolean isClassTypeinfo() {
if(inheritedSpecialTypeinfo.getNamespace().getName().equals(CLASS_TYPEINFO_NAMESPACE)) {
return true;
}
return false;
}
public boolean isSiClassTypeinfo() {
if(inheritedSpecialTypeinfo.getNamespace().getName().equals(SI_CLASS_TYPEINFO_NAMESPACE)) {
return true;
}
return false;
}
public boolean isVmiClassTypeinfo() {
if(inheritedSpecialTypeinfo.getNamespace().getName().equals(VMI_CLASS_TYPEINFO_NAMESPACE)) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,40 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import ghidra.program.model.address.Address;
public class GccTypeinfoRef extends TypeinfoRef{
private Boolean inVtable = null;
GccTypeinfoRef(Address address, Typeinfo typeinfo){
super(address, typeinfo);
}
GccTypeinfoRef(Address address, Typeinfo typeinfo, Boolean inVtable){
super(address, typeinfo);
this.inVtable = inVtable;
}
public void setIsInVtable(Boolean setting) {
inVtable = setting;
}
public Boolean isInVtable() {
return inVtable;
}
}

View file

@ -606,7 +606,7 @@ public class RecoveredClass {
DataTypeComponent component = DataTypeComponent component =
computedClassStructure.getComponentAt(offset.intValue()); computedClassStructure.getComponentAt(offset.intValue());
if (!component.getDataType().equals(dataType)) { if (component != null && !component.getDataType().equals(dataType)) {
computedClassStructure.replaceAtOffset(offset.intValue(), dataType, computedClassStructure.replaceAtOffset(offset.intValue(), dataType,
dataType.getLength(), component.getFieldName(), component.getComment()); dataType.getLength(), component.getFieldName(), component.getComment());
} }

View file

@ -0,0 +1,85 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class SpecialVtable extends Vtable {
Address refFromTypeinfos;
public SpecialVtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean inExternalMemory, Namespace classNamespace, TaskMonitor monitor) throws CancelledException {
super(program, vtableAddress, typeinfoRef, true, inExternalMemory, monitor);
this.classNamespace = classNamespace;
}
@Override
protected void setup() throws CancelledException {
if(inExternalMemory) {
refFromTypeinfos = vtableAddress;
isConstruction = false;
isPrimary = true;
typeinfoAddress = vtableAddress;
length = defaultPointerSize;
hasVfunctions = false;
return;
}
typeinfoRefAddress = vtableAddress.add(defaultPointerSize);
setTypeinfoAddress();
if(!isValid) {
return;
}
setTopOffsetValue();
if(!isValid) {
return;
}
isPrimary = true;
setHasVfunctions();
if(!isValid) {
return;
}
isConstruction = false;
classNamespace = typeinfoNamespace;
setLength();
}
public Address getRefFromTypeinfos() {
return refFromTypeinfos;
}
}

View file

@ -0,0 +1,52 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.Namespace;
public class Typeinfo {
private Address address;
private Namespace classNamespace;
private Boolean hasDefinedStructure = false;
Typeinfo(Address address, Namespace classNamespace){
this.address = address;
this.classNamespace = classNamespace;
}
public Address getAddress() {
return address;
}
public Namespace getNamespace() {
return classNamespace;
}
/**
* method to define if has defined structure or not
* @param setting true - has defined structure, false - not yet defined, null - not enough memory to defined a structure
*/
private void setHasDefinedStructure(Boolean setting) {
hasDefinedStructure = setting;
}
public Boolean hasDefinedStructure() {
return hasDefinedStructure;
}
}

View file

@ -0,0 +1,52 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import ghidra.program.model.address.Address;
public class TypeinfoRef {
private Address address;
private Typeinfo typeinfo;
private Boolean inVtable = null;
TypeinfoRef(Address address, Typeinfo typeinfo){
this.address = address;
this.typeinfo = typeinfo;
}
TypeinfoRef(Address address, Typeinfo typeinfo, Boolean inVtable){
this.address = address;
this.typeinfo = typeinfo;
this.inVtable = inVtable;
}
public Address getAddress() {
return address;
}
public Typeinfo getReferencedTypeinfo() {
return typeinfo;
}
public void setIsInVtable(Boolean setting) {
inVtable = setting;
}
public Boolean isInVtable() {
return inVtable;
}
}

View file

@ -0,0 +1,822 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import java.util.ArrayList;
import java.util.List;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.LongDataType;
import ghidra.program.model.data.LongLongDataType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Bookmark;
import ghidra.program.model.listing.BookmarkType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class Vtable {
private static final String VTABLE_LABEL = "vtable";
Program program;
Address vtableAddress;
Boolean isSpecial = null;
GccTypeinfoRef typeinfoRef = null;
Address typeinfoRefAddress = null;
GccTypeinfo typeinfo = null;
Boolean hasVfunctions = null;
Integer numVfunctions = null;
Long topOffsetValue = null;
Address vfunctionTop = null;
Address typeinfoAddress = null;
Boolean isPrimary = null;
Boolean isConstruction = null;
List<Vtable> internalVtables = new ArrayList<Vtable>();
List<Address> relatedMainVtables = new ArrayList<Address>();
boolean inExternalMemory;
Boolean isValid = true;
Namespace typeinfoNamespace = null;
Namespace classNamespace = null;
Integer length = null;
Vtable primaryVtable = null;
int defaultPointerSize;
SymbolTable symbolTable;
ExtendedFlatProgramAPI extendedFlatAPI;
TaskMonitor monitor;
GlobalNamespace globalNamespace;
FunctionManager functionManager;
DataTypeManager dataTypeManager;
Listing listing;
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean isSpecial, boolean inExternalMemory,
Vtable primaryVtable, Boolean isConstruction, TaskMonitor monitor)
throws CancelledException {
this.program = program;
this.vtableAddress = vtableAddress;
this.typeinfoRef = typeinfoRef;
this.isSpecial = isSpecial;
this.inExternalMemory = inExternalMemory;
this.primaryVtable = primaryVtable;
this.isConstruction = isConstruction;
this.monitor = monitor;
this.typeinfoRefAddress = typeinfoRef.getAddress();
this.typeinfo = (GccTypeinfo) typeinfoRef.getReferencedTypeinfo();
this.typeinfoNamespace = typeinfo.getNamespace();
AddressSpace addressSpace = vtableAddress.getAddressSpace();
defaultPointerSize = addressSpace.getPointerSize();
symbolTable = program.getSymbolTable();
extendedFlatAPI = new ExtendedFlatProgramAPI(program, monitor);
globalNamespace = (GlobalNamespace) program.getGlobalNamespace();
functionManager = program.getFunctionManager();
dataTypeManager = program.getDataTypeManager();
listing = program.getListing();
setup();
}
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean isSpecial, boolean inExternalMemory, TaskMonitor monitor)
throws CancelledException {
this(program, vtableAddress, typeinfoRef, isSpecial, inExternalMemory, null, null, monitor);
}
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean isSpecial, boolean inExternalMemory, boolean isConstruction,
TaskMonitor monitor) throws CancelledException {
this(program, vtableAddress, typeinfoRef, isSpecial, inExternalMemory, null, isConstruction, monitor);
}
protected void setup() throws CancelledException {
checkValidTop();
if (!isValid) {
return;
}
setTopOffsetValue();
if (!isValid) {
return;
}
setIsInternalVtable();
if (!isValid) {
return;
}
figureOutNamespace();
setHasVfunctions();
if (!isValid) {
return;
}
// setIsConstructionVtable();
if (!isValid) {
return;
}
setLength();
if (!isValid) {
return;
}
try {
applyVtableData();
} catch (Exception e) {
isValid = false;
}
findInternalVtables();
}
public boolean equals(Vtable vtable) {
return vtable.getAddress().equals(vtableAddress);
}
private void checkValidTop() {
// check for existing vtable name - if has a non-default label that isn't vtable
// then assume not valid
Symbol symbol = symbolTable.getPrimarySymbol(vtableAddress);
if (symbol != null && symbol.getSource() != SourceType.DEFAULT) {
if (symbol.getName().contains(VTABLE_LABEL)) {
isValid = true;
return;
}
isValid = false;
return;
}
// for ones with no symbol or default symbol check for long value/non address at
// top
// if is an address not valid
// if is not an address may be valid - will need further checks in other methods
Address referencedAddress = getReferencedAddress(vtableAddress);
if (referencedAddress != null) {
isValid = false;
return;
}
// May or may not be valid but so far valid
isValid = true;
}
public Address getAddress() {
return vtableAddress;
}
public boolean isExternal() {
return inExternalMemory;
}
public boolean isValid() {
return isValid;
}
public Address getTypeinfoRefAddress() {
return typeinfoRef.getAddress();
}
public GccTypeinfo getReferencedTypeinfo() {
return (GccTypeinfo) typeinfoRef.getReferencedTypeinfo();
}
protected void setTypeinfoAddress() {
GccTypeinfo typeinfo = getReferencedTypeinfo();
typeinfoAddress = typeinfo.getAddress();
typeinfoNamespace = typeinfo.getNamespace();
}
public Address getTypeinfoAddress() {
return typeinfoAddress;
}
protected void setTopOffsetValue() {
try {
Address topOffset = typeinfoRefAddress.subtract(defaultPointerSize);
if (topOffset.getOffset() < vtableAddress.getOffset()) {
Msg.debug(this,"No offset field in vtable at " + vtableAddress.toString());
isValid = false;
return;
}
topOffsetValue = extendedFlatAPI.getLongValueAt(topOffset);
} catch (IllegalArgumentException e) {
Msg.debug(this, "Invalid vtable: " + vtableAddress.toString() + " No offset field");
isValid = false;
}
}
private void setIsInternalVtable() {
if (topOffsetValue == null) {
isValid = false;
return;
}
// if it has an existing label that is exactly "vtable" then set it true and
// return
Symbol primarySymbol = symbolTable.getPrimarySymbol(vtableAddress);
if (primarySymbol != null && primarySymbol.getName().equals("vtable")) {
isPrimary = true;
return;
}
// otherwise, use the topOffsetValue to figure it out
if (topOffsetValue == 0L) {
isPrimary = true;
} else {
isPrimary = false;
}
}
public Boolean isPrimary() {
if (isPrimary == null) {
isValid = false;
return null;
}
return isPrimary;
}
public void setNumVfunctions(int num) {
numVfunctions = num;
}
public Integer getNumVfunctions() {
if (numVfunctions == null) {
isValid = false;
return null;
}
return numVfunctions;
}
protected void setHasVfunctions() throws CancelledException {
Address typeinfoRefAddress = getTypeinfoRefAddress();
if (isPrimary == null) {
isValid = false;
return;
}
try {
Address possVfunctionTop = typeinfoRefAddress.add(defaultPointerSize);
numVfunctions = getNumFunctionPointers(possVfunctionTop, true, false);
if (numVfunctions == 0) {
hasVfunctions = false;
} else {
hasVfunctions = true;
vfunctionTop = possVfunctionTop;
}
} catch (AddressOutOfBoundsException e) {
hasVfunctions = false;
}
}
private int getNumFunctionPointers(Address topAddress, boolean allowNullFunctionPtrs,
boolean allowDefaultRefsInMiddle) throws CancelledException {
int numFunctionPointers = 0;
Address address = topAddress;
// if it has a primary non-default symbol and it isn't "vftable" then it isn't a vftable
Symbol primarySymbol = symbolTable.getPrimarySymbol(topAddress);
if(primarySymbol != null && primarySymbol.getSource() != SourceType.DEFAULT && !primarySymbol.getName().contains("vftable")) {
return numFunctionPointers;
}
MemoryBlock currentBlock = program.getMemory().getBlock(topAddress);
boolean stillInCurrentTable = true;
while (address != null && currentBlock.contains(address) && stillInCurrentTable
&& (isPossibleFunctionPointer(address) || (allowNullFunctionPtrs && isPossibleNullPointer(address)))) {
numFunctionPointers++;
address = address.add(defaultPointerSize);
Symbol symbol = symbolTable.getPrimarySymbol(address);
if (symbol == null) {
continue;
}
// never let non-default refs in middle
if (symbol.getSource() != SourceType.DEFAULT) {
stillInCurrentTable = false;
}
// if it gets here it is default
if (!allowDefaultRefsInMiddle) {
stillInCurrentTable = false;
}
}
//NEW: TESTING Don't allow single null pointer at top of vftable
//OR test to see if nulls then typeinfo ptr
// if(isPossibleNullPointer(topAddress) && numFunctionPointers == 1) {
// return 0;
// }
// check to see if last is null ptr and next addr after that is typeinfo ref - indicating the null is really top of next vtable
Address lastAddress = topAddress.add((numFunctionPointers-1)*defaultPointerSize);
if(isPossibleNullPointer(lastAddress) && (isTypeinfoRef(lastAddress.add(defaultPointerSize)))){
numFunctionPointers--;
}
return numFunctionPointers;
}
private boolean isTypeinfoRef(Address addr) {
Address referencedAddress = getReferencedAddress(addr);
if(referencedAddress == null) {
return false;
}
Data data = program.getListing().getDataAt(referencedAddress);
if(data == null) {
return false;
}
if(data.getBaseDataType().getName().contains("ClassTypeInfoStructure")) {
return true;
}
return false;
}
/**
* Method to determine if there are enough zeros to make a null poihnter and no
* references into or out of the middle
*
* @param address the given address
* @return true if the given address could be a valid null pointer, false if not
*/
private boolean isPossibleNullPointer(Address address) throws CancelledException {
if (!extendedFlatAPI.hasNumZeros(address, defaultPointerSize)) {
return false;
}
return true;
}
/**
* Method to determine if the given address contains a possible function pointer
*
* @param address the given address
* @return true if the given address contains a possible function pointer or
* false otherwise
* @throws CancelledException if cancelled
*/
private boolean isPossibleFunctionPointer(Address address) throws CancelledException {
// TODO: make one that works for all casea in helper
// TODO: make sure it recognizes the external functions
long longValue = extendedFlatAPI.getLongValueAt(address);
Register lowBitCodeMode = program.getRegister("LowBitCodeMode");
if (lowBitCodeMode != null) {
longValue = longValue & ~0x1;
}
Address possibleFunctionPointer = null;
try {
possibleFunctionPointer = address.getNewAddress(longValue);
} catch (AddressOutOfBoundsException e) {
return false;
}
if (possibleFunctionPointer == null) {
return false;
}
Function function = extendedFlatAPI.getFunctionAt(possibleFunctionPointer);
if (function != null) {
return true;
}
AddressSetView executeSet = program.getMemory().getExecuteSet();
if (!executeSet.contains(possibleFunctionPointer)) {
return false;
}
Instruction instruction = extendedFlatAPI.getInstructionAt(possibleFunctionPointer);
if (instruction != null) {
extendedFlatAPI.createFunction(possibleFunctionPointer, null);
return true;
}
boolean disassemble = extendedFlatAPI.disassemble(possibleFunctionPointer);
if (disassemble) {
// check for the case where there is conflicting data at the thumb offset
// function
// pointer and if so clear the data and redisassemble and remove the bad
// bookmark
long originalLongValue = extendedFlatAPI.getLongValueAt(address);
if (originalLongValue != longValue) {
Address offsetPointer = address.getNewAddress(originalLongValue);
if (extendedFlatAPI.getDataAt(offsetPointer) != null) {
extendedFlatAPI.clearListing(offsetPointer);
disassemble = extendedFlatAPI.disassemble(address);
Bookmark bookmark = getBookmarkAt(possibleFunctionPointer, BookmarkType.ERROR, "Bad Instruction",
"conflicting data");
if (bookmark != null) {
extendedFlatAPI.removeBookmark(bookmark);
}
}
}
extendedFlatAPI.createFunction(possibleFunctionPointer, null);
return true;
}
return false;
}
private Bookmark getBookmarkAt(Address address, String bookmarkType, String category, String commentContains)
throws CancelledException {
Bookmark[] bookmarks = program.getBookmarkManager().getBookmarks(address);
for (Bookmark bookmark : bookmarks) {
monitor.checkCancelled();
if (bookmark.getType().getTypeString().equals(bookmarkType) && bookmark.getCategory().equals(category)
&& bookmark.getComment().contains(commentContains)) {
return bookmark;
}
}
return null;
}
public void setHasVfunctions(boolean flag) {
hasVfunctions = flag;
}
public boolean hasVfunctions() {
return hasVfunctions;
}
public Address getVfunctionTop() {
return vfunctionTop;
}
protected void setLength() {
if (hasVfunctions == null) {
isValid = false;
return;
}
if (!hasVfunctions) {
length = (int) (typeinfoRefAddress.getOffset() + defaultPointerSize - vtableAddress.getOffset());
return;
}
if (numVfunctions == null || vfunctionTop == null) {
isValid = false;
return;
}
Address endAddr = vfunctionTop.add(numVfunctions * defaultPointerSize);
length = (int) (endAddr.getOffset() - vtableAddress.getOffset());
}
private void addToLength(int amountToAdd) {
length = length + amountToAdd;
}
public int getLength() {
return length;
}
private void findInternalVtables() throws CancelledException {
// if the current table is already an internal vtable there won't be any
// internal ones in it
if (!isPrimary) {
return;
}
boolean keepChecking = true;
int limit = length;
while (keepChecking) {
monitor.checkCancelled();
Address nextAddr = vtableAddress.add(length);
Address typeinfoAddr = typeinfo.getAddress();
int alignment = nextAddr.getSize()/8;
Address nextTypeinfoRefAddr = getNextReferenceTo(nextAddr, typeinfoAddr, alignment, limit);
if(nextTypeinfoRefAddr == null) {
keepChecking = false;
continue;
}
GccTypeinfoRef internalTypenfoRef = new GccTypeinfoRef(nextTypeinfoRefAddr, typeinfo, true);
Vtable possibleInternalVtable = new Vtable(program, nextAddr,internalTypenfoRef, isSpecial, inExternalMemory,
this, isConstruction, monitor);
if (!possibleInternalVtable.isValid()) {
keepChecking = false;
continue;
}
if (possibleInternalVtable.isPrimary()) {
keepChecking = false;
continue;
}
Namespace internalVtableNamespace = possibleInternalVtable.getNamespace();
if (internalVtableNamespace != null && internalVtableNamespace.equals(classNamespace)) {
addInternalVtable(possibleInternalVtable);
} else {
keepChecking = false;
}
}
}
private Address getNextReferenceTo(Address startAddress, Address refdAddress, int alignment, int limit) {
int offset = alignment;
while(offset < limit) {
Address addr = startAddress.add(offset);
Address referencedAddress = getReferencedAddress(addr);
if(referencedAddress != null && referencedAddress.equals(refdAddress)) {
return addr;
}
offset += alignment;
}
return null;
}
private void addInternalVtable(Vtable internalVtable) {
internalVtables.add(internalVtable);
addToLength(internalVtable.getLength());
}
public List<Vtable> getInternalVtables() {
return internalVtables;
}
// TODO: put in helper or ext api
private boolean inExternalBlock(Address address) {
MemoryBlock externalBlock = getExternalBlock();
if (externalBlock == null) {
return false;
}
if (externalBlock.contains(address)) {
return true;
}
return false;
}
private MemoryBlock getExternalBlock() {
return program.getMemory().getBlock("EXTERNAL");
}
public void setIsConstructionVtable(Boolean setting) {
isConstruction = setting;
}
public Boolean isConstructionVtable() {
return isConstruction;
}
private void figureOutNamespace() {
if(isConstruction == null) {
setNamespace(globalNamespace);
return;
}
// if construction vtable can't figure out from within vtable object
// have to assign later after further inspection of vtts and other info
if (isConstruction != null && isConstructionVtable()) {
setNamespace(globalNamespace);
return;
}
if (isPrimary()) {
setNamespace(typeinfoNamespace);
return;
}
// if not primary and the primary has same namespace then it is an internal
// vtable and can
// set the namespace to the typeinfo namespace
if (!primaryVtable.getNamespace().isGlobal() && primaryVtable.getNamespace().equals(typeinfoNamespace)) {
setNamespace(typeinfoNamespace);
return;
}
}
// for setting namespaces after vtable is created when they can't
// be determined by looking at internals of current vtable
public void setNamespace(Namespace namespace) {
classNamespace = namespace;
for(Vtable internalVtable : internalVtables) {
internalVtable.setNamespace(namespace);
}
}
public Namespace getNamespace() {
return classNamespace;
}
protected boolean applyVtableData() throws CancelledException, Exception {
Data dataAt = listing.getDataAt(vtableAddress);
// first check to see it is an erroneous vtable that has been made a byte array
// if so, clear it and start looking for the typeinfo reference
//TODO: check !isDefined and use known length to clear
if (dataAt != null && dataAt.isArray()) {
listing.clearCodeUnits(vtableAddress, vtableAddress, false);
}
if (dataAt != null && !dataAt.getDataType().getName().equals("long")) {
listing.clearCodeUnits(vtableAddress, vtableAddress, false);
}
// create the typeinfo pointer if there isn't already one
Data typeinfoPtr = listing.getDataAt(typeinfoRefAddress);
if (typeinfoPtr == null || !typeinfoPtr.isDefined()) {
DataType nullPointer = dataTypeManager.getPointer(null);
listing.createData(typeinfoRefAddress, nullPointer);
}
// create longs from top of vtable to the typeinfo reference
createLongs(this.vtableAddress, typeinfoRefAddress);
int numFunctionPointers = getNumVfunctions();
if (numFunctionPointers != 0) {
Address vftableAddress = getVfunctionTop();
createVftableArray(vftableAddress, numFunctionPointers);
}
return true;
}
public Data createVftableArray(Address vftableAddress, int numFunctionPointers)
throws CancelledException, AddressOutOfBoundsException {
listing.clearCodeUnits(vftableAddress,
vftableAddress.add((numFunctionPointers * defaultPointerSize - 1)), false);
DataType pointerDataType = dataTypeManager.getPointer(null);
ArrayDataType vftableArrayDataType = new ArrayDataType(pointerDataType, numFunctionPointers,
defaultPointerSize);
try {
Data vftableArrayData = listing.createData(vftableAddress, vftableArrayDataType);
return vftableArrayData;
} catch (Exception e) {
return null;
}
}
/**
* Method to create a series of long data types from the given start address to
* the given end address
*
* @param start the starting address
* @param end the ending address
* @throws CancelledException if cancelled
* @throws Exception if data has conflict when created
*/
private void createLongs(Address start, Address end) throws CancelledException, Exception {
DataType longDT = new LongDataType(dataTypeManager);
if (defaultPointerSize == 8) {
longDT = new LongLongDataType();
}
int offset = 0;
Address address = start;
while (address != null && !address.equals(end)) {
listing.clearCodeUnits(address, address.add(defaultPointerSize - 1),false);
listing.createData(address, longDT);
offset += defaultPointerSize;
address = getAddress(start, offset);
}
}
/**
* Method to get address at address + offset
*
* @param address the given address
* @param offset the given offset
* @return the address at address + offset or null if it doesn't exist
*/
private Address getAddress(Address address, int offset) {
try {
Address newAddress = address.add(offset);
return newAddress;
} catch (AddressOutOfBoundsException e) {
return null;
}
}
private Address getReferencedAddress(Address address) {
int addressSize = address.getSize();
Memory memory = program.getMemory();
try {
if (addressSize == 32) {
long offset32 = memory.getInt(address);
Address newAddr = address.getNewAddress(offset32);
if(memory.contains(newAddr)) {
return newAddr;
}
return null;
}
else if (addressSize == 64) {
long offset64 = memory.getLong(address);
Address newAddr = address.getNewAddress(offset64);
if(memory.contains(newAddr)) {
return newAddr;
}
return null;
}
else {
return null;
}
}
catch (MemoryAccessException e) {
return null;
}
}
}

View file

@ -0,0 +1,54 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package classrecovery;
import java.util.ArrayList;
import java.util.List;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.Namespace;
public class Vtt {
Address vttAddress;
Namespace namespace;
List<Address> pointers = new ArrayList<Address>();
public Vtt(Address vttAddress, Namespace namespace){
this.vttAddress = vttAddress;
this.namespace = namespace;
}
public Address getAddress() {
return vttAddress;
}
public Namespace getNamespace() {
return namespace;
}
public void addPointerToList(Address pointer) {
pointers.add(pointer);
}
public boolean containsPointer(Address pointer) {
return(pointers.contains(pointer));
}
public int getNumPtrs() {
return pointers.size();
}
}