mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
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:
parent
269ea1ae7a
commit
1d14222f37
14 changed files with 4937 additions and 2131 deletions
|
@ -34,7 +34,7 @@
|
|||
// 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
|
||||
// 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
|
||||
// virtual inheritance but if the program contains DWARF, there will be some amount of data recovered
|
||||
// by the DWARF analyzer.
|
||||
|
@ -54,10 +54,20 @@
|
|||
|
||||
import java.io.File;
|
||||
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 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 ghidra.app.decompiler.DecompInterface;
|
||||
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.pdb.PdbParserConstants;
|
||||
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.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
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.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.GraphException;
|
||||
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 INDETERMINATE_BOOKMARK = "INDETERMINATE";
|
||||
|
||||
boolean hasDebugSymbols;
|
||||
boolean isGcc = false;
|
||||
boolean isWindows = false;
|
||||
boolean programHasRTTIApplied = false;
|
||||
boolean hasDebugSymbols = false;
|
||||
String ghidraVersion = null;
|
||||
|
||||
DecompilerScriptUtils decompilerUtils;
|
||||
|
@ -159,15 +184,16 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
|
||||
String errorMsg = validate();
|
||||
|
||||
if (!errorMsg.isEmpty()) {
|
||||
println(errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWindows()) {
|
||||
|
||||
|
||||
if (!isGcc() && isWindows()) {
|
||||
|
||||
if (!isRttiAnalyzed()) {
|
||||
println("Running the RTTIAnalyzer...");
|
||||
analysisMode = AnalysisMode.ENABLED;
|
||||
|
@ -192,11 +218,27 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS,
|
||||
nameVfunctions, hasDebugSymbols, monitor);
|
||||
}
|
||||
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()) {
|
||||
|
||||
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.");
|
||||
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.");
|
||||
|
||||
if (!runGcc) {
|
||||
return;
|
||||
}
|
||||
|
@ -376,8 +418,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
}
|
||||
|
||||
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();
|
||||
|
@ -438,7 +479,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
AttributedGraph g = new AttributedGraph("Recovered Classes Graph", graphType);
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
AttributedVertex classVertex =
|
||||
g.addVertex(recoveredClass.getClassPath().getPath(), recoveredClass.getName());
|
||||
|
@ -470,7 +511,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
recoveredClass.getParentToBaseTypeMap();
|
||||
|
||||
for (RecoveredClass parent : parents) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
AttributedVertex parentVertex =
|
||||
g.addVertex(parent.getClassPath().getPath(), parent.getName());
|
||||
|
||||
|
@ -541,11 +582,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
List<RecoveredClass> classHierarchyList = recoveredClass.getClassHierarchy();
|
||||
for (RecoveredClass currentClass : classHierarchyList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
println(currentClass.getName());
|
||||
}
|
||||
|
||||
|
@ -567,15 +608,26 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
}
|
||||
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
|
||||
*/
|
||||
private boolean isGcc() {
|
||||
|
||||
if (!ElfLoader.ELF_NAME.equals(currentProgram.getExecutableFormat())) {
|
||||
return false;
|
||||
}
|
||||
boolean isGcc;
|
||||
|
||||
boolean isCompilerSpecGcc = currentProgram.getCompilerSpec()
|
||||
.getCompilerSpecID()
|
||||
|
@ -584,6 +636,11 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (isCompilerSpecGcc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String compiler = currentProgram.getCompiler();
|
||||
if(compiler != null && compiler.contains("gcc")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MemoryBlock commentBlock = currentProgram.getMemory().getBlock(".comment");
|
||||
if (commentBlock == null) {
|
||||
|
@ -610,6 +667,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
return isGcc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to set the global variable isWindows
|
||||
|
@ -618,7 +676,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
String compilerID =
|
||||
currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase();
|
||||
isWindows = compilerID.contains("windows");
|
||||
boolean isWindows = compilerID.contains("windows");
|
||||
return isWindows;
|
||||
}
|
||||
|
||||
|
@ -741,7 +799,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append(" : ");
|
||||
int lastColon = stringBuffer.lastIndexOf(":");
|
||||
for (RecoveredClass parentClass : classHierarchyMap.keySet()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (classHierarchyMap.size() == 1) {
|
||||
//stringBuffer.append(" : ");
|
||||
getSimpleClassHierarchyString(stringBuffer, parentClass);
|
||||
|
@ -752,7 +810,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
//int lastColon = stringBuffer.lastIndexOf(":");
|
||||
for (int i = 0; i <= lastColon; i++) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append(" ");
|
||||
}
|
||||
getSimpleClassHierarchyString(stringBuffer, parentClass);
|
||||
|
@ -775,7 +833,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
|
||||
|
||||
for (MemoryBlock block : blocks) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
if (block.isInitialized()) {
|
||||
dataAddresses.add(block.getStart(), block.getEnd());
|
||||
|
@ -793,7 +851,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
bookmarkFunctionsOnList(recoveredClass.getConstructorList(), CONSTRUCTOR_BOOKMARK);
|
||||
}
|
||||
}
|
||||
|
@ -807,7 +865,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
bookmarkFunctionsOnList(recoveredClass.getDestructorList(), DESTRUCTOR_BOOKMARK);
|
||||
bookmarkFunctionsOnList(recoveredClass.getNonThisDestructors(), DESTRUCTOR_BOOKMARK);
|
||||
}
|
||||
|
@ -822,7 +880,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
List<RecoveredClass> recoveredClasses) throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
bookmarkFunctionsOnList(recoveredClass.getIndeterminateList(), INDETERMINATE_BOOKMARK);
|
||||
}
|
||||
}
|
||||
|
@ -841,7 +899,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
}
|
||||
|
||||
for (Function function : functions) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
Address address = function.getEntryPoint();
|
||||
recoverClassesFromRTTI.bookmarkAddress(address, comment);
|
||||
}
|
||||
|
@ -902,7 +960,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
private void printClassDefinitions(List<RecoveredClass> recoveredClasses)
|
||||
throws CancelledException {
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (!recoveredClass.hasParentClass()) {
|
||||
println(createClassDefinitionString(recoveredClass).toString());
|
||||
}
|
||||
|
@ -912,7 +970,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
private void outputClassDefinitions(List<RecoveredClass> recoveredClasses, PrintWriter out)
|
||||
throws CancelledException {
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (!recoveredClass.hasParentClass()) {
|
||||
out.append(createClassDefinitionString(recoveredClass));
|
||||
}
|
||||
|
@ -928,7 +986,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
*/
|
||||
private void printClassInfo(List<RecoveredClass> recoveredClasses) throws CancelledException {
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (!recoveredClass.hasParentClass()) {
|
||||
println(createClassInfoString(recoveredClass).toString());
|
||||
}
|
||||
|
@ -938,12 +996,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
private void printClassParents(List<RecoveredClass> recoveredClasses)
|
||||
throws CancelledException {
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
String printString = new String("\n" + recoveredClass.getName() + "\n");
|
||||
if (recoveredClass.hasParentClass()) {
|
||||
List<RecoveredClass> parentList = recoveredClass.getParentList();
|
||||
for (RecoveredClass parent : parentList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
printString = printString.concat("\t" + parent.getName() + "\n");
|
||||
}
|
||||
}
|
||||
|
@ -963,7 +1021,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
StringBuffer wholeBuffer = new StringBuffer();
|
||||
wholeBuffer.append("\r\n");
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (!recoveredClass.hasChildClass()) {
|
||||
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
|
@ -990,7 +1048,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
wholeBuffer.append("\r\n");
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (!recoveredClass.hasChildClass()) {
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
wholeBuffer.append(
|
||||
|
@ -1011,7 +1069,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (!recoveredClass.hasParentClass()) {
|
||||
out.append(createClassInfoString(recoveredClass).toString());
|
||||
}
|
||||
|
@ -1070,7 +1128,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
int total = 0;
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
List<Function> constructorList = recoveredClass.getConstructorOrDestructorFunctions();
|
||||
total += constructorList.size();
|
||||
}
|
||||
|
@ -1088,7 +1146,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
int total = 0;
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
List<Function> inlineList = recoveredClass.getInlinedConstructorList();
|
||||
total += inlineList.size();
|
||||
}
|
||||
|
@ -1102,7 +1160,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
*/
|
||||
private void printAddresses(List<Address> addresses) throws CancelledException {
|
||||
for (Address element : addresses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
println(element.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1117,7 +1175,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
PrintWriter out) throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
out.append(printClassParentsandChildren(recoveredClass));
|
||||
}
|
||||
}
|
||||
|
@ -1131,7 +1189,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
throws CancelledException {
|
||||
|
||||
for (RecoveredClass recoveredClass : recoveredClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
println(printClassParentsandChildren(recoveredClass).toString());
|
||||
}
|
||||
}
|
||||
|
@ -1157,7 +1215,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (recoveredClass.hasParentClass()) {
|
||||
Set<RecoveredClass> keySet = recoveredClass.getClassHierarchyMap().keySet();
|
||||
for (RecoveredClass parent : keySet) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + parent.getName() + "\r\n");
|
||||
}
|
||||
}
|
||||
|
@ -1168,7 +1226,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (recoveredClass.hasChildClass()) {
|
||||
List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
|
||||
for (RecoveredClass element : childClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + element.getName() + "\r\n");
|
||||
}
|
||||
|
||||
|
@ -1209,7 +1267,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
recoveredClass.getParentToBaseTypeMap();
|
||||
Set<RecoveredClass> ancestors = parentToBaseTypeMap.keySet();
|
||||
for (RecoveredClass ancestor : ancestors) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
if (directParents.contains(ancestor)) {
|
||||
|
||||
Boolean isVirtualParent = parentToBaseTypeMap.get(ancestor);
|
||||
|
@ -1232,7 +1290,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (recoveredClass.hasChildClass()) {
|
||||
List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
|
||||
for (RecoveredClass element : childClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + element.getName() + "\r\n");
|
||||
}
|
||||
|
||||
|
@ -1243,7 +1301,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append("constructor(s):\r\n");
|
||||
List<Function> constructorList = recoveredClass.getConstructorList();
|
||||
for (Function constructorFunction : constructorList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + constructorFunction.getName() + " " +
|
||||
constructorFunction.getEntryPoint().toString() + "\r\n");
|
||||
}
|
||||
|
@ -1254,7 +1312,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (inlinedConstructorList.size() > 0) {
|
||||
stringBuffer.append("inlined constructor(s):\r\n");
|
||||
for (Function inlinedConstructorFunction : inlinedConstructorList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + inlinedConstructorFunction.getName() + " " +
|
||||
inlinedConstructorFunction.getEntryPoint().toString() + "\r\n");
|
||||
}
|
||||
|
@ -1265,7 +1323,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append("destructor(s):\r\n");
|
||||
List<Function> destructorList = recoveredClass.getDestructorList();
|
||||
for (Function destructorFunction : destructorList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + destructorFunction.getName() + " " +
|
||||
destructorFunction.getEntryPoint().toString() + "\r\n");
|
||||
}
|
||||
|
@ -1276,7 +1334,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (inlinedDestructorList.size() > 0) {
|
||||
stringBuffer.append("inlined destructor(s):\r\n");
|
||||
for (Function inlinedDestructorFunction : inlinedDestructorList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + inlinedDestructorFunction.getName() + " " +
|
||||
inlinedDestructorFunction.getEntryPoint().toString() + "\r\n");
|
||||
}
|
||||
|
@ -1287,7 +1345,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (indeterminateList.size() > 0) {
|
||||
stringBuffer.append("\r\nindeterminate constructor(s) or destructor(s):\r\n");
|
||||
for (Function indeterminateFunction : indeterminateList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + indeterminateFunction.getName() + " " +
|
||||
indeterminateFunction.getEntryPoint().toString() + "\r\n");
|
||||
}
|
||||
|
@ -1299,7 +1357,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append("member function(s):\r\n");
|
||||
List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions();
|
||||
for (Function vfunction : virtualFunctions) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append(
|
||||
"\t" + vfunction.getName() + " " + vfunction.getEntryPoint().toString() + "\r\n");
|
||||
}
|
||||
|
@ -1318,7 +1376,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents();
|
||||
for (int i = 0; i < numDefinedComponents; i++) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
stringBuffer.append("\t" + definedComponents[i].getDataType() + " " +
|
||||
definedComponents[i].getFieldName() + "\r\n");
|
||||
|
@ -1330,7 +1388,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (recoveredClass.hasChildClass()) {
|
||||
List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
|
||||
for (RecoveredClass element : childClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append(createClassInfoString(element));
|
||||
}
|
||||
}
|
||||
|
@ -1377,7 +1435,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
}
|
||||
else {
|
||||
for (int i = autoParamCount - 1; i < paramCount; i++) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
Parameter param = function.getParameter(i);
|
||||
stringBuffer.append(param.getDataType().getDisplayName() + " " + param.getName());
|
||||
if (i == paramCount) {
|
||||
|
@ -1412,7 +1470,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append("constructor(s):\r\n");
|
||||
List<Function> constructorList = recoveredClass.getConstructorList();
|
||||
for (Function constructorFunction : constructorList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
String functionSignatureString = getFunctionSignatureString(constructorFunction, true);
|
||||
|
||||
stringBuffer.append(functionSignatureString);
|
||||
|
@ -1423,7 +1481,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append("\r\ndestructor(s):\r\n");
|
||||
List<Function> destructorList = recoveredClass.getDestructorList();
|
||||
for (Function destructorFunction : destructorList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
String functionSignatureString = getFunctionSignatureString(destructorFunction, true);
|
||||
stringBuffer.append(functionSignatureString);
|
||||
stringBuffer.append("\r\n");
|
||||
|
@ -1434,7 +1492,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (indeterminateList.size() > 0) {
|
||||
stringBuffer.append("\r\nindeterminate constructor or destructor function(s):\r\n");
|
||||
for (Function indeterminateFunction : indeterminateList) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
String functionSignatureString =
|
||||
getFunctionSignatureString(indeterminateFunction, true);
|
||||
stringBuffer.append(functionSignatureString);
|
||||
|
@ -1446,7 +1504,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
stringBuffer.append("\r\nmember function(s):\r\n");
|
||||
List<Function> virtualFunctions = recoveredClass.getAllVirtualFunctions();
|
||||
for (Function vfunction : virtualFunctions) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
String functionSignatureString = getFunctionSignatureString(vfunction, true);
|
||||
stringBuffer.append(functionSignatureString);
|
||||
stringBuffer.append("\r\n");
|
||||
|
@ -1466,7 +1524,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
|
||||
DataTypeComponent[] definedComponents = memberDataStructure.getDefinedComponents();
|
||||
for (int i = 0; i < numDefinedComponents; i++) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append("\t" + definedComponents[i].getDataType() + " " +
|
||||
definedComponents[i].getFieldName() + "\r\n");
|
||||
}
|
||||
|
@ -1477,7 +1535,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
if (recoveredClass.hasChildClass()) {
|
||||
List<RecoveredClass> childClasses = recoveredClass.getChildClasses();
|
||||
for (RecoveredClass element : childClasses) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
stringBuffer.append(createClassDefinitionString(element));
|
||||
}
|
||||
}
|
||||
|
@ -1485,4 +1543,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
|||
return stringBuffer;
|
||||
}
|
||||
|
||||
|
||||
private void printTime() {
|
||||
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
println(dtf.format(now));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -49,12 +49,16 @@ class EditStructureUtils {
|
|||
|
||||
for (DataTypeComponent newStructComponent : newStructComponents) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
int structOffset = newStructComponent.getOffset();
|
||||
|
||||
DataTypeComponent currentComponentAtOffset =
|
||||
containingStruct.getComponentAt(offset + structOffset);
|
||||
|
||||
if(currentComponentAtOffset == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DataType newStructCompDt = newStructComponent.getDataType();
|
||||
DataType containingComDt = currentComponentAtOffset.getDataType();
|
||||
|
@ -104,7 +108,7 @@ class EditStructureUtils {
|
|||
}
|
||||
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
DataTypeComponent component = structure.getComponentAt(i);
|
||||
if (component == null) {
|
||||
return false;
|
||||
|
@ -142,7 +146,7 @@ class EditStructureUtils {
|
|||
|
||||
while (offset < endOfClear) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
DataTypeComponent component = structure.getComponentContaining(offset);
|
||||
|
||||
|
@ -209,7 +213,7 @@ class EditStructureUtils {
|
|||
|
||||
while (offset < endOfRange) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
DataTypeComponent component = structure.getComponentContaining(offset);
|
||||
|
||||
|
@ -357,7 +361,7 @@ class EditStructureUtils {
|
|||
int index = offset - 1;
|
||||
|
||||
while (index >= 0) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
DataTypeComponent component = structure.getComponentAt(index);
|
||||
if (component != null && component.getDataType() == DataType.DEFAULT) {
|
||||
index--;
|
||||
|
@ -385,7 +389,7 @@ class EditStructureUtils {
|
|||
int index = offset;
|
||||
|
||||
while (index < structure.getLength()) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
DataTypeComponent component = structure.getComponentAt(index);
|
||||
if (component.getDataType() == DataType.DEFAULT) {
|
||||
index++;
|
||||
|
|
|
@ -16,19 +16,52 @@
|
|||
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
|
||||
package classrecovery;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.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.plugin.core.analysis.ReferenceAddressPair;
|
||||
import ghidra.app.util.PseudoDisassembler;
|
||||
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.IsolatedEntrySubModel;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.IBO32DataType;
|
||||
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.task.TaskMonitor;
|
||||
|
||||
|
@ -221,8 +254,18 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
|
|||
if (referencesFrom.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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);
|
||||
if (function == null) {
|
||||
// try to create function
|
||||
|
@ -597,13 +640,21 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
|
|||
try {
|
||||
if (addressSize == 32) {
|
||||
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) {
|
||||
|
||||
long offset64 = getLong(address);
|
||||
return address.getNewAddress(offset64);
|
||||
Address newAddr = address.getNewAddress(offset64);
|
||||
if(currentProgram.getMemory().contains(newAddr)) {
|
||||
return newAddr;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
else {
|
||||
|
@ -615,6 +666,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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -606,7 +606,7 @@ public class RecoveredClass {
|
|||
DataTypeComponent component =
|
||||
computedClassStructure.getComponentAt(offset.intValue());
|
||||
|
||||
if (!component.getDataType().equals(dataType)) {
|
||||
if (component != null && !component.getDataType().equals(dataType)) {
|
||||
computedClassStructure.replaceAtOffset(offset.intValue(), dataType,
|
||||
dataType.getLength(), component.getFieldName(), component.getComment());
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue