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
// 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));
}
}