mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 23:17:03 +08:00
Merge remote-tracking branch 'origin/GP-4364_emteere_SwitchCallotherOptimization--SQUASHED'
This commit is contained in:
+127
-30
@@ -18,8 +18,6 @@ package ghidra.app.plugin.core.analysis;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import generic.concurrent.*;
|
import generic.concurrent.*;
|
||||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||||
import ghidra.app.cmd.function.DecompilerSwitchAnalysisCmd;
|
import ghidra.app.cmd.function.DecompilerSwitchAnalysisCmd;
|
||||||
@@ -32,8 +30,11 @@ import ghidra.framework.options.Options;
|
|||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.block.*;
|
import ghidra.program.model.block.*;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.InjectPayload;
|
||||||
|
import ghidra.program.model.lang.PcodeInjectLibrary;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -53,10 +54,11 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
|
|||||||
public static final int OPTION_DEFAULT_DECOMPILER_TIMEOUT_SECS = 60;
|
public static final int OPTION_DEFAULT_DECOMPILER_TIMEOUT_SECS = 60;
|
||||||
private int decompilerTimeoutSecondsOption = OPTION_DEFAULT_DECOMPILER_TIMEOUT_SECS;
|
private int decompilerTimeoutSecondsOption = OPTION_DEFAULT_DECOMPILER_TIMEOUT_SECS;
|
||||||
|
|
||||||
|
// cache for pcode callother injection payloads
|
||||||
|
private HashMap<Long, InjectPayload> injectPayloadCache = new HashMap<>();
|
||||||
|
|
||||||
private boolean hitNonReturningFunction = false;
|
private boolean hitNonReturningFunction = false;
|
||||||
|
|
||||||
private Register isaModeSwitchRegister = null;
|
|
||||||
private Register isaModeRegister = null;
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Interface Methods
|
// Interface Methods
|
||||||
@@ -95,9 +97,6 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
|
|||||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
isaModeSwitchRegister = program.getRegister("ISAModeSwitch");
|
|
||||||
isaModeRegister = program.getRegister("ISA_MODE");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ArrayList<Address> locations = findLocations(program, set, monitor);
|
ArrayList<Address> locations = findLocations(program, set, monitor);
|
||||||
if (locations.isEmpty()) {
|
if (locations.isEmpty()) {
|
||||||
@@ -224,6 +223,16 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find locations that could be an unrecovered switches
|
||||||
|
*
|
||||||
|
* @param program program
|
||||||
|
* @param set area of program to check
|
||||||
|
* @param monitor monitor
|
||||||
|
* @return list of addresses that could be a switch
|
||||||
|
*
|
||||||
|
* @throws CancelledException if monitor cancels
|
||||||
|
*/
|
||||||
private ArrayList<Address> findLocations(Program program, AddressSetView set,
|
private ArrayList<Address> findLocations(Program program, AddressSetView set,
|
||||||
TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
@@ -244,6 +253,12 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for break type construct
|
||||||
|
if (hasUnrecoverableCallOther(program, instruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Address address = instruction.getMinAddress();
|
Address address = instruction.getMinAddress();
|
||||||
locations.add(address);
|
locations.add(address);
|
||||||
@@ -254,6 +269,110 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
|
|||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check an instruction for an unrecoverable computed destination due
|
||||||
|
* to calling a callOther pcode op that has no associated injection.
|
||||||
|
* If there is an associated injection, then it might yet be recoverable.
|
||||||
|
*
|
||||||
|
* @param program program
|
||||||
|
* @param instr branching instruction to check
|
||||||
|
* @return true if there is a callOther that will block switch recovery
|
||||||
|
*/
|
||||||
|
private boolean hasUnrecoverableCallOther(Program program, Instruction instr) {
|
||||||
|
HashSet<Varnode> callOtherOutputs = new HashSet<Varnode>();
|
||||||
|
|
||||||
|
PcodeOp[] pcode = instr.getPcode(true);
|
||||||
|
for (PcodeOp op : pcode) {
|
||||||
|
if (op.getOpcode() == PcodeOp.CALLOTHER) {
|
||||||
|
// if callother has defined pcode inject replacement
|
||||||
|
// then could recover
|
||||||
|
if (hasPcodeInject(program, op)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// save callother output varnode
|
||||||
|
Varnode dest = op.getOutput();
|
||||||
|
if (dest != null) {
|
||||||
|
callOtherOutputs.add(dest);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!callOtherOutputs.isEmpty() && op.getOpcode()==PcodeOp.BRANCHIND) {
|
||||||
|
// check if branching to an output varnode of callother
|
||||||
|
if (callOtherOutputs.contains(op.getInput(0))) {
|
||||||
|
// target is computed from a callother output
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if have a callother destinations, check for it as an input
|
||||||
|
if (!callOtherOutputs.isEmpty()) {
|
||||||
|
Varnode[] inputs = op.getInputs();
|
||||||
|
for (Varnode in : inputs) {
|
||||||
|
if (callOtherOutputs.contains(in)) {
|
||||||
|
Varnode dest = op.getOutput();
|
||||||
|
if (dest != null) {
|
||||||
|
callOtherOutputs.add(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the callOther Pcode op has an associated injection
|
||||||
|
*
|
||||||
|
* @param program program
|
||||||
|
* @param op callother pcode op
|
||||||
|
* @return true if there is a pcode injection, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean hasPcodeInject(Program program, PcodeOp op) {
|
||||||
|
long callOtherIndex = op.getInput(0).getOffset();
|
||||||
|
InjectPayload payload = findPcodeInjection(program, callOtherIndex);
|
||||||
|
|
||||||
|
return payload != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find out if a callother pcode op has a pcodeInjection attached to it
|
||||||
|
*
|
||||||
|
* @param program program
|
||||||
|
* @param callOtherIndex callOther ID index
|
||||||
|
* @return injection payload or null if no register injections
|
||||||
|
*/
|
||||||
|
private InjectPayload findPcodeInjection(Program program, long callOtherIndex) {
|
||||||
|
InjectPayload payload = injectPayloadCache.get(callOtherIndex);
|
||||||
|
|
||||||
|
// has a payload value for the pcode callother index
|
||||||
|
if (payload != null) {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
// value null, if contains the key, then already looked up
|
||||||
|
if (injectPayloadCache.containsKey(callOtherIndex)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PcodeInjectLibrary snippetLibrary = program.getCompilerSpec().getPcodeInjectLibrary();
|
||||||
|
|
||||||
|
String opName = program.getLanguage().getUserDefinedOpName((int) callOtherIndex);
|
||||||
|
|
||||||
|
// segment is special named injection
|
||||||
|
if ("segment".equals(opName)) {
|
||||||
|
payload =
|
||||||
|
snippetLibrary.getPayload(InjectPayload.EXECUTABLEPCODE_TYPE, "segment_pcode");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
payload = snippetLibrary.getPayload(InjectPayload.CALLOTHERFIXUP_TYPE, opName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save payload in cache for next lookup
|
||||||
|
injectPayloadCache.put(callOtherIndex, payload);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isCallFixup(Program program, Instruction instr, FlowType flowType) {
|
private boolean isCallFixup(Program program, Instruction instr, FlowType flowType) {
|
||||||
if (!flowType.isCall()) {
|
if (!flowType.isCall()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -370,31 +489,9 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
|
|||||||
if (refType.isComputed() && refType.isFlow() &&
|
if (refType.isComputed() && refType.isFlow() &&
|
||||||
program.getMemory().contains(address)) {
|
program.getMemory().contains(address)) {
|
||||||
foundCount.incrementAndGet();
|
foundCount.incrementAndGet();
|
||||||
// don't propagate low code mode, let something else do that
|
|
||||||
// propogateCodeMode(context, address);
|
|
||||||
// don't make references, let other analysis do this
|
|
||||||
//return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propogateCodeMode(VarnodeContext context, Address addr) {
|
|
||||||
// get CodeModeRegister and flow it to destination, if it is set here
|
|
||||||
|
|
||||||
if (isaModeSwitchRegister == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BigInteger value = context.getValue(isaModeSwitchRegister, false);
|
|
||||||
if (value != null && program.getListing().getInstructionAt(addr) == null) {
|
|
||||||
try {
|
|
||||||
program.getProgramContext()
|
|
||||||
.setValue(isaModeRegister, addr, addr, value);
|
|
||||||
}
|
|
||||||
catch (ContextChangeException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, false, monitor);
|
}, false, monitor);
|
||||||
|
|
||||||
// only found one reference
|
// only found one reference
|
||||||
|
|||||||
Reference in New Issue
Block a user