GP-4708 Adjustments to RecoverClassesFromRTTIScript and

FillOutStructureHelper
This commit is contained in:
caheckman 2024-06-21 18:30:04 +00:00
parent 5ab72bf4f2
commit 184c657cfd
4 changed files with 112 additions and 119 deletions

View file

@ -18,8 +18,6 @@ package classrecovery;
import java.util.*; import java.util.*;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.decompiler.util.FillOutStructureHelper; import ghidra.app.decompiler.util.FillOutStructureHelper;
import ghidra.app.decompiler.util.FillOutStructureHelper.OffsetPcodeOpPair; import ghidra.app.decompiler.util.FillOutStructureHelper.OffsetPcodeOpPair;
import ghidra.app.util.opinion.PeLoader; import ghidra.app.util.opinion.PeLoader;
@ -1480,10 +1478,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
} }
} }
DecompileOptions decompileOptions = FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
DecompilerUtils.getDecompileOptions(serviceProvider, program);
FillOutStructureHelper fillStructHelper =
new FillOutStructureHelper(program, decompileOptions, monitor);
for (Function constructor : constructorList) { for (Function constructor : constructorList) {
@ -1568,7 +1563,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
monitor.checkCancelled(); monitor.checkCancelled();
fillStructHelper.processStructure(highVariable, function, true, false); fillStructHelper.processStructure(highVariable, function, true, false, null);
List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps(); List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps();
stores = removePcodeOpsNotInFunction(function, stores); stores = removePcodeOpsNotInFunction(function, stores);

View file

@ -22,8 +22,6 @@ import java.util.stream.Collectors;
import ghidra.app.cmd.function.ApplyFunctionSignatureCmd; import ghidra.app.cmd.function.ApplyFunctionSignatureCmd;
import ghidra.app.cmd.label.AddLabelCmd; import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.decompiler.util.FillOutStructureHelper; import ghidra.app.decompiler.util.FillOutStructureHelper;
import ghidra.app.decompiler.util.FillOutStructureHelper.OffsetPcodeOpPair; import ghidra.app.decompiler.util.FillOutStructureHelper.OffsetPcodeOpPair;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference; import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
@ -1135,10 +1133,7 @@ public class RecoveredClassHelper {
highVariables highVariables
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); .addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
DecompileOptions decompileOptions = FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
DecompilerUtils.getDecompileOptions(serviceProvider, program);
FillOutStructureHelper fillStructHelper =
new FillOutStructureHelper(program, decompileOptions, monitor);
Address vftableAddress = null; Address vftableAddress = null;
for (HighVariable highVariable : highVariables) { for (HighVariable highVariable : highVariables) {
@ -1146,7 +1141,8 @@ public class RecoveredClassHelper {
monitor.checkCancelled(); monitor.checkCancelled();
Structure structure = Structure structure =
fillStructHelper.processStructure(highVariable, function, true, false); fillStructHelper.processStructure(highVariable, function, true, false,
decompilerUtils.getDecompilerInterface());
NoisyStructureBuilder componentMap = fillStructHelper.getComponentMap(); NoisyStructureBuilder componentMap = fillStructHelper.getComponentMap();
@ -1202,17 +1198,16 @@ public class RecoveredClassHelper {
Address address = getTargetAddressFromPcodeOp(pcodeOp); Address address = getTargetAddressFromPcodeOp(pcodeOp);
if (address.equals(vftableReference)) { if (address.equals(vftableReference)) {
Varnode input = pcodeOp.getInput(1);
Varnode[] inputs = pcodeOp.getInputs(); if (input.getDef() != null && input.getDef().getOpcode() == PcodeOp.CAST) {
for (Varnode input : inputs) { input = input.getDef().getInput(0);
monitor.checkCancelled(); }
if (input.getHigh() != null) { if (input.getHigh() != null) {
highVars.add(input.getHigh()); highVars.add(input.getHigh());
} }
} }
} }
} }
}
return highVars; return highVars;
} }
@ -5855,16 +5850,13 @@ public class RecoveredClassHelper {
highVariables highVariables
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference)); .addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
DecompileOptions decompileOptions = FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
DecompilerUtils.getDecompileOptions(serviceProvider, program);
FillOutStructureHelper fillStructHelper =
new FillOutStructureHelper(program, decompileOptions, monitor);
for (HighVariable highVariable : highVariables) { for (HighVariable highVariable : highVariables) {
monitor.checkCancelled(); monitor.checkCancelled();
fillStructHelper.processStructure(highVariable, function, true, false); fillStructHelper.processStructure(highVariable, function, true, false, null);
List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps(); List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps();
stores = removePcodeOpsNotInFunction(function, stores); stores = removePcodeOpsNotInFunction(function, stores);

View file

@ -66,7 +66,6 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
throw new AssertionError("program does not match location"); throw new AssertionError("program does not match location");
} }
try {
Function function = Function function =
program.getFunctionManager().getFunctionContaining(location.getAddress()); program.getFunctionManager().getFunctionContaining(location.getAddress());
if (function == null) { if (function == null) {
@ -75,14 +74,16 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
} }
FillOutStructureHelper fillStructureHelper = FillOutStructureHelper fillStructureHelper =
new FillOutStructureHelper(program, decompileOptions, monitor); new FillOutStructureHelper(program, monitor);
DecompInterface decompInterface = fillStructureHelper.setUpDecompiler(decompileOptions);
try {
HighVariable var = null; HighVariable var = null;
if (!(location instanceof DecompilerLocation dloc)) { if (!(location instanceof DecompilerLocation dloc)) {
// if we don't have one, make one, and map variable to a varnode // if we don't have one, make one, and map variable to a varnode
Address storageAddr = computeStorageAddress(function); Address storageAddr = computeStorageAddress(function);
var = fillStructureHelper.computeHighVariable(storageAddr, function); var =
fillStructureHelper.computeHighVariable(storageAddr, function, decompInterface);
} }
else { else {
@ -108,7 +109,8 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
} }
} }
Structure structDT = fillStructureHelper.processStructure(var, function, false, true); Structure structDT =
fillStructureHelper.processStructure(var, function, false, true, decompInterface);
if (structDT == null) { if (structDT == null) {
setStatusMsg("Failed to fill-out structure"); setStatusMsg("Failed to fill-out structure");
return false; return false;
@ -131,6 +133,9 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
Msg.showError(this, null, "Auto Create Structure Failed", Msg.showError(this, null, "Auto Create Structure Failed",
"Failed to create Structure variable", e); "Failed to create Structure variable", e);
} }
finally {
decompInterface.dispose();
}
return false; return false;
} }

View file

@ -21,7 +21,6 @@ import java.util.Map.Entry;
import ghidra.app.cmd.label.RenameLabelCmd; import ghidra.app.cmd.label.RenameLabelCmd;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerUtils; import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -35,11 +34,16 @@ import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/** /**
* Automatically creates a structure definition based on the references found by the decompiler. * Automatically create a Structure data-type based on references found by the decompiler to a
* * root parameter or other variable.
* If the parameter is already a structure pointer, any new references found will be added
* to the structure, even if the structure must grow.
* *
* If the parameter is already a Structure pointer, any new references found can optionally be added
* to the existing Structure data-type.
* {@link #processStructure(HighVariable, Function, boolean, boolean, DecompInterface)} is the primary
* entry point to the helper, which computes the new or updated Structure based on an existing
* decompiled function. Decompilation, if not provided externally, can be performed by calling
* {@link #computeHighVariable(Address, Function, DecompInterface)}. A decompiler process,
* if not provided externally, can be started by calling {@link #setUpDecompiler(DecompileOptions)}.
*/ */
public class FillOutStructureHelper { public class FillOutStructureHelper {
@ -61,7 +65,6 @@ public class FillOutStructureHelper {
private Program currentProgram; private Program currentProgram;
private TaskMonitor monitor; private TaskMonitor monitor;
private DecompileOptions decompileOptions;
private static final int maxCallDepth = 1; private static final int maxCallDepth = 1;
@ -75,36 +78,35 @@ public class FillOutStructureHelper {
* Constructor. * Constructor.
* *
* @param program the current program * @param program the current program
* @param decompileOptions decompiler options
* (see {@link DecompilerUtils#getDecompileOptions(ServiceProvider, Program)})
* @param monitor task monitor * @param monitor task monitor
*/ */
public FillOutStructureHelper(Program program, DecompileOptions decompileOptions, public FillOutStructureHelper(Program program, TaskMonitor monitor) {
TaskMonitor monitor) {
this.currentProgram = program; this.currentProgram = program;
this.decompileOptions = decompileOptions;
this.monitor = monitor; this.monitor = monitor;
} }
/** /**
* Method to create a structure data type for a variable in the given function. * Create or update a Structure data-type given a function and a root pointer variable.
* Unlike the applyTo() action, this method will not modify the function, its variables, * The function must already be decompiled, but if a decompiler interface is provided, this
* or any existing data-types. A new structure is always created. * method will recursively follow variable references into CALLs, possibly triggering additional
* @param var a parameter, local variable, or global variable used in the given function * decompilation.
* @param function the function to process * @param var is the pointer variable
* @param createNewStructure if true a new structure with a unique name will always be generated, * @param function is the function to process
* if false and variable corresponds to a structure pointer the existing structure will be * @param createNewStructure if true a new Structure with a unique name will always be generated,
* if false and the variable corresponds to a Structure pointer, the existing Structure will be
* updated instead. * updated instead.
* @param createClassIfNeeded if true and variable corresponds to a <B>this</B> pointer without * @param createClassIfNeeded if true and variable corresponds to a <B>this</B> pointer without
* an assigned Ghidra Class (i.e., {@code void * this}), the function will be assigned to a * an assigned Ghidra Class (i.e., {@code void * this}), the function will be assigned to a
* new unique Ghidra Class namespace with a new identically named structure returned. If false, * new unique Ghidra Class namespace with a new identically named Structure returned. If false,
* a new uniquely structure will be created. * a new unique Structure will be created.
* @return a filled-in structure or null if one could not be created * @param decomplib is the (optional) decompiler interface, which can be used to recursively
* decompile into CALLs.
* @return a filled-in Structure or null if one could not be created
*/ */
public Structure processStructure(HighVariable var, Function function, public Structure processStructure(HighVariable var, Function function,
boolean createNewStructure, boolean createClassIfNeeded) { boolean createNewStructure, boolean createClassIfNeeded, DecompInterface decomplib) {
if (var == null || var.getSymbol() == null || var.getOffset() >= 0) { if (var == null) {
return null; return null;
} }
@ -120,7 +122,9 @@ public class FillOutStructureHelper {
} }
fillOutStructureDef(var); fillOutStructureDef(var);
pushIntoCalls(); if (decomplib != null) {
pushIntoCalls(decomplib);
}
long size = componentMap.getSize(); long size = componentMap.getSize();
if (size == 0) { if (size == 0) {
@ -168,7 +172,7 @@ public class FillOutStructureHelper {
/** /**
* Retrieve the component map that was generated when structure was created using decompiler * Retrieve the component map that was generated when structure was created using decompiler
* info. Results are not valid until * info. Results are not valid until
* {@link #processStructure(HighVariable, Function, boolean, boolean)} is invoked. * {@link #processStructure(HighVariable, Function, boolean, boolean, DecompInterface)} is invoked.
* @return componentMap * @return componentMap
*/ */
public NoisyStructureBuilder getComponentMap() { public NoisyStructureBuilder getComponentMap() {
@ -179,7 +183,7 @@ public class FillOutStructureHelper {
* Retrieve the offset/pcodeOp pairs that are used to store data into the variable * Retrieve the offset/pcodeOp pairs that are used to store data into the variable
* used to fill-out structure. * used to fill-out structure.
* Results are not valid until * Results are not valid until
* {@link #processStructure(HighVariable, Function, boolean, boolean)} is invoked. * {@link #processStructure(HighVariable, Function, boolean, boolean, DecompInterface)} is invoked.
* @return the pcodeOps doing the storing to the associated variable * @return the pcodeOps doing the storing to the associated variable
*/ */
public List<OffsetPcodeOpPair> getStorePcodeOps() { public List<OffsetPcodeOpPair> getStorePcodeOps() {
@ -190,7 +194,7 @@ public class FillOutStructureHelper {
* Retrieve the offset/pcodeOp pairs that are used to load data from the variable * Retrieve the offset/pcodeOp pairs that are used to load data from the variable
* used to fill-out structure. * used to fill-out structure.
* Results are not valid until * Results are not valid until
* {@link #processStructure(HighVariable, Function, boolean, boolean)} is invoked. * {@link #processStructure(HighVariable, Function, boolean, boolean, DecompInterface)} is invoked.
* @return the pcodeOps doing the loading from the associated variable * @return the pcodeOps doing the loading from the associated variable
*/ */
public List<OffsetPcodeOpPair> getLoadPcodeOps() { public List<OffsetPcodeOpPair> getLoadPcodeOps() {
@ -237,8 +241,9 @@ public class FillOutStructureHelper {
/** /**
* Recursively visit calls that take the structure pointer as a parameter. * Recursively visit calls that take the structure pointer as a parameter.
* Add any new references to the offsetToDataTypeMap. * Add any new references to the offsetToDataTypeMap.
* @param decomplib is the active interface for decompiling
*/ */
private void pushIntoCalls() { private void pushIntoCalls(DecompInterface decomplib) {
AddressSet doneSet = new AddressSet(); AddressSet doneSet = new AddressSet();
while (addressToCallInputMap.size() > 0) { while (addressToCallInputMap.size() > 0) {
@ -256,7 +261,7 @@ public class FillOutStructureHelper {
doneSet.addRange(addr, addr); doneSet.addRange(addr, addr);
Function func = currentProgram.getFunctionManager().getFunctionAt(addr); Function func = currentProgram.getFunctionManager().getFunctionAt(addr);
Address storageAddr = savedList.get(addr); Address storageAddr = savedList.get(addr);
HighVariable paramHighVar = computeHighVariable(storageAddr, func); HighVariable paramHighVar = computeHighVariable(storageAddr, func, decomplib);
if (paramHighVar != null) { if (paramHighVar != null) {
fillOutStructureDef(paramHighVar); fillOutStructureDef(paramHighVar);
} }
@ -268,21 +273,17 @@ public class FillOutStructureHelper {
* Decompile a function and return the resulting HighVariable associated with a storage address * Decompile a function and return the resulting HighVariable associated with a storage address
* @param storageAddress the storage address of the variable * @param storageAddress the storage address of the variable
* @param function is the function * @param function is the function
* @param decomplib is the active interface to use for decompiling
* @return the corresponding HighVariable or null * @return the corresponding HighVariable or null
*/ */
public HighVariable computeHighVariable(Address storageAddress, Function function) { public HighVariable computeHighVariable(Address storageAddress, Function function,
DecompInterface decomplib) {
if (storageAddress == null) { if (storageAddress == null) {
return null; return null;
} }
DecompInterface decomplib = setUpDecompiler();
HighVariable highVar = null; HighVariable highVar = null;
// call decompiler to get syntax tree // call decompiler to get syntax tree
try {
if (!decomplib.openProgram(currentProgram)) {
return null;
}
DecompileResults results = decomplib.decompileFunction(function, DecompileResults results = decomplib.decompileFunction(function,
decomplib.getOptions().getDefaultTimeout(), monitor); decomplib.getOptions().getDefaultTimeout(), monitor);
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
@ -321,24 +322,24 @@ public class FillOutStructureHelper {
} }
highVar = sym.getHighVariable(); highVar = sym.getHighVariable();
}
finally {
decomplib.dispose();
}
return highVar; return highVar;
} }
/** /**
* Set up a decompiler interface for recovering data-flow * Set up a decompiler interface and prepare for decompiling on the currentProgram.
* The interface can be used to pass to computeHighVariable or to processStructure.
* @param options are the options to pass to the decompiler
* @return the decompiler interface * @return the decompiler interface
*/ */
private DecompInterface setUpDecompiler() { public DecompInterface setUpDecompiler(DecompileOptions options) {
DecompInterface decomplib = new DecompInterface(); DecompInterface decomplib = new DecompInterface();
decomplib.setOptions(decompileOptions); decomplib.setOptions(options);
decomplib.toggleCCode(true); decomplib.toggleCCode(true);
decomplib.toggleSyntaxTree(true); decomplib.toggleSyntaxTree(true);
decomplib.setSimplificationStyle("decompile"); decomplib.setSimplificationStyle("decompile");
if (!decomplib.openProgram(currentProgram)) {
return null;
}
return decomplib; return decomplib;
} }