mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-4708 Adjustments to RecoverClassesFromRTTIScript and
FillOutStructureHelper
This commit is contained in:
parent
5ab72bf4f2
commit
184c657cfd
4 changed files with 112 additions and 119 deletions
|
@ -18,8 +18,6 @@ package classrecovery;
|
|||
|
||||
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.OffsetPcodeOpPair;
|
||||
import ghidra.app.util.opinion.PeLoader;
|
||||
|
@ -1480,10 +1478,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||
}
|
||||
}
|
||||
|
||||
DecompileOptions decompileOptions =
|
||||
DecompilerUtils.getDecompileOptions(serviceProvider, program);
|
||||
FillOutStructureHelper fillStructHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
|
||||
|
||||
for (Function constructor : constructorList) {
|
||||
|
||||
|
@ -1568,7 +1563,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||
|
||||
monitor.checkCancelled();
|
||||
|
||||
fillStructHelper.processStructure(highVariable, function, true, false);
|
||||
fillStructHelper.processStructure(highVariable, function, true, false, null);
|
||||
List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps();
|
||||
stores = removePcodeOpsNotInFunction(function, stores);
|
||||
|
||||
|
@ -2484,7 +2479,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||
baseClassDescriptorAddress.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Continue if the class has mult inh but base class is not on the parent list
|
||||
if (!recoveredClass.getParentList().contains(baseClass)) {
|
||||
continue;
|
||||
|
|
|
@ -22,8 +22,6 @@ import java.util.stream.Collectors;
|
|||
import ghidra.app.cmd.function.ApplyFunctionSignatureCmd;
|
||||
import ghidra.app.cmd.label.AddLabelCmd;
|
||||
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.OffsetPcodeOpPair;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
|
||||
|
@ -1135,10 +1133,7 @@ public class RecoveredClassHelper {
|
|||
highVariables
|
||||
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
|
||||
DecompileOptions decompileOptions =
|
||||
DecompilerUtils.getDecompileOptions(serviceProvider, program);
|
||||
FillOutStructureHelper fillStructHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
|
||||
|
||||
Address vftableAddress = null;
|
||||
for (HighVariable highVariable : highVariables) {
|
||||
|
@ -1146,7 +1141,8 @@ public class RecoveredClassHelper {
|
|||
monitor.checkCancelled();
|
||||
|
||||
Structure structure =
|
||||
fillStructHelper.processStructure(highVariable, function, true, false);
|
||||
fillStructHelper.processStructure(highVariable, function, true, false,
|
||||
decompilerUtils.getDecompilerInterface());
|
||||
|
||||
NoisyStructureBuilder componentMap = fillStructHelper.getComponentMap();
|
||||
|
||||
|
@ -1202,13 +1198,12 @@ public class RecoveredClassHelper {
|
|||
|
||||
Address address = getTargetAddressFromPcodeOp(pcodeOp);
|
||||
if (address.equals(vftableReference)) {
|
||||
|
||||
Varnode[] inputs = pcodeOp.getInputs();
|
||||
for (Varnode input : inputs) {
|
||||
monitor.checkCancelled();
|
||||
if (input.getHigh() != null) {
|
||||
highVars.add(input.getHigh());
|
||||
}
|
||||
Varnode input = pcodeOp.getInput(1);
|
||||
if (input.getDef() != null && input.getDef().getOpcode() == PcodeOp.CAST) {
|
||||
input = input.getDef().getInput(0);
|
||||
}
|
||||
if (input.getHigh() != null) {
|
||||
highVars.add(input.getHigh());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5855,16 +5850,13 @@ public class RecoveredClassHelper {
|
|||
highVariables
|
||||
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
|
||||
DecompileOptions decompileOptions =
|
||||
DecompilerUtils.getDecompileOptions(serviceProvider, program);
|
||||
FillOutStructureHelper fillStructHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
|
||||
|
||||
for (HighVariable highVariable : highVariables) {
|
||||
|
||||
monitor.checkCancelled();
|
||||
|
||||
fillStructHelper.processStructure(highVariable, function, true, false);
|
||||
fillStructHelper.processStructure(highVariable, function, true, false, null);
|
||||
List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps();
|
||||
stores = removePcodeOpsNotInFunction(function, stores);
|
||||
|
||||
|
|
|
@ -66,23 +66,24 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
|
|||
throw new AssertionError("program does not match location");
|
||||
}
|
||||
|
||||
Function function =
|
||||
program.getFunctionManager().getFunctionContaining(location.getAddress());
|
||||
if (function == null) {
|
||||
setStatusMsg("Function not found at " + location.getAddress());
|
||||
return false;
|
||||
}
|
||||
|
||||
FillOutStructureHelper fillStructureHelper =
|
||||
new FillOutStructureHelper(program, monitor);
|
||||
DecompInterface decompInterface = fillStructureHelper.setUpDecompiler(decompileOptions);
|
||||
try {
|
||||
Function function =
|
||||
program.getFunctionManager().getFunctionContaining(location.getAddress());
|
||||
if (function == null) {
|
||||
setStatusMsg("Function not found at " + location.getAddress());
|
||||
return false;
|
||||
}
|
||||
|
||||
FillOutStructureHelper fillStructureHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
|
||||
HighVariable var = null;
|
||||
|
||||
if (!(location instanceof DecompilerLocation dloc)) {
|
||||
// if we don't have one, make one, and map variable to a varnode
|
||||
Address storageAddr = computeStorageAddress(function);
|
||||
var = fillStructureHelper.computeHighVariable(storageAddr, function);
|
||||
var =
|
||||
fillStructureHelper.computeHighVariable(storageAddr, function, decompInterface);
|
||||
}
|
||||
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) {
|
||||
setStatusMsg("Failed to fill-out structure");
|
||||
return false;
|
||||
|
@ -131,6 +133,9 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
|
|||
Msg.showError(this, null, "Auto Create Structure Failed",
|
||||
"Failed to create Structure variable", e);
|
||||
}
|
||||
finally {
|
||||
decompInterface.dispose();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.Map.Entry;
|
|||
import ghidra.app.cmd.label.RenameLabelCmd;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.*;
|
||||
|
@ -35,11 +34,16 @@ import ghidra.util.exception.InvalidInputException;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Automatically creates a structure definition based on the references found by the decompiler.
|
||||
*
|
||||
* If the parameter is already a structure pointer, any new references found will be added
|
||||
* to the structure, even if the structure must grow.
|
||||
* 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 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 {
|
||||
|
||||
|
@ -61,7 +65,6 @@ public class FillOutStructureHelper {
|
|||
|
||||
private Program currentProgram;
|
||||
private TaskMonitor monitor;
|
||||
private DecompileOptions decompileOptions;
|
||||
|
||||
private static final int maxCallDepth = 1;
|
||||
|
||||
|
@ -75,36 +78,35 @@ public class FillOutStructureHelper {
|
|||
* Constructor.
|
||||
*
|
||||
* @param program the current program
|
||||
* @param decompileOptions decompiler options
|
||||
* (see {@link DecompilerUtils#getDecompileOptions(ServiceProvider, Program)})
|
||||
* @param monitor task monitor
|
||||
*/
|
||||
public FillOutStructureHelper(Program program, DecompileOptions decompileOptions,
|
||||
TaskMonitor monitor) {
|
||||
public FillOutStructureHelper(Program program, TaskMonitor monitor) {
|
||||
this.currentProgram = program;
|
||||
this.decompileOptions = decompileOptions;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a structure data type for a variable in the given function.
|
||||
* Unlike the applyTo() action, this method will not modify the function, its variables,
|
||||
* or any existing data-types. A new structure is always created.
|
||||
* @param var a parameter, local variable, or global variable used in the given function
|
||||
* @param function the function to process
|
||||
* @param createNewStructure if true a new structure with a unique name will always be generated,
|
||||
* if false and variable corresponds to a structure pointer the existing structure will be
|
||||
* Create or update a Structure data-type given a function and a root pointer variable.
|
||||
* The function must already be decompiled, but if a decompiler interface is provided, this
|
||||
* method will recursively follow variable references into CALLs, possibly triggering additional
|
||||
* decompilation.
|
||||
* @param var is the pointer variable
|
||||
* @param function is the function to process
|
||||
* @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.
|
||||
* @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
|
||||
* new unique Ghidra Class namespace with a new identically named structure returned. If false,
|
||||
* a new uniquely structure will be created.
|
||||
* @return a filled-in structure or null if one could not be created
|
||||
* new unique Ghidra Class namespace with a new identically named Structure returned. If false,
|
||||
* a new unique Structure will 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,
|
||||
boolean createNewStructure, boolean createClassIfNeeded) {
|
||||
boolean createNewStructure, boolean createClassIfNeeded, DecompInterface decomplib) {
|
||||
|
||||
if (var == null || var.getSymbol() == null || var.getOffset() >= 0) {
|
||||
if (var == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -120,7 +122,9 @@ public class FillOutStructureHelper {
|
|||
}
|
||||
|
||||
fillOutStructureDef(var);
|
||||
pushIntoCalls();
|
||||
if (decomplib != null) {
|
||||
pushIntoCalls(decomplib);
|
||||
}
|
||||
|
||||
long size = componentMap.getSize();
|
||||
if (size == 0) {
|
||||
|
@ -168,7 +172,7 @@ public class FillOutStructureHelper {
|
|||
/**
|
||||
* Retrieve the component map that was generated when structure was created using decompiler
|
||||
* 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
|
||||
*/
|
||||
public NoisyStructureBuilder getComponentMap() {
|
||||
|
@ -179,7 +183,7 @@ public class FillOutStructureHelper {
|
|||
* Retrieve the offset/pcodeOp pairs that are used to store data into the variable
|
||||
* used to fill-out structure.
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
* used to fill-out structure.
|
||||
* 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
|
||||
*/
|
||||
public List<OffsetPcodeOpPair> getLoadPcodeOps() {
|
||||
|
@ -237,8 +241,9 @@ public class FillOutStructureHelper {
|
|||
/**
|
||||
* Recursively visit calls that take the structure pointer as a parameter.
|
||||
* 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();
|
||||
|
||||
while (addressToCallInputMap.size() > 0) {
|
||||
|
@ -256,7 +261,7 @@ public class FillOutStructureHelper {
|
|||
doneSet.addRange(addr, addr);
|
||||
Function func = currentProgram.getFunctionManager().getFunctionAt(addr);
|
||||
Address storageAddr = savedList.get(addr);
|
||||
HighVariable paramHighVar = computeHighVariable(storageAddr, func);
|
||||
HighVariable paramHighVar = computeHighVariable(storageAddr, func, decomplib);
|
||||
if (paramHighVar != null) {
|
||||
fillOutStructureDef(paramHighVar);
|
||||
}
|
||||
|
@ -268,77 +273,73 @@ public class FillOutStructureHelper {
|
|||
* Decompile a function and return the resulting HighVariable associated with a storage address
|
||||
* @param storageAddress the storage address of the variable
|
||||
* @param function is the function
|
||||
* @param decomplib is the active interface to use for decompiling
|
||||
* @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) {
|
||||
return null;
|
||||
}
|
||||
DecompInterface decomplib = setUpDecompiler();
|
||||
HighVariable highVar = null;
|
||||
|
||||
// call decompiler to get syntax tree
|
||||
try {
|
||||
if (!decomplib.openProgram(currentProgram)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DecompileResults results = decomplib.decompileFunction(function,
|
||||
decomplib.getOptions().getDefaultTimeout(), monitor);
|
||||
if (monitor.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
|
||||
// no decompile...
|
||||
if (highFunc == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// try to map the variable
|
||||
HighSymbol sym =
|
||||
highFunc.getMappedSymbol(storageAddress, function.getEntryPoint().subtractWrap(1L));
|
||||
if (sym == null) {
|
||||
sym = highFunc.getMappedSymbol(storageAddress, null);
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getMappedSymbol(storageAddress, function.getEntryPoint());
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getLocalSymbolMap()
|
||||
.findLocal(storageAddress, function.getEntryPoint().subtractWrap(1L));
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getLocalSymbolMap().findLocal(storageAddress, null);
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getLocalSymbolMap()
|
||||
.findLocal(storageAddress, function.getEntryPoint());
|
||||
}
|
||||
if (sym == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
highVar = sym.getHighVariable();
|
||||
}
|
||||
finally {
|
||||
decomplib.dispose();
|
||||
DecompileResults results = decomplib.decompileFunction(function,
|
||||
decomplib.getOptions().getDefaultTimeout(), monitor);
|
||||
if (monitor.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HighFunction highFunc = results.getHighFunction();
|
||||
|
||||
// no decompile...
|
||||
if (highFunc == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// try to map the variable
|
||||
HighSymbol sym =
|
||||
highFunc.getMappedSymbol(storageAddress, function.getEntryPoint().subtractWrap(1L));
|
||||
if (sym == null) {
|
||||
sym = highFunc.getMappedSymbol(storageAddress, null);
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getMappedSymbol(storageAddress, function.getEntryPoint());
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getLocalSymbolMap()
|
||||
.findLocal(storageAddress, function.getEntryPoint().subtractWrap(1L));
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getLocalSymbolMap().findLocal(storageAddress, null);
|
||||
}
|
||||
if (sym == null) {
|
||||
sym = highFunc.getLocalSymbolMap()
|
||||
.findLocal(storageAddress, function.getEntryPoint());
|
||||
}
|
||||
if (sym == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
highVar = sym.getHighVariable();
|
||||
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
|
||||
*/
|
||||
private DecompInterface setUpDecompiler() {
|
||||
public DecompInterface setUpDecompiler(DecompileOptions options) {
|
||||
DecompInterface decomplib = new DecompInterface();
|
||||
decomplib.setOptions(decompileOptions);
|
||||
decomplib.setOptions(options);
|
||||
decomplib.toggleCCode(true);
|
||||
decomplib.toggleSyntaxTree(true);
|
||||
decomplib.setSimplificationStyle("decompile");
|
||||
if (!decomplib.openProgram(currentProgram)) {
|
||||
return null;
|
||||
}
|
||||
return decomplib;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue