mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 00:55:39 +08:00
GP-6412: restoring high-/low- absint options
GP-6412: restoring high-/low- absint options GP-6412: first pass, untested GP-6412: first pass, untested
This commit is contained in:
@@ -43,8 +43,8 @@ public class LisaLaunchScript extends GhidraScript {
|
||||
return;
|
||||
}
|
||||
|
||||
PcodeFrontend frontend = new PcodeFrontend();
|
||||
Program p = frontend.doWork(currentProgram.getListing(), currentAddress);
|
||||
PcodeFrontend frontend = new PcodeFrontend(state.getTool());
|
||||
Program p = frontend.doWork(currentProgram.getListing(), currentAddress, false);
|
||||
|
||||
DefaultConfiguration conf = new DefaultConfiguration();
|
||||
conf.serializeResults = true;
|
||||
|
||||
@@ -297,7 +297,7 @@ public class Lisa_ResolveX86orX64LinuxSyscallsScript extends GhidraScript {
|
||||
initLisa();
|
||||
Set<CFG> cfgs = new HashSet<>();
|
||||
for (Function func : funcsToCalls.keySet()) {
|
||||
CFG cfg = frontend.visitFunction(func, func.getEntryPoint());
|
||||
CFG cfg = frontend.visitFunction(func, func.getEntryPoint(), false);
|
||||
cfgs.add(cfg);
|
||||
}
|
||||
it.unive.lisa.program.Program p = frontend.getProgram();
|
||||
@@ -390,7 +390,7 @@ public class Lisa_ResolveX86orX64LinuxSyscallsScript extends GhidraScript {
|
||||
}
|
||||
|
||||
private void initLisa() {
|
||||
frontend = new PcodeFrontend();
|
||||
frontend = new PcodeFrontend(state.getTool());
|
||||
|
||||
DefaultConfiguration conf = new DefaultConfiguration();
|
||||
conf.serializeResults = true;
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
*/
|
||||
package ghidra.lisa.gui;
|
||||
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.lisa.pcode.analyses.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
import it.unive.lisa.DefaultConfiguration;
|
||||
import it.unive.lisa.analysis.dataflow.*;
|
||||
import it.unive.lisa.analysis.heap.*;
|
||||
@@ -42,7 +44,7 @@ import it.unive.lisa.interprocedural.context.*;
|
||||
/**
|
||||
* Parameters used to control LiSA
|
||||
*/
|
||||
public class LisaOptions {
|
||||
public class LisaOptions implements OptionsChangeListener {
|
||||
|
||||
// ResourceManager may be able to pull these from a configuration.
|
||||
|
||||
@@ -58,6 +60,9 @@ public class LisaOptions {
|
||||
public final static String OP_KEY_LISA_ANALYSIS_POST = "Post-State (vs Pre-)";
|
||||
public final static String OP_KEY_LISA_ANALYSIS_SHOW_TOP = "Display 'top' values";
|
||||
public final static String OP_KEY_LISA_ANALYSIS_SHOW_UNIQUE = "Display 'unique' values";
|
||||
public final static String OP_KEY_LISA_ANALYSIS_USE_HIGH_PCODE = "Use high pcode (experimental)";
|
||||
public final static String OP_KEY_LISA_ANALYSIS_SIMPLIFICATION_STYLE =
|
||||
"Decompiler simplification style";
|
||||
public final static String OP_KEY_LISA_ANALYSIS_CALL_DEPTH = "Compute CFGs to Depth";
|
||||
public final static String OP_KEY_LISA_ANALYSIS_THRESH = "Threshhold";
|
||||
|
||||
@@ -80,9 +85,11 @@ public class LisaOptions {
|
||||
public final static boolean DEFAULT_LISA_ANALYSIS_POST = false;
|
||||
public final static boolean DEFAULT_LISA_ANALYSIS_SHOW_TOP = false;
|
||||
public final static boolean DEFAULT_LISA_ANALYSIS_SHOW_UNIQUE = false;
|
||||
public final static int DEFAULT_LISA_ANALYSIS_CALL_DEPTH = 0;
|
||||
public final static int DEFAULT_LISA_ANALYSIS_THRESH = 5;
|
||||
|
||||
public final static boolean DEFAULT_LISA_ANALYSIS_USE_HIGH_PCODE = false;
|
||||
public final static String DEFAULT_LISA_ANALYSIS_SIMPLIFICATION_STYLE = "normalize";
|
||||
public final static int DEFAULT_LISA_ANALYSIS_CALL_DEPTH = 0;
|
||||
public final static int DEFAULT_LISA_ANALYSIS_THRESH = 5;
|
||||
|
||||
public final static String DEFAULT_LISA_ANALYSIS_OUTDIR = "";
|
||||
public final static String DEFAULT_LISA_ANALYSIS_GRAPH = GraphOption.DEFAULT.optionString;
|
||||
public final static boolean DEFAULT_LISA_ANALYSIS_SERIAL = false;
|
||||
@@ -90,6 +97,8 @@ public class LisaOptions {
|
||||
public final static String DEFAULT_LISA_TOP_REPRESENTATION = "#TOP#";
|
||||
private static String suppress; // Suppressed in results (typically #TOP#)
|
||||
|
||||
private LisaPlugin plugin;
|
||||
|
||||
private HeapDomainOption heapDomainOption;
|
||||
private TypeDomainOption typeDomainOption;
|
||||
private ValueDomainOption valueDomainOption;
|
||||
@@ -106,6 +115,12 @@ public class LisaOptions {
|
||||
private boolean postState;
|
||||
private boolean showTop;
|
||||
private boolean showUnique;
|
||||
private boolean useHighPcode;
|
||||
private String simplificationStyle;
|
||||
|
||||
public LisaOptions(LisaPlugin lisaPlugin) {
|
||||
this.plugin = lisaPlugin;
|
||||
}
|
||||
|
||||
public static enum HeapDomainOption {
|
||||
HEAP_MONOLITHIC("Monolithic"),
|
||||
@@ -166,9 +181,11 @@ public class LisaOptions {
|
||||
public static enum ValueDomainOption {
|
||||
VALUE_CONSTPROP("Numeric: ConstantPropagation"),
|
||||
VALUE_INTERVAL("Numeric: Interval"),
|
||||
VALUE_INTERVAL_LX86("Numeric: Interval (Low X86)"),
|
||||
VALUE_POWERSET("Numeric: NonRedundantPowersetOfInterval"),
|
||||
VALUE_PARITY("Numeric: Parity"),
|
||||
VALUE_PENTAGON("Numeric: Pentagon"),
|
||||
VALUE_PENTAGON_LX86("Numeric: Pentagon (Low X86)"),
|
||||
VALUE_SIGN("Numeric: Sign"),
|
||||
VALUE_UPPERBOUND("Numeric: UpperBound"),
|
||||
DDATA_AVAILABLE("Dataflow: AvailableExpressions"),
|
||||
@@ -197,7 +214,11 @@ public class LisaOptions {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public ValueDomain getDomain(Program program) {
|
||||
suppress = DEFAULT_LISA_TOP_REPRESENTATION;
|
||||
if (this == VALUE_INTERVAL || this == VALUE_POWERSET) {
|
||||
if (this == VALUE_INTERVAL ||
|
||||
this == VALUE_INTERVAL_LX86 ||
|
||||
this == VALUE_PENTAGON ||
|
||||
this == VALUE_PENTAGON_LX86 ||
|
||||
this == VALUE_POWERSET) {
|
||||
suppress = "[-Inf, +Inf]";
|
||||
}
|
||||
if (this == VALUE_UPPERBOUND) {
|
||||
@@ -216,8 +237,10 @@ public class LisaOptions {
|
||||
case VALUE_CONSTPROP -> new ValueEnvironment<>(
|
||||
new PcodeByteBasedConstantPropagation(program.getLanguage()));
|
||||
case VALUE_INTERVAL -> new ValueEnvironment<>(new PcodeInterval());
|
||||
case VALUE_INTERVAL_LX86 -> new ValueEnvironment<>(new PcodeIntervalLowX86());
|
||||
case VALUE_PARITY -> new ValueEnvironment<>(new PcodeParity());
|
||||
case VALUE_PENTAGON -> new PcodePentagon();
|
||||
case VALUE_PENTAGON_LX86 -> new PcodePentagonLowX86();
|
||||
case VALUE_POWERSET -> new ValueEnvironment<>(
|
||||
new PcodeNonRedundantPowersetOfInterval());
|
||||
case VALUE_SIGN -> new ValueEnvironment<>(new PcodeSign());
|
||||
@@ -461,6 +484,7 @@ public class LisaOptions {
|
||||
"Call graph input");
|
||||
|
||||
grabFromToolAndProgram(ownerPlugin, opt, program);
|
||||
opt.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,8 +514,10 @@ public class LisaOptions {
|
||||
callGraphOption = opt.getEnum(OP_KEY_LISA_ANALYSIS_CALL_GRAPH, CallGraphOption.RTA);
|
||||
postState = opt.getBoolean(OP_KEY_LISA_ANALYSIS_POST, DEFAULT_LISA_ANALYSIS_POST);
|
||||
showTop = opt.getBoolean(OP_KEY_LISA_ANALYSIS_SHOW_TOP, DEFAULT_LISA_ANALYSIS_SHOW_TOP);
|
||||
showUnique =
|
||||
opt.getBoolean(OP_KEY_LISA_ANALYSIS_SHOW_UNIQUE, DEFAULT_LISA_ANALYSIS_SHOW_UNIQUE);
|
||||
showUnique = opt.getBoolean(OP_KEY_LISA_ANALYSIS_SHOW_UNIQUE, DEFAULT_LISA_ANALYSIS_SHOW_UNIQUE);
|
||||
useHighPcode = opt.getBoolean(OP_KEY_LISA_ANALYSIS_USE_HIGH_PCODE, DEFAULT_LISA_ANALYSIS_USE_HIGH_PCODE);
|
||||
simplificationStyle = opt.getString(OP_KEY_LISA_ANALYSIS_SIMPLIFICATION_STYLE,
|
||||
DEFAULT_LISA_ANALYSIS_SIMPLIFICATION_STYLE);
|
||||
cfgDepth = opt.getInt(OP_KEY_LISA_ANALYSIS_CALL_DEPTH, DEFAULT_LISA_ANALYSIS_CALL_DEPTH);
|
||||
threshhold = opt.getInt(OP_KEY_LISA_ANALYSIS_THRESH, DEFAULT_LISA_ANALYSIS_THRESH);
|
||||
outputDir = opt.getString(OP_KEY_LISA_ANALYSIS_OUTDIR, DEFAULT_LISA_ANALYSIS_OUTDIR);
|
||||
@@ -612,6 +638,22 @@ public class LisaOptions {
|
||||
this.showUnique = showUnique;
|
||||
}
|
||||
|
||||
public boolean isHighPcode() {
|
||||
return useHighPcode;
|
||||
}
|
||||
|
||||
public void setHighPcode(boolean useHighPcode) {
|
||||
this.useHighPcode = useHighPcode;
|
||||
}
|
||||
|
||||
public String getSimplificationStyle() {
|
||||
return simplificationStyle;
|
||||
}
|
||||
|
||||
public void setSimplificationStyle(String style) {
|
||||
this.simplificationStyle = style;
|
||||
}
|
||||
|
||||
public GraphOption getGraphOption() {
|
||||
return graphOption;
|
||||
}
|
||||
@@ -632,4 +674,12 @@ public class LisaOptions {
|
||||
return suppress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) throws OptionsVetoException {
|
||||
if (optionName.equals(OP_KEY_LISA_ANALYSIS_USE_HIGH_PCODE)) {
|
||||
plugin.clearCfgs(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class LisaPlugin extends ProgramPlugin implements OptionsChangeListener,
|
||||
|
||||
public LisaPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
setOptions(new LisaOptions());
|
||||
setOptions(new LisaOptions(this));
|
||||
createActions();
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ public class LisaPlugin extends ProgramPlugin implements OptionsChangeListener,
|
||||
return;
|
||||
}
|
||||
Msg.info(this, "Adding "+f);
|
||||
CFG cfg = frontend.visitFunction(f, f.getEntryPoint());
|
||||
CFG cfg = frontend.visitFunction(f, f.getEntryPoint(), options.isHighPcode());
|
||||
cfgs.add(cfg);
|
||||
}
|
||||
|
||||
@@ -278,6 +278,9 @@ public class LisaPlugin extends ProgramPlugin implements OptionsChangeListener,
|
||||
}
|
||||
Set<Function> calledFunctions = f.getCalledFunctions(new DummyCancellableTaskMonitor());
|
||||
for (Function func : calledFunctions) {
|
||||
if (func.isThunk()) {
|
||||
continue;
|
||||
}
|
||||
Address entryPoint = func.getEntryPoint();
|
||||
if (entryPoint.getAddressSpace().equals(f.getEntryPoint().getAddressSpace())) {
|
||||
addFunction(cfgs, func);
|
||||
@@ -286,7 +289,7 @@ public class LisaPlugin extends ProgramPlugin implements OptionsChangeListener,
|
||||
}
|
||||
}
|
||||
|
||||
private void clearCfgs(ProgramLocationActionContext context) {
|
||||
protected void clearCfgs(ProgramLocationActionContext context) {
|
||||
initProgram();
|
||||
frontend.clearTargets();
|
||||
}
|
||||
@@ -404,7 +407,7 @@ public class LisaPlugin extends ProgramPlugin implements OptionsChangeListener,
|
||||
}
|
||||
|
||||
private void initProgram() {
|
||||
frontend = new PcodeFrontend();
|
||||
frontend = new PcodeFrontend(this.getTool());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -84,4 +84,12 @@ public class PcodeBranch extends ControlFlowStructure {
|
||||
}
|
||||
}
|
||||
|
||||
public Statement getBranch() {
|
||||
return branch;
|
||||
}
|
||||
|
||||
public Statement getFallThrough() {
|
||||
return fallThrough;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import ghidra.lisa.pcode.WorkItem.PredType;
|
||||
import ghidra.lisa.pcode.contexts.*;
|
||||
import ghidra.lisa.pcode.expressions.*;
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.lisa.pcode.statements.PcodeNop;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Listing;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.SequenceNumber;
|
||||
@@ -64,7 +66,7 @@ public class PcodeCodeMemberVisitor {
|
||||
|
||||
private UnitContext currentUnit;
|
||||
|
||||
private Map<String, PcodeBranch> flows;
|
||||
private Map<Address, PcodeBranch> flows;
|
||||
|
||||
private int varCount = 0;
|
||||
|
||||
@@ -181,7 +183,8 @@ public class PcodeCodeMemberVisitor {
|
||||
}
|
||||
PredType type = item.getType();
|
||||
if (!type.equals(PredType.SEQ)) {
|
||||
String loc = pred.getLocation().getCodeLocation();
|
||||
PcodeLocation location = (PcodeLocation) pred.getLocation();
|
||||
Address loc = location.getAddress();
|
||||
PcodeBranch flow = flows.get(loc);
|
||||
if (flow == null) {
|
||||
flow = new PcodeBranch(cfg.getNodeList(), pred);
|
||||
@@ -194,10 +197,12 @@ public class PcodeCodeMemberVisitor {
|
||||
else {
|
||||
entrypoints.add(st);
|
||||
}
|
||||
|
||||
if (st instanceof Ret || st instanceof Return) {
|
||||
return;
|
||||
}
|
||||
List<StatementContext> branches = ctx.branch(this.listing, this.currentUnit);
|
||||
|
||||
List<StatementContext> branches = currentUnit.branch(ctx, this.listing);
|
||||
for (StatementContext branch : branches) {
|
||||
WorkItem n = new WorkItem(st, branch);
|
||||
if (ctx.isConditional()) {
|
||||
@@ -205,7 +210,7 @@ public class PcodeCodeMemberVisitor {
|
||||
}
|
||||
workItems.add(n);
|
||||
}
|
||||
StatementContext next = ctx.next(this.listing);
|
||||
StatementContext next = currentUnit.next(ctx, this.listing);
|
||||
if (next != null) {
|
||||
WorkItem n = new WorkItem(st, next);
|
||||
if (ctx.isBranch()) {
|
||||
@@ -273,6 +278,8 @@ public class PcodeCodeMemberVisitor {
|
||||
PcodeContext right = ctx.expression();
|
||||
|
||||
int opcode = ctx.opcode();
|
||||
|
||||
// Special case logic first
|
||||
switch (opcode) {
|
||||
case PcodeOp.COPY -> {
|
||||
Expression target = visitVariable(loc, left, true);
|
||||
@@ -302,12 +309,20 @@ public class PcodeCodeMemberVisitor {
|
||||
Expression expression = visitVarnode(loc, left, false);
|
||||
return new Assignment(cfg, loc, target, expression);
|
||||
}
|
||||
case PcodeOp.MULTIEQUAL -> {
|
||||
Expression target = visitVariable(loc, left, true);
|
||||
//Expression expression = visitVariable(loc, left, true);
|
||||
Expression expression = visitVarargsExpr(new VarargsExprContext(right));
|
||||
return new Assignment(cfg, loc, target, expression);
|
||||
}
|
||||
}
|
||||
|
||||
if (right == null) {
|
||||
throw new UnsupportedOperationException("Type of expression not supported: " + ctx);
|
||||
}
|
||||
|
||||
// Everything else...
|
||||
// NB: left is the output of the assignment, right the complete expression
|
||||
return switch (right.getNumInputs()) {
|
||||
case 1 -> {
|
||||
Expression target = visitVariable(loc, left, true);
|
||||
@@ -319,6 +334,12 @@ public class PcodeCodeMemberVisitor {
|
||||
Expression expression = visitBinaryExpr(new BinaryExprContext(right));
|
||||
yield new Assignment(cfg, loc, target, expression);
|
||||
}
|
||||
// NB: This may be unnecssary with the move of LOAD and STORE to the special-case section
|
||||
case 3 -> {
|
||||
Expression target = visitVariable(loc, left, true);
|
||||
Expression expression = visitTernaryExpr(new TernaryExprContext(right));
|
||||
yield new Assignment(cfg, loc, target, expression);
|
||||
}
|
||||
default -> throw new UnsupportedOperationException(
|
||||
"Type of expression not supported: " + ctx);
|
||||
};
|
||||
@@ -343,8 +364,25 @@ public class PcodeCodeMemberVisitor {
|
||||
return new PcodeBinaryExpression(cfg, ctx, lexp, rexp);
|
||||
}
|
||||
|
||||
public Expression visitVarnode(CodeLocation loc, VarnodeContext ctx, Type type,
|
||||
boolean define) {
|
||||
|
||||
public Expression visitTernaryExpr(TernaryExprContext ctx) {
|
||||
CodeLocation loc = ctx.location();
|
||||
Expression lexp = visitVariable(loc, ctx.left, false);
|
||||
Expression mexp = visitVariable(loc, ctx.middle, false);
|
||||
Expression rexp = visitVarnode(loc, ctx.right, false);
|
||||
return new PcodeTernaryExpression(cfg, ctx, lexp, mexp, rexp);
|
||||
}
|
||||
|
||||
public Expression visitVarargsExpr(VarargsExprContext ctx) {
|
||||
CodeLocation loc = ctx.location();
|
||||
Expression[] exps = new Expression[ctx.varargs.length];
|
||||
for (int i = 0; i < ctx.varargs.length; i++) {
|
||||
exps[i] = visitVariable(loc, ctx.varargs[i], false);
|
||||
}
|
||||
return new PcodeVarargsExpression(cfg, ctx, exps);
|
||||
}
|
||||
|
||||
public Expression visitVarnode(CodeLocation loc, VarnodeContext ctx, Type type, boolean define) {
|
||||
if (ctx.isConstant()) {
|
||||
return visitConstant(loc, ctx);
|
||||
}
|
||||
@@ -384,7 +422,7 @@ public class PcodeCodeMemberVisitor {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public Map<String, PcodeBranch> getFlows() {
|
||||
public Map<Address, PcodeBranch> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ package ghidra.lisa.pcode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.lisa.gui.LisaOptions;
|
||||
import ghidra.lisa.pcode.contexts.HighUnitContext;
|
||||
import ghidra.lisa.pcode.contexts.UnitContext;
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.lisa.pcode.types.PcodeTypeSystem;
|
||||
@@ -24,6 +30,8 @@ import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import it.unive.lisa.program.Program;
|
||||
import it.unive.lisa.program.cfg.*;
|
||||
import it.unive.lisa.program.cfg.Parameter;
|
||||
@@ -38,18 +46,34 @@ public class PcodeFrontend {
|
||||
|
||||
//private static final Logger log = LogManager.getLogger(PcodeFrontend.class);
|
||||
|
||||
private PluginTool tool;
|
||||
private ToolOptions options;
|
||||
private final Program program;
|
||||
private Map<Address, Set<Statement>> nodeMap = new HashMap<>();
|
||||
private Map<Address, CFG> targets = new HashMap<>();
|
||||
private Set<CFG> cfgs = new HashSet<>();
|
||||
|
||||
public PcodeFrontend() {
|
||||
// High Pcode-only
|
||||
private final Map<Function, HighFunction> decompCache = new HashMap<>();
|
||||
private String simplificationStyle;
|
||||
|
||||
public PcodeFrontend(PluginTool tool) {
|
||||
this.tool = tool;
|
||||
ToolOptions[] optionsList = tool.getOptions();
|
||||
for (ToolOptions opt : optionsList) {
|
||||
if (opt.getName().equals("Abstract Interpretation")) {
|
||||
this.options = opt;
|
||||
simplificationStyle =
|
||||
options.getString(LisaOptions.OP_KEY_LISA_ANALYSIS_SIMPLIFICATION_STYLE,
|
||||
LisaOptions.DEFAULT_LISA_ANALYSIS_SIMPLIFICATION_STYLE);
|
||||
}
|
||||
}
|
||||
program = new Program(new PcodeFeatures(), new PcodeTypeSystem());
|
||||
}
|
||||
|
||||
public Program doWork(Listing listing, Address startAddress) {
|
||||
public Program doWork(Listing listing, Address startAddress, boolean useHighPcode) {
|
||||
|
||||
Program p = visitListing(listing, startAddress);
|
||||
Program p = visitListing(listing, startAddress, useHighPcode);
|
||||
|
||||
Collection<CFG> baseline = p.getAllCFGs();
|
||||
for (CFG cfg : cfgs) {
|
||||
@@ -60,36 +84,65 @@ public class PcodeFrontend {
|
||||
return p;
|
||||
}
|
||||
|
||||
public Program visitListing(Listing listing, Address startAddress) {
|
||||
public Program visitListing(Listing listing, Address startAddress, boolean useHighPcode) {
|
||||
Program p = getProgram();
|
||||
for (Function f : listing.getFunctions(startAddress, false)) {
|
||||
CFG cfg = visitFunction(f, startAddress);
|
||||
CFG cfg = visitFunction(f, startAddress, useHighPcode);
|
||||
cfgs.add(cfg);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public CFG visitFunction(Function f, Address start) {
|
||||
public CFG visitFunction(Function f, Address start, boolean useHighPcode) {
|
||||
Program p = getProgram();
|
||||
UnitContext ctx = new UnitContext(this, program, f, start);
|
||||
UnitContext ctx = getUnitContext(f, start, useHighPcode);
|
||||
|
||||
CodeMemberDescriptor descr = mkDescriptor(ctx);
|
||||
PcodeCodeMemberVisitor visitor =
|
||||
new PcodeCodeMemberVisitor(descr, ctx.getListing());
|
||||
CFG cfg = visitor.visitCodeMember(ctx);
|
||||
targets.put(f.getEntryPoint(), cfg);
|
||||
cfgs.add(cfg);
|
||||
|
||||
ctx.unit().addCodeMember(cfg);
|
||||
p.addUnit(ctx.unit());
|
||||
Collection<Statement> nodes = cfg.getNodes();
|
||||
for (Statement statement : nodes) {
|
||||
Address addr = toAddr(statement.getLocation());
|
||||
nodeMap.computeIfAbsent(addr, a -> new HashSet<>()).add(statement);
|
||||
}
|
||||
|
||||
targets.put(f.getEntryPoint(), cfg);
|
||||
cfgs.add(cfg);
|
||||
|
||||
ctx.unit().addCodeMember(cfg);
|
||||
p.addUnit(ctx.unit());
|
||||
return cfg;
|
||||
}
|
||||
|
||||
private UnitContext getUnitContext(Function f, Address start, boolean useHighPcode) {
|
||||
if (useHighPcode) {
|
||||
DecompInterface decomp = null;
|
||||
try {
|
||||
decomp = new DecompInterface();
|
||||
decomp.toggleSyntaxTree(true);
|
||||
decomp.setSimplificationStyle(simplificationStyle);
|
||||
|
||||
DecompileOptions opts = DecompilerUtils.getDecompileOptions(tool, f.getProgram());
|
||||
decomp.setOptions(opts);
|
||||
|
||||
decomp.openProgram(f.getProgram());
|
||||
DecompileResults results =
|
||||
decomp.decompileFunction(f, opts.getDefaultTimeout(), TaskMonitor.DUMMY);
|
||||
HighFunction hfunc = results.getHighFunction();
|
||||
return new HighUnitContext(this, program, f, hfunc, start);
|
||||
}
|
||||
finally {
|
||||
if (decomp != null) {
|
||||
decomp.closeProgram();
|
||||
decomp.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new UnitContext(this, program, f, start);
|
||||
}
|
||||
|
||||
private Address toAddr(CodeLocation location) {
|
||||
if (location instanceof PcodeLocation loc) {
|
||||
return loc.op.getSeqnum().getTarget();
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
package ghidra.lisa.pcode.analyses;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Iterator;
|
||||
|
||||
import it.unive.lisa.util.numeric.*;
|
||||
@@ -185,8 +184,7 @@ public class LongInterval implements Iterable<Long>, Comparable<LongInterval> {
|
||||
* @return {@code true} if that condition holds
|
||||
*/
|
||||
public boolean is(long n) {
|
||||
BigDecimal number = low.getNumber();
|
||||
return isSingleton() && number != null && number.equals(new BigDecimal(n));
|
||||
return isSingleton() && low.is((int) n);
|
||||
}
|
||||
|
||||
private static LongInterval cacheAndRound(
|
||||
@@ -235,6 +233,35 @@ public class LongInterval implements Iterable<Long>, Comparable<LongInterval> {
|
||||
return cacheAndRound(new LongInterval(low.subtract(other.high), high.subtract(other.low)));
|
||||
}
|
||||
|
||||
// NB: This is NOT really a set-theoretic complement because the domain doesn't support sets of
|
||||
// intervals. Rather it handles two cases - (1) intervals unbounded at one end, and (2) intervals
|
||||
// representing booleans. Caveat emptor.
|
||||
public LongInterval complement() {
|
||||
if (this.equals(ONE)) {
|
||||
return ZERO;
|
||||
}
|
||||
if (this.equals(ZERO)) {
|
||||
return ONE;
|
||||
}
|
||||
if (high.equals(INFINITY.getHigh())) {
|
||||
return cacheAndRound(new LongInterval(INFINITY.getLow(), low.subtract(MathNumber.ONE)));
|
||||
}
|
||||
if (low.equals(INFINITY.getLow())) {
|
||||
return cacheAndRound(new LongInterval(high.add(MathNumber.ONE), INFINITY.getHigh()));
|
||||
}
|
||||
return INFINITY;
|
||||
}
|
||||
|
||||
public LongInterval flip() {
|
||||
if (high.equals(INFINITY.getHigh())) {
|
||||
return cacheAndRound(new LongInterval(INFINITY.getLow(), low));
|
||||
}
|
||||
if (low.equals(INFINITY.getLow())) {
|
||||
return cacheAndRound(new LongInterval(high, INFINITY.getHigh()));
|
||||
}
|
||||
return INFINITY;
|
||||
}
|
||||
|
||||
private static MathNumber min(
|
||||
MathNumber... nums) {
|
||||
if (nums.length == 0) {
|
||||
|
||||
+11
@@ -9,6 +9,7 @@ import java.util.Objects;
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.lisa.pcode.statements.PcodeBinaryOperator;
|
||||
import ghidra.pcode.exec.BytesPcodeArithmetic;
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
@@ -152,6 +153,11 @@ public class PcodeByteBasedConstantPropagation
|
||||
|
||||
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||
PcodeOp op = ploc.op;
|
||||
OpBehavior opBehavior = OpBehaviorFactory.getOpBehavior(op.getOpcode());
|
||||
if (opBehavior instanceof SpecialOpBehavior) {
|
||||
// TODO
|
||||
return top();
|
||||
}
|
||||
|
||||
byte[] bytes = arithmetic.unaryOp(op.getOpcode(), op.getOutput().getSize(),
|
||||
op.getInput(0).getSize(), arg.getValue(op.getInput(0).getSize()));
|
||||
@@ -183,6 +189,11 @@ public class PcodeByteBasedConstantPropagation
|
||||
|
||||
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||
PcodeOp op = ploc.op;
|
||||
OpBehavior opBehavior = OpBehaviorFactory.getOpBehavior(op.getOpcode());
|
||||
if (opBehavior instanceof SpecialOpBehavior) {
|
||||
// TODO
|
||||
return left;
|
||||
}
|
||||
if (left.isTop) {
|
||||
return specialCaseLogic(op);
|
||||
}
|
||||
|
||||
+9
-5
@@ -8,6 +8,7 @@ import java.util.*;
|
||||
import ghidra.lisa.pcode.locations.InstLocation;
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.pcode.exec.BytesPcodeArithmetic;
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFormatException;
|
||||
@@ -134,13 +135,16 @@ public class PcodeDataflowConstantPropagation implements
|
||||
PcodeOp op = ploc.op;
|
||||
|
||||
if (e instanceof UnaryExpression unary) {
|
||||
Long i = eval(unary.getExpression(), pp, domain);
|
||||
|
||||
if (i == null) {
|
||||
return i;
|
||||
OpBehavior opBehavior = OpBehaviorFactory.getOpBehavior(op.getOpcode());
|
||||
if (opBehavior instanceof SpecialOpBehavior) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
Long exp = eval(unary.getExpression(), pp, domain);
|
||||
if (exp == null) {
|
||||
return exp;
|
||||
}
|
||||
|
||||
Long exp = eval(unary.getExpression(), pp, domain);
|
||||
byte[] bytes = arithmetic.unaryOp(op.getOpcode(), op.getOutput().getSize(),
|
||||
op.getInput(0).getSize(), getValue(exp, op.getInput(0).getSize()));
|
||||
return Utils.bytesToLong(bytes, op.getOutput().getSize(), isBigEndian);
|
||||
|
||||
+208
-145
@@ -6,10 +6,12 @@ package ghidra.lisa.pcode.analyses;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
import generic.stl.Pair;
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.lisa.pcode.statements.PcodeBinaryOperator;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.Msg;
|
||||
import it.unive.lisa.analysis.*;
|
||||
import it.unive.lisa.analysis.lattices.Satisfiability;
|
||||
@@ -19,6 +21,7 @@ import it.unive.lisa.program.cfg.ProgramPoint;
|
||||
import it.unive.lisa.symbolic.value.*;
|
||||
import it.unive.lisa.symbolic.value.operator.binary.*;
|
||||
import it.unive.lisa.symbolic.value.operator.unary.UnaryOperator;
|
||||
import it.unive.lisa.type.Untyped;
|
||||
import it.unive.lisa.util.numeric.IntInterval;
|
||||
import it.unive.lisa.util.numeric.MathNumber;
|
||||
import it.unive.lisa.util.representation.StringRepresentation;
|
||||
@@ -47,6 +50,11 @@ public class PcodeInterval
|
||||
*/
|
||||
public static final PcodeInterval ZERO = new PcodeInterval(LongInterval.ZERO);
|
||||
|
||||
/**
|
||||
* The abstract true ({@code [1, 1]}) element.
|
||||
*/
|
||||
public static final PcodeInterval ONE = new PcodeInterval(LongInterval.ONE);
|
||||
|
||||
/**
|
||||
* The abstract top ({@code [-Inf, +Inf]}) element.
|
||||
*/
|
||||
@@ -62,10 +70,16 @@ public class PcodeInterval
|
||||
*/
|
||||
public final LongInterval interval;
|
||||
|
||||
public PcodeOp target;
|
||||
public MathNumber bound;
|
||||
public LongInterval intEq;
|
||||
public LongInterval intNeq;
|
||||
public boolean rightIsExpr;
|
||||
|
||||
/**
|
||||
* Builds the interval.
|
||||
*
|
||||
* @param interval the underlying {@link IntInterval}
|
||||
* @param interval the underlying {@link LongInterval}
|
||||
*/
|
||||
public PcodeInterval(
|
||||
LongInterval interval) {
|
||||
@@ -84,18 +98,6 @@ public class PcodeInterval
|
||||
this(new LongInterval(low, high));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the interval.
|
||||
*
|
||||
* @param low the lower bound
|
||||
* @param high the higher bound
|
||||
*/
|
||||
public PcodeInterval(
|
||||
long low,
|
||||
long high) {
|
||||
this(new LongInterval(low, high));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the top interval.
|
||||
*/
|
||||
@@ -137,6 +139,40 @@ public class PcodeInterval
|
||||
return representation().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeInterval evalIdentifier(
|
||||
Identifier id,
|
||||
ValueEnvironment<PcodeInterval> environment,
|
||||
ProgramPoint pp,
|
||||
SemanticOracle oracle)
|
||||
throws SemanticException {
|
||||
if (id.getCodeLocation() instanceof PcodeLocation ploc) {
|
||||
Varnode vn = id2vn(id);
|
||||
if (vn != null && vn.isConstant()) {
|
||||
return evalNonNullConstant(new Constant(Untyped.INSTANCE, vn.getOffset(), ploc), pp,
|
||||
oracle);
|
||||
}
|
||||
}
|
||||
return environment.getState(id);
|
||||
}
|
||||
|
||||
private Varnode id2vn(Identifier id) {
|
||||
String name = id.getName();
|
||||
PcodeLocation loc = (PcodeLocation) id.getCodeLocation();
|
||||
Varnode output = loc.op.getOutput();
|
||||
if (output != null) {
|
||||
if (name.equals(output.getAddress().toString())) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
for (Varnode vn : loc.op.getInputs()) {
|
||||
if (name.equals(vn.getAddress().toString())) {
|
||||
return vn;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeInterval evalNonNullConstant(
|
||||
Constant constant,
|
||||
@@ -174,14 +210,18 @@ public class PcodeInterval
|
||||
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||
PcodeOp op = ploc.op;
|
||||
int opcode = op.getOpcode();
|
||||
PcodeInterval result = arg;
|
||||
if (opcode == PcodeOp.INT_NEGATE || opcode == PcodeOp.INT_2COMP ||
|
||||
opcode == PcodeOp.FLOAT_NEG) {
|
||||
if (arg.isTop()) {
|
||||
return top();
|
||||
}
|
||||
return new PcodeInterval(arg.interval.mul(LongInterval.MINUS_ONE));
|
||||
result = arg.isTop() ? top()
|
||||
: new PcodeInterval(arg.interval.mul(LongInterval.MINUS_ONE));
|
||||
}
|
||||
return top();
|
||||
if (opcode == PcodeOp.BOOL_NEGATE) {
|
||||
result = arg.isTop() ? top()
|
||||
: new PcodeInterval(arg.interval.complement());
|
||||
result.target = ploc.op;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,8 +245,52 @@ public class PcodeInterval
|
||||
PcodeInterval right,
|
||||
ProgramPoint pp,
|
||||
SemanticOracle oracle) {
|
||||
PcodeLocation ploc = (PcodeLocation) pp.getLocation();
|
||||
PcodeOp op = ploc.op;
|
||||
if (!(pp.getLocation() instanceof PcodeLocation ploc)) {
|
||||
return top();
|
||||
}
|
||||
Pair<PcodeInterval, PcodeInterval> pair = new Pair<>(left, right);
|
||||
|
||||
if (operator instanceof PcodeBinaryOperator) {
|
||||
return evalPcodeBinaryExpression(pair, ploc.op);
|
||||
}
|
||||
|
||||
try {
|
||||
LongInterval interval1 = pair.first.interval;
|
||||
LongInterval interval2 = pair.second.interval;
|
||||
boolean exprOnRight = interval2.isFinite();
|
||||
if (exprOnRight && interval1.isFinite()) {
|
||||
exprOnRight = interval2.getLow().equals(interval2.getHigh());
|
||||
}
|
||||
PcodeInterval result = evalBooleanBinaryExpression(pair, operator);
|
||||
|
||||
result.target = ploc.op;
|
||||
if (!(operator instanceof LogicalOperation)) {
|
||||
Pair<PcodeInterval, PcodeInterval> basePair =
|
||||
new Pair<>(exprOnRight ? top() : left, exprOnRight ? right : top());
|
||||
PcodeInterval init = exprOnRight ? left : right;
|
||||
PcodeInterval bounds = exprOnRight ? right : left;
|
||||
PcodeInterval baseResult = evalBooleanBinaryExpression(basePair, operator);
|
||||
PcodeInterval eq = baseResult.narrowing(init);
|
||||
PcodeInterval neq = baseResult.complement().narrowing(init);
|
||||
result.bound = bounds.interval.isFinite() ? bounds.interval.getLow() : null;
|
||||
result.rightIsExpr = exprOnRight;
|
||||
result.intEq = eq.interval;
|
||||
result.intNeq = neq.interval;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (SemanticException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
}
|
||||
|
||||
return top();
|
||||
}
|
||||
|
||||
protected PcodeInterval evalPcodeBinaryExpression(Pair<PcodeInterval, PcodeInterval> pair,
|
||||
PcodeOp op) {
|
||||
|
||||
PcodeInterval left = pair.first;
|
||||
PcodeInterval right = pair.second;
|
||||
int opcode = op.getOpcode();
|
||||
if (!(opcode == PcodeOp.INT_DIV) && !(opcode == PcodeOp.FLOAT_DIV) &&
|
||||
(left.isTop() || right.isTop())) {
|
||||
@@ -268,8 +352,8 @@ public class PcodeInterval
|
||||
}
|
||||
|
||||
if (left.interval.getHigh().compareTo(MathNumber.ZERO) < 0) {
|
||||
yield new PcodeInterval(
|
||||
M.multiply(MathNumber.MINUS_ONE).add(MathNumber.ONE), MathNumber.ZERO);
|
||||
yield new PcodeInterval(M.multiply(MathNumber.MINUS_ONE).add(MathNumber.ONE),
|
||||
MathNumber.ZERO);
|
||||
}
|
||||
if (left.interval.getLow().compareTo(MathNumber.ZERO) > 0) {
|
||||
yield new PcodeInterval(MathNumber.ZERO, M.subtract(MathNumber.ONE));
|
||||
@@ -281,14 +365,74 @@ public class PcodeInterval
|
||||
};
|
||||
}
|
||||
|
||||
protected PcodeInterval evalBooleanBinaryExpression(Pair<PcodeInterval, PcodeInterval> pair,
|
||||
BinaryOperator operator) throws SemanticException {
|
||||
boolean exprOnRight = pair.second.interval.isFinite();
|
||||
|
||||
PcodeInterval starting = exprOnRight ? pair.first : pair.second;
|
||||
PcodeInterval eval = exprOnRight ? pair.second : pair.first;
|
||||
LongInterval lval = eval.interval;
|
||||
|
||||
boolean lowIsMinusInfinity = lval.lowIsMinusInfinity();
|
||||
PcodeInterval low_inf = new PcodeInterval(lval.getLow(), MathNumber.PLUS_INFINITY);
|
||||
PcodeInterval lowp1_inf =
|
||||
new PcodeInterval(lval.getLow().add(MathNumber.ONE), MathNumber.PLUS_INFINITY);
|
||||
PcodeInterval inf_high = new PcodeInterval(MathNumber.MINUS_INFINITY, lval.getHigh());
|
||||
PcodeInterval inf_highm1 =
|
||||
new PcodeInterval(MathNumber.MINUS_INFINITY, lval.getHigh().subtract(MathNumber.ONE));
|
||||
|
||||
PcodeInterval update = switch (operator) {
|
||||
case ComparisonEq op -> new PcodeInterval(lval);
|
||||
case ComparisonNe op -> new PcodeInterval(lval.complement());
|
||||
case ComparisonLe op -> {
|
||||
if (exprOnRight) {
|
||||
yield starting.glb(inf_high);
|
||||
}
|
||||
yield lowIsMinusInfinity ? null : starting.glb(low_inf);
|
||||
}
|
||||
case ComparisonLt op -> {
|
||||
if (exprOnRight) {
|
||||
yield lowIsMinusInfinity ? eval : starting.glb(inf_highm1);
|
||||
}
|
||||
yield lowIsMinusInfinity ? null : starting.glb(lowp1_inf);
|
||||
}
|
||||
case ComparisonGe op -> {
|
||||
if (exprOnRight) {
|
||||
yield lowIsMinusInfinity ? null : starting.glb(low_inf);
|
||||
}
|
||||
yield starting.glb(inf_high);
|
||||
}
|
||||
case ComparisonGt op -> {
|
||||
if (exprOnRight) {
|
||||
yield lowIsMinusInfinity ? null : starting.glb(lowp1_inf);
|
||||
}
|
||||
yield lowIsMinusInfinity ? eval : starting.glb(inf_highm1);
|
||||
}
|
||||
case LogicalOr op -> {
|
||||
MathNumber min = starting.interval.getLow().min(eval.interval.getLow());
|
||||
MathNumber max = starting.interval.getHigh().max(eval.interval.getHigh());
|
||||
yield new PcodeInterval(min, max);
|
||||
}
|
||||
default -> throw new AssertionError();
|
||||
};
|
||||
|
||||
if (update == null || update.isBottom()) {
|
||||
return new PcodeInterval(LongInterval.ZERO);
|
||||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeInterval lubAux(
|
||||
PcodeInterval other)
|
||||
throws SemanticException {
|
||||
MathNumber newLow = interval.getLow().min(other.interval.getLow());
|
||||
MathNumber newHigh = interval.getHigh().max(other.interval.getHigh());
|
||||
return newLow.isMinusInfinity() && newHigh.isPlusInfinity() ? top()
|
||||
PcodeInterval res = newLow.isMinusInfinity() && newHigh.isPlusInfinity() ? top()
|
||||
: new PcodeInterval(newLow, newHigh);
|
||||
res.target = this.target;
|
||||
res.bound = this.bound;
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -351,78 +495,6 @@ public class PcodeInterval
|
||||
PcodeInterval right,
|
||||
ProgramPoint pp,
|
||||
SemanticOracle oracle) {
|
||||
if (left.isTop() || right.isTop()) {
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
|
||||
if (!(operator instanceof PcodeBinaryOperator)) {
|
||||
if (operator instanceof ComparisonEq) {
|
||||
PcodeInterval glb = null;
|
||||
try {
|
||||
glb = left.glb(right);
|
||||
}
|
||||
catch (SemanticException e) {
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
|
||||
if (glb.isBottom()) {
|
||||
return Satisfiability.NOT_SATISFIED;
|
||||
}
|
||||
else if (left.interval.isSingleton() && left.equals(right)) {
|
||||
return Satisfiability.SATISFIED;
|
||||
}
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
else if (operator instanceof ComparisonLe) {
|
||||
PcodeInterval glb = null;
|
||||
try {
|
||||
glb = left.glb(right);
|
||||
}
|
||||
catch (SemanticException e) {
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
|
||||
if (glb.isBottom()) {
|
||||
return Satisfiability.fromBoolean(
|
||||
left.interval.getHigh().compareTo(right.interval.getLow()) <= 0);
|
||||
}
|
||||
// we might have a singleton as glb if the two intervals share a
|
||||
// bound
|
||||
if (glb.interval.isSingleton() &&
|
||||
left.interval.getHigh().compareTo(right.interval.getLow()) == 0) {
|
||||
return Satisfiability.SATISFIED;
|
||||
}
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
else if (operator instanceof ComparisonLt) {
|
||||
PcodeInterval glb = null;
|
||||
try {
|
||||
glb = left.glb(right);
|
||||
}
|
||||
catch (SemanticException e) {
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
|
||||
if (glb.isBottom()) {
|
||||
return Satisfiability.fromBoolean(
|
||||
left.interval.getHigh().compareTo(right.interval.getLow()) < 0);
|
||||
}
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
else if (operator instanceof ComparisonNe) {
|
||||
PcodeInterval glb = null;
|
||||
try {
|
||||
glb = left.glb(right);
|
||||
}
|
||||
catch (SemanticException e) {
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
if (glb.isBottom()) {
|
||||
return Satisfiability.SATISFIED;
|
||||
}
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
return Satisfiability.UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -457,62 +529,52 @@ public class PcodeInterval
|
||||
ProgramPoint dest,
|
||||
SemanticOracle oracle)
|
||||
throws SemanticException {
|
||||
Identifier id;
|
||||
PcodeInterval eval;
|
||||
boolean rightIsExpr;
|
||||
if (left instanceof Identifier) {
|
||||
|
||||
if (operator instanceof PcodeBinaryOperator) {
|
||||
return environment;
|
||||
}
|
||||
|
||||
Identifier id = null;
|
||||
PcodeInterval eval = null;
|
||||
PcodeInterval val = null;
|
||||
boolean complement = false;
|
||||
if (left instanceof Identifier leftId) {
|
||||
id = leftId;
|
||||
eval = eval(right, environment, src, oracle);
|
||||
id = (Identifier) left;
|
||||
rightIsExpr = true;
|
||||
}
|
||||
else if (right instanceof Identifier) {
|
||||
eval = eval(left, environment, src, oracle);
|
||||
id = (Identifier) right;
|
||||
rightIsExpr = false;
|
||||
}
|
||||
else
|
||||
return environment;
|
||||
|
||||
PcodeInterval starting = environment.getState(id);
|
||||
if (eval.isBottom() || starting.isBottom()) {
|
||||
return environment.bottom();
|
||||
}
|
||||
|
||||
boolean lowIsMinusInfinity = eval.interval.lowIsMinusInfinity();
|
||||
PcodeInterval low_inf = new PcodeInterval(eval.interval.getLow(), MathNumber.PLUS_INFINITY);
|
||||
PcodeInterval lowp1_inf =
|
||||
new PcodeInterval(eval.interval.getLow().add(MathNumber.ONE), MathNumber.PLUS_INFINITY);
|
||||
PcodeInterval inf_high =
|
||||
new PcodeInterval(MathNumber.MINUS_INFINITY, eval.interval.getHigh());
|
||||
PcodeInterval inf_highm1 = new PcodeInterval(MathNumber.MINUS_INFINITY,
|
||||
eval.interval.getHigh().subtract(MathNumber.ONE));
|
||||
|
||||
PcodeInterval update = null;
|
||||
if (!(operator instanceof PcodeBinaryOperator)) {
|
||||
if (operator instanceof ComparisonEq) {
|
||||
update = eval;
|
||||
val = environment.getState(leftId);
|
||||
if (val.isBottom()) {
|
||||
return environment.bottom();
|
||||
}
|
||||
else if (operator instanceof ComparisonLe) {
|
||||
update = rightIsExpr ? starting.glb(inf_high)
|
||||
: lowIsMinusInfinity ? null : starting.glb(low_inf);
|
||||
}
|
||||
else if (operator instanceof ComparisonLt) {
|
||||
if (rightIsExpr) {
|
||||
update = lowIsMinusInfinity ? eval : starting.glb(inf_highm1);
|
||||
}
|
||||
else {
|
||||
update = lowIsMinusInfinity ? null : starting.glb(lowp1_inf);
|
||||
}
|
||||
if (operator instanceof ComparisonNe) {
|
||||
eval = new PcodeInterval(eval.interval.complement());
|
||||
complement = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update == null) {
|
||||
if (id == null) {
|
||||
return environment;
|
||||
}
|
||||
else if (update.isBottom()) {
|
||||
return environment.bottom();
|
||||
|
||||
environment = updateImpliedConditions(environment, src, dest, val, complement);
|
||||
|
||||
return environment.putState(id, eval);
|
||||
}
|
||||
|
||||
protected ValueEnvironment<PcodeInterval> updateImpliedConditions(
|
||||
ValueEnvironment<PcodeInterval> environment,
|
||||
ProgramPoint src, ProgramPoint dest, PcodeInterval val, boolean complement) {
|
||||
if (val.target != null && val.bound != null) {
|
||||
Varnode tgt = val.rightIsExpr ? val.target.getInput(0) : val.target.getInput(1);
|
||||
Identifier vnId =
|
||||
new Variable(Untyped.INSTANCE, tgt.getAddress().toString(), dest.getLocation());
|
||||
PcodeInterval res = new PcodeInterval(complement ? val.intNeq : val.intEq);
|
||||
return environment.putState(vnId, res);
|
||||
}
|
||||
return environment.putState(id, update);
|
||||
return environment;
|
||||
}
|
||||
|
||||
protected PcodeInterval complement() {
|
||||
PcodeInterval comp = new PcodeInterval(this.interval.complement());
|
||||
return comp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -538,9 +600,10 @@ public class PcodeInterval
|
||||
if (rv != null) {
|
||||
BigInteger val = rv.getUnsignedValue();
|
||||
if (val != null) {
|
||||
return new PcodeInterval(val.longValue(), val.longValue());
|
||||
return new PcodeInterval(new LongInterval(val.longValue(), val.longValue()));
|
||||
}
|
||||
}
|
||||
return top();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/* ###
|
||||
* IP: MIT
|
||||
*/
|
||||
package ghidra.lisa.pcode.analyses;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||
import it.unive.lisa.program.cfg.ProgramPoint;
|
||||
import it.unive.lisa.symbolic.value.Identifier;
|
||||
import it.unive.lisa.symbolic.value.Variable;
|
||||
import it.unive.lisa.type.Untyped;
|
||||
import it.unive.lisa.util.numeric.MathNumber;
|
||||
|
||||
/**
|
||||
* This class represents a pretty-extensive hack to enable low-pcode interval logic. Low pcode differs from both source and high pcode
|
||||
* in that the calculations made to determine a branch are not co-located with the branch. The information needed for the "assume"
|
||||
* processing (i.e. variables values assumed as a result fo the branch) is therefore not local. The UpdateImpliedConditions is
|
||||
* essentially a table match against the default X86 implementation of flag-lookups representing a particular branch condition.
|
||||
*
|
||||
* JBE CF || ZF
|
||||
* JB CF (unhandled)
|
||||
* JL SF==OF
|
||||
* JLE SF==OF || ZF
|
||||
*
|
||||
*/
|
||||
public class PcodeIntervalLowX86 extends PcodeInterval {
|
||||
|
||||
private class KeyPattern {
|
||||
|
||||
// NB: At some point, we may want to convert this to a more generic type
|
||||
private String key;
|
||||
private PcodeInterval kinterval;
|
||||
|
||||
public KeyPattern() {
|
||||
this.key = "";
|
||||
}
|
||||
|
||||
public KeyPattern(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void append(KeyPattern kp) {
|
||||
this.key += kp.key;
|
||||
if (this.kinterval == null) {
|
||||
this.kinterval = kp.kinterval;
|
||||
}
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setInterval(PcodeInterval interval) {
|
||||
if (this.kinterval == null) {
|
||||
this.kinterval = interval;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueEnvironment<PcodeInterval> updateImpliedConditions(
|
||||
ValueEnvironment<PcodeInterval> environment,
|
||||
ProgramPoint src, ProgramPoint dest, PcodeInterval val, boolean complement) {
|
||||
KeyPattern key = buildKey(indexEnv(environment), val.target);
|
||||
LongInterval update = buildInterval(key, complement);
|
||||
if (update == null) {
|
||||
return environment;
|
||||
}
|
||||
PcodeOp tgt = key.kinterval.target;
|
||||
Varnode vn = key.kinterval.rightIsExpr ? tgt.getInput(0) : tgt.getInput(1);
|
||||
Identifier vnId =
|
||||
new Variable(Untyped.INSTANCE, vn.getAddress().toString(), dest.getLocation());
|
||||
PcodeInterval state = environment.getState(vnId);
|
||||
if (vn.isUnique()) {
|
||||
vnId = swapId(environment, dest, vnId, state);
|
||||
}
|
||||
PcodeInterval res = new PcodeInterval(update);
|
||||
res = intersection(res, state);
|
||||
return environment.putState(vnId, res);
|
||||
}
|
||||
|
||||
private Map<String, PcodeInterval> indexEnv(ValueEnvironment<PcodeInterval> environment) {
|
||||
Map<String, PcodeInterval> map = new HashMap<>();
|
||||
for (Entry<Identifier, PcodeInterval> entry : environment) {
|
||||
map.put(entry.getKey().getName(), entry.getValue());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private KeyPattern buildKey(Map<String, PcodeInterval> envMap, PcodeOp op) {
|
||||
|
||||
KeyPattern key = new KeyPattern();
|
||||
for (Varnode in : op.getInputs()) {
|
||||
String envKey = in.getAddress().toString();
|
||||
if (envMap.containsKey(envKey)) {
|
||||
PcodeInterval envVal = envMap.get(envKey);
|
||||
if (envVal.target != null) {
|
||||
key.append(buildKey(envMap, envVal.target));
|
||||
}
|
||||
else {
|
||||
Varnode out = op.getOutput();
|
||||
Address addr = out.getAddress();
|
||||
key = new KeyPattern(key.key + "(" + Long.toHexString(addr.getOffset()) + ")");
|
||||
if (isTerminal(op)) {
|
||||
key.setInterval(envMap.get(addr.toString()));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key.setKey(op.getMnemonic() + key.key);
|
||||
return key;
|
||||
}
|
||||
|
||||
private boolean isTerminal(PcodeOp op) {
|
||||
Varnode out = op.getOutput();
|
||||
if (!out.isRegister()) {
|
||||
return false;
|
||||
}
|
||||
Address addr = out.getAddress();
|
||||
if ((addr.getOffset() & 0x200) == 0) {
|
||||
return false;
|
||||
}
|
||||
return (op.getInput(0).isRegister() || op.getInput(1).isRegister());
|
||||
}
|
||||
|
||||
private String key2op(KeyPattern kp, boolean complement) {
|
||||
String ret = switch (kp.key) {
|
||||
case "BOOL_OR(206)INT_NOTEQUAL(20b)(207)", "BOOL_OR(206)INT_NOTEQUAL(207)(20b)" -> "JLE";
|
||||
case "BOOL_ANDBOOL_NEGATE(206)INT_EQUAL(20b)(207)", "BOOL_ANDBOOL_NEGATE(206)INT_EQUAL(207)(20b)" -> "JG";
|
||||
case "INT_NOTEQUAL(20b)(207)", "INT_NOTEQUAL(207)(20b)" -> "JL";
|
||||
case "INT_EQUAL(20b)(207)", "INT_EQUAL(207)(20b)" -> "JGE";
|
||||
case "BOOL_OR(200)(206)", "BOOL_OR(206)(200)" -> "JBE";
|
||||
case "BOOL_NEGATEBOOL_OR(200)(206)", "BOOL_NEGATEBOOL_OR(206)(200)" -> "JA";
|
||||
case "(200)" -> "JB";
|
||||
case "BOOL_NEGATE(200)" -> "JAE";
|
||||
default -> null;
|
||||
};
|
||||
if (ret == null) {
|
||||
return ret;
|
||||
}
|
||||
if (kp.kinterval != null && !kp.kinterval.rightIsExpr) {
|
||||
ret = ret.contains("A") ? ret.replace("A", "B") : ret.replace("B", "A");
|
||||
ret = ret.contains("G") ? ret.replace("G", "L") : ret.replace("L", "G");
|
||||
}
|
||||
if (complement) {
|
||||
ret = switch (ret) {
|
||||
case "JLE" -> "JG";
|
||||
case "JG" -> "JLE";
|
||||
case "JL" -> "JGE";
|
||||
case "JGE" -> "JL";
|
||||
case "JA" -> "JBE";
|
||||
case "JBE" -> "JA";
|
||||
case "JB" -> "JAE";
|
||||
case "JAE" -> "JB";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private LongInterval buildInterval(KeyPattern key, boolean complement) {
|
||||
String comparison = key2op(key, complement);
|
||||
if (key.kinterval == null) {
|
||||
//Msg.error(this, "Null interval for key: " + key.key);
|
||||
return null;
|
||||
}
|
||||
MathNumber bnd = key.kinterval.bound;
|
||||
return switch (comparison) {
|
||||
case "JG" -> new LongInterval(bnd.add(MathNumber.ONE), MathNumber.PLUS_INFINITY);
|
||||
case "JLE" -> new LongInterval(MathNumber.MINUS_INFINITY, bnd);
|
||||
case "JGE" -> new LongInterval(bnd, MathNumber.PLUS_INFINITY);
|
||||
case "JL" -> new LongInterval(MathNumber.MINUS_INFINITY,
|
||||
bnd.subtract(MathNumber.ONE));
|
||||
case "JA" -> new LongInterval(bnd.add(MathNumber.ONE), MathNumber.PLUS_INFINITY);
|
||||
case "JBE" -> new LongInterval(MathNumber.MINUS_INFINITY, bnd);
|
||||
case "JAE" -> new LongInterval(bnd, MathNumber.PLUS_INFINITY);
|
||||
case "JB" -> new LongInterval(MathNumber.MINUS_INFINITY,
|
||||
bnd.subtract(MathNumber.ONE));
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private Identifier swapId(ValueEnvironment<PcodeInterval> environment, ProgramPoint dest,
|
||||
Identifier vnId, PcodeInterval state) {
|
||||
for (Entry<Identifier, PcodeInterval> n : environment) {
|
||||
PcodeInterval v = n.getValue();
|
||||
if (v.interval.equals(state.interval) && !n.getKey().equals(vnId) &&
|
||||
n.getKey().toString().contains("register")) {
|
||||
vnId = new Variable(Untyped.INSTANCE, n.getKey().getName(),
|
||||
dest.getLocation());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return vnId;
|
||||
}
|
||||
|
||||
private PcodeInterval intersection(PcodeInterval a, PcodeInterval b) {
|
||||
if (a.interval.intersects(b.interval)) {
|
||||
MathNumber min = a.interval.getHigh().min(b.interval.getHigh());
|
||||
MathNumber max = a.interval.getLow().max(b.interval.getLow());
|
||||
return new PcodeInterval(min, max);
|
||||
}
|
||||
return bottom();
|
||||
}
|
||||
|
||||
}
|
||||
+24
-23
@@ -29,13 +29,13 @@ import it.unive.lisa.util.numeric.MathNumber;
|
||||
*/
|
||||
public class PcodeNonRedundantPowersetOfInterval
|
||||
extends
|
||||
NonRedundantPowersetOfBaseNonRelationalValueDomain<PcodeNonRedundantPowersetOfInterval, Interval> {
|
||||
NonRedundantPowersetOfBaseNonRelationalValueDomain<PcodeNonRedundantPowersetOfInterval, PcodeInterval> {
|
||||
|
||||
/**
|
||||
* Constructs an empty non redundant set of intervals.
|
||||
*/
|
||||
public PcodeNonRedundantPowersetOfInterval() {
|
||||
super(new TreeSet<>(), Interval.BOTTOM);
|
||||
super(new TreeSet<>(), PcodeInterval.BOTTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,8 +44,8 @@ public class PcodeNonRedundantPowersetOfInterval
|
||||
* @param elements the set of intervals
|
||||
*/
|
||||
public PcodeNonRedundantPowersetOfInterval(
|
||||
SortedSet<Interval> elements) {
|
||||
super(elements, Interval.BOTTOM);
|
||||
SortedSet<PcodeInterval> elements) {
|
||||
super(elements, PcodeInterval.BOTTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,19 +62,19 @@ public class PcodeNonRedundantPowersetOfInterval
|
||||
* </p>
|
||||
* s'<sub>1</sub> can be chosen randomly but in this case is chosen to be
|
||||
* the closest interval to s<sub>2</sub> (closest based on
|
||||
* {@link #middlePoint(Interval) middle point}).
|
||||
* {@link #middlePoint(PcodeInterval) middle point}).
|
||||
*/
|
||||
@Override
|
||||
protected PcodeNonRedundantPowersetOfInterval EgliMilnerConnector(
|
||||
PcodeNonRedundantPowersetOfInterval other)
|
||||
throws SemanticException {
|
||||
SortedSet<Interval> newElementsSet = new TreeSet<>();
|
||||
SortedSet<Interval> notCoverSet = new TreeSet<>();
|
||||
SortedSet<PcodeInterval> newElementsSet = new TreeSet<>();
|
||||
SortedSet<PcodeInterval> notCoverSet = new TreeSet<>();
|
||||
|
||||
// first side of the union
|
||||
for (Interval s2 : other.elementsSet) {
|
||||
for (PcodeInterval s2 : other.elementsSet) {
|
||||
boolean existsLower = false;
|
||||
for (Interval s1 : elementsSet) {
|
||||
for (PcodeInterval s1 : elementsSet) {
|
||||
if (s1.lessOrEqual(s2)) {
|
||||
existsLower = true;
|
||||
break;
|
||||
@@ -89,12 +89,12 @@ public class PcodeNonRedundantPowersetOfInterval
|
||||
}
|
||||
|
||||
// second side of the union
|
||||
for (Interval s2 : notCoverSet) {
|
||||
for (PcodeInterval s2 : notCoverSet) {
|
||||
MathNumber middlePoint = middlePoint(s2);
|
||||
MathNumber closestValue = middlePoint;
|
||||
MathNumber closestDiff = closestValue.subtract(middlePoint).abs();
|
||||
Interval closest = Interval.TOP;
|
||||
for (Interval s1 : elementsSet) {
|
||||
PcodeInterval closest = PcodeInterval.TOP;
|
||||
for (PcodeInterval s1 : elementsSet) {
|
||||
if (closestValue.compareTo(middlePoint) == 0) {
|
||||
closest = s1;
|
||||
closestValue = middlePoint(s1);
|
||||
@@ -127,7 +127,7 @@ public class PcodeNonRedundantPowersetOfInterval
|
||||
* @return the middle point of the interval
|
||||
*/
|
||||
protected MathNumber middlePoint(
|
||||
Interval interval) {
|
||||
PcodeInterval interval) {
|
||||
if (interval.interval.isFinite()) {
|
||||
return interval.interval.getLow()
|
||||
.add(interval.interval.getHigh())
|
||||
@@ -175,18 +175,19 @@ public class PcodeNonRedundantPowersetOfInterval
|
||||
return environment.bottom();
|
||||
}
|
||||
|
||||
SortedSet<Interval> newSet = new TreeSet<>();
|
||||
SortedSet<PcodeInterval> newSet = new TreeSet<>();
|
||||
|
||||
for (Interval startingInterval : starting.elementsSet)
|
||||
for (Interval interval : eval.elementsSet) {
|
||||
for (PcodeInterval startingInterval : starting.elementsSet)
|
||||
for (PcodeInterval interval : eval.elementsSet) {
|
||||
boolean lowIsMinusInfinity = interval.interval.lowIsMinusInfinity();
|
||||
Interval lowInf =
|
||||
new Interval(interval.interval.getLow(), MathNumber.PLUS_INFINITY);
|
||||
Interval lowp1Inf = new Interval(interval.interval.getLow().add(MathNumber.ONE),
|
||||
PcodeInterval lowInf =
|
||||
new PcodeInterval(interval.interval.getLow(), MathNumber.PLUS_INFINITY);
|
||||
PcodeInterval lowp1Inf =
|
||||
new PcodeInterval(interval.interval.getLow().add(MathNumber.ONE),
|
||||
MathNumber.PLUS_INFINITY);
|
||||
Interval infHigh =
|
||||
new Interval(MathNumber.MINUS_INFINITY, interval.interval.getHigh());
|
||||
Interval infHighm1 = new Interval(MathNumber.MINUS_INFINITY,
|
||||
PcodeInterval infHigh =
|
||||
new PcodeInterval(MathNumber.MINUS_INFINITY, interval.interval.getHigh());
|
||||
PcodeInterval infHighm1 = new PcodeInterval(MathNumber.MINUS_INFINITY,
|
||||
interval.interval.getHigh().subtract(MathNumber.ONE));
|
||||
|
||||
if (!(operator instanceof PcodeBinaryOperator)) {
|
||||
@@ -234,7 +235,7 @@ public class PcodeNonRedundantPowersetOfInterval
|
||||
|
||||
@Override
|
||||
protected PcodeNonRedundantPowersetOfInterval mk(
|
||||
SortedSet<Interval> elements) {
|
||||
SortedSet<PcodeInterval> elements) {
|
||||
return new PcodeNonRedundantPowersetOfInterval(elements);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,12 +45,12 @@ public class PcodePentagon implements ValueDomain<PcodePentagon>, BaseLattice<Pc
|
||||
/**
|
||||
* The interval environment.
|
||||
*/
|
||||
private final ValueEnvironment<PcodeInterval> intervals;
|
||||
protected ValueEnvironment<PcodeInterval> intervals;
|
||||
|
||||
/**
|
||||
* The upper bounds environment.
|
||||
*/
|
||||
private final ValueEnvironment<PcodeUpperBounds> upperBounds;
|
||||
protected ValueEnvironment<PcodeUpperBounds> upperBounds;
|
||||
|
||||
/**
|
||||
* Builds the PcodePentagons.
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/* ###
|
||||
* IP: MIT
|
||||
*/
|
||||
package ghidra.lisa.pcode.analyses;
|
||||
|
||||
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||
import it.unive.lisa.analysis.value.ValueDomain;
|
||||
|
||||
/**
|
||||
* /** The pentagons abstract domain, a weakly relational numeric abstract
|
||||
* domain. This abstract domain captures properties of the form of x \in [a, b]
|
||||
* ∧ x < y. It is more precise than the well known interval domain, but
|
||||
* it is less precise than the octagon domain. It is implemented as a
|
||||
* {@link ValueDomain}.
|
||||
*
|
||||
* <p>
|
||||
* Modified to handle pcode from original source written by:
|
||||
* <p>
|
||||
* @author <a href="mailto:luca.negrini@unive.it">Luca Negrini</a>
|
||||
* @author <a href="mailto:vincenzo.arceri@unipr.it">Vincenzo Arceri</a>
|
||||
*
|
||||
* <p>
|
||||
* @see <a href=
|
||||
* "https://www.sciencedirect.com/science/article/pii/S0167642309000719?ref=cra_js_challenge&fr=RR-1">Pentagons:
|
||||
* A weakly relational abstract domain for the efficient validation of
|
||||
* array accesses</a>
|
||||
*/
|
||||
public class PcodePentagonLowX86 extends PcodePentagon {
|
||||
|
||||
/**
|
||||
* Builds the PcodePentagons.
|
||||
*/
|
||||
public PcodePentagonLowX86() {
|
||||
this.intervals = new ValueEnvironment<>(new PcodeIntervalLowX86()).top();
|
||||
this.upperBounds = new ValueEnvironment<>(new PcodeUpperBounds(true)).top();
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -21,11 +21,11 @@ public class ConditionContext extends PcodeContext {
|
||||
|
||||
public ConditionContext(PcodeOp op) {
|
||||
super(op);
|
||||
assert (op.getOpcode() == PcodeOp.CBRANCH);
|
||||
}
|
||||
|
||||
public VarnodeContext expression() {
|
||||
// opcode should be CBRANCH
|
||||
assert (op.getInputs().length < 2);
|
||||
assert (op.getInputs().length <= 2);
|
||||
return new VarnodeContext(op.getInput(1));
|
||||
}
|
||||
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.contexts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HighInstructionContext extends InstructionContext {
|
||||
|
||||
public HighInstructionContext(HighStatementContext ctx) {
|
||||
ops = new ArrayList<>();
|
||||
ops.add(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HighInstructionContext next() {
|
||||
return null; // UNUSED
|
||||
}
|
||||
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.contexts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
public class HighStatementContext extends StatementContext {
|
||||
|
||||
// High pcode-only
|
||||
private HighFunction hfunc;
|
||||
private HighStatementContext prev;
|
||||
private HighStatementContext succ;
|
||||
private List<StatementContext> branches = new ArrayList<>();
|
||||
|
||||
|
||||
public HighStatementContext(HighFunction hfunc, PcodeOp op) {
|
||||
super(op);
|
||||
this.hfunc = hfunc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return op.getSeqnum() + ": " + op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressFactory getAddressFactory() {
|
||||
return hfunc.getAddressFactory();
|
||||
}
|
||||
|
||||
public HighStatementContext getPrev() {
|
||||
return prev;
|
||||
}
|
||||
|
||||
public void setPrev(HighStatementContext ctx) {
|
||||
this.prev = ctx;
|
||||
}
|
||||
|
||||
public HighStatementContext getNext() {
|
||||
return succ;
|
||||
}
|
||||
|
||||
public void setNext(HighStatementContext ctx) {
|
||||
this.succ = ctx;
|
||||
}
|
||||
|
||||
public List<StatementContext> getBranches() {
|
||||
return branches;
|
||||
}
|
||||
|
||||
public void addBranch(HighStatementContext ctx) {
|
||||
branches.add(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.contexts;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.lisa.pcode.PcodeFrontend;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Listing;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import it.unive.lisa.program.Program;
|
||||
|
||||
public class HighUnitContext extends UnitContext {
|
||||
|
||||
// High pcode-only
|
||||
private HighFunction hfunc;
|
||||
Map<SequenceNumber, HighStatementContext> map = new HashMap<>();
|
||||
|
||||
public HighUnitContext(PcodeFrontend frontend, Program program, Function f, HighFunction hfunc, Address entry) {
|
||||
super(frontend, program, f, entry);
|
||||
this.hfunc = hfunc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstructionContext entry() {
|
||||
return initEdges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StatementContext> branch(StatementContext ctx, Listing listing) {
|
||||
if (ctx instanceof HighStatementContext hctx) {
|
||||
return hctx.getBranches();
|
||||
}
|
||||
return new ArrayList<StatementContext>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HighStatementContext next(StatementContext ctx, Listing listing) {
|
||||
if (ctx instanceof HighStatementContext hctx) {
|
||||
return hctx.getNext();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setBranches(PcodeBlockBasic bb) {
|
||||
PcodeOp lastOp = bb.getLastOp();
|
||||
HighStatementContext ctx = map.get(lastOp.getSeqnum());
|
||||
for (int i = 0; i < bb.getOutSize(); i++) {
|
||||
if (bb.getOut(i) instanceof PcodeBlockBasic basic) {
|
||||
PcodeOp op = basic.getFirstOp();
|
||||
if (lastOp.getOpcode() == PcodeOp.CBRANCH) {
|
||||
if (basic.equals(bb.getFalseOut())) {
|
||||
ctx.setNext(map.get(op.getSeqnum()));
|
||||
}
|
||||
else {
|
||||
ctx.addBranch(map.get(op.getSeqnum()));
|
||||
}
|
||||
}
|
||||
else if (lastOp.getOpcode() == PcodeOp.BRANCH ||
|
||||
lastOp.getOpcode() == PcodeOp.BRANCHIND) {
|
||||
ctx.addBranch(map.get(op.getSeqnum()));
|
||||
}
|
||||
else {
|
||||
ctx.setNext(map.get(op.getSeqnum()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InstructionContext initEdges() {
|
||||
Iterator<PcodeOpAST> pcodeOps = hfunc.getPcodeOps();
|
||||
while (pcodeOps.hasNext()) {
|
||||
PcodeOpAST next = pcodeOps.next();
|
||||
HighStatementContext ctx = new HighStatementContext(hfunc, next);
|
||||
map.put(next.getSeqnum(), ctx);
|
||||
}
|
||||
ArrayList<PcodeBlockBasic> basicBlocks = hfunc.getBasicBlocks();
|
||||
HighStatementContext first = null;
|
||||
for (PcodeBlockBasic b : basicBlocks) {
|
||||
Iterator<PcodeOp> iterator = b.getIterator();
|
||||
PcodeOp prev = null;
|
||||
while (iterator.hasNext()) {
|
||||
PcodeOp next = iterator.next();
|
||||
HighStatementContext n = map.get(next.getSeqnum());
|
||||
if (prev != null) {
|
||||
HighStatementContext p = map.get(prev.getSeqnum());
|
||||
if (p != null && n != null) {
|
||||
p.setNext(n);
|
||||
n.setPrev(p);
|
||||
}
|
||||
}
|
||||
else if (first == null) {
|
||||
first = n;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
for (PcodeBlockBasic b : basicBlocks) {
|
||||
setBranches(b);
|
||||
}
|
||||
return new HighInstructionContext(first);
|
||||
}
|
||||
|
||||
}
|
||||
+11
-8
@@ -25,10 +25,10 @@ import it.unive.lisa.program.cfg.CodeLocation;
|
||||
|
||||
public class InstructionContext {
|
||||
|
||||
private Function function;
|
||||
private Instruction inst;
|
||||
private List<StatementContext> ops;
|
||||
private InstLocation loc;
|
||||
protected Function function;
|
||||
protected Instruction inst;
|
||||
protected List<StatementContext> ops;
|
||||
protected InstLocation loc;
|
||||
|
||||
public InstructionContext(Function function, Instruction inst) {
|
||||
this.function = function;
|
||||
@@ -41,6 +41,10 @@ public class InstructionContext {
|
||||
loc = new InstLocation(function, inst.getAddress());
|
||||
}
|
||||
|
||||
public InstructionContext() {
|
||||
// For HighInstructionContext
|
||||
}
|
||||
|
||||
public Collection<StatementContext> getPcodeOps() {
|
||||
return ops;
|
||||
}
|
||||
@@ -49,11 +53,10 @@ public class InstructionContext {
|
||||
return ops.get(i);
|
||||
}
|
||||
|
||||
public Instruction getInstruction() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
public InstructionContext next() {
|
||||
if (inst == null) {
|
||||
return null;
|
||||
}
|
||||
Listing listing = inst.getProgram().getListing();
|
||||
Address nextAddress = inst.getAddress().add(inst.getLength());
|
||||
Instruction next = listing.getInstructionAt(nextAddress);
|
||||
|
||||
@@ -24,8 +24,11 @@ public class MemLocContext extends VarnodeContext {
|
||||
|
||||
public MemLocContext(StatementContext ctx) {
|
||||
super(ctx.getOp().getInput(1));
|
||||
AddressFactory addressFactory = ctx.inst.getProgram().getAddressFactory();
|
||||
space = addressFactory.getAddressSpace((int) ctx.getOp().getInput(0).getOffset());
|
||||
AddressFactory addressFactory = ctx.getAddressFactory();
|
||||
space = addressFactory.getAddressSpace((int)ctx.getOp().getInput(0).getOffset());
|
||||
if (space == null) {
|
||||
space = addressFactory.getDefaultAddressSpace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,10 +40,7 @@ public class PcodeContext {
|
||||
}
|
||||
|
||||
public VarnodeContext basicExpr() {
|
||||
if (op.getNumInputs() == 1) {
|
||||
return new VarnodeContext(op.getInput(0));
|
||||
}
|
||||
return null;
|
||||
return new VarnodeContext(op.getInput(0));
|
||||
}
|
||||
|
||||
public int opcode() {
|
||||
|
||||
+14
-71
@@ -15,29 +15,22 @@
|
||||
*/
|
||||
package ghidra.lisa.pcode.contexts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.Listing;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
||||
public class StatementContext extends PcodeContext {
|
||||
|
||||
public StatementContext otherwise;
|
||||
public StatementContext then;
|
||||
|
||||
private int opcode;
|
||||
public Instruction inst;
|
||||
protected int opcode;
|
||||
protected Instruction inst;
|
||||
public VarDefContext left;
|
||||
public PcodeContext right;
|
||||
|
||||
public StatementContext(Instruction inst, PcodeOp op) {
|
||||
|
||||
protected StatementContext(PcodeOp op) {
|
||||
super(op);
|
||||
this.inst = inst;
|
||||
if (op != null) {
|
||||
this.opcode = op.getOpcode();
|
||||
if (op.getOutput() != null) {
|
||||
@@ -50,6 +43,11 @@ public class StatementContext extends PcodeContext {
|
||||
}
|
||||
}
|
||||
|
||||
public StatementContext(Instruction inst, PcodeOp op) {
|
||||
this(op);
|
||||
this.inst = inst;
|
||||
}
|
||||
|
||||
public VarDefContext target() {
|
||||
return left;
|
||||
}
|
||||
@@ -74,68 +72,13 @@ public class StatementContext extends PcodeContext {
|
||||
return opcode == PcodeOp.CBRANCH;
|
||||
}
|
||||
|
||||
public List<StatementContext> branch(Listing listing, UnitContext currentUnit) {
|
||||
List<StatementContext> list = new ArrayList<>();
|
||||
if (opcode == PcodeOp.BRANCH || opcode == PcodeOp.CBRANCH) {
|
||||
Varnode vn = op.getInput(0);
|
||||
if (vn.getAddress().isConstantAddress()) {
|
||||
int order = op.getSeqnum().getTime();
|
||||
order += vn.getOffset();
|
||||
list.add(new StatementContext(inst, inst.getPcode()[order]));
|
||||
}
|
||||
else {
|
||||
Instruction next =
|
||||
listing.getInstructionAt(vn.getAddress().getNewAddress(vn.getOffset()));
|
||||
if (next == null || next.getPcode().length == 0) {
|
||||
return list;
|
||||
}
|
||||
list.add(new StatementContext(next, next.getPcode()[0]));
|
||||
}
|
||||
}
|
||||
if (opcode == PcodeOp.BRANCHIND) {
|
||||
ReferenceManager referenceManager =
|
||||
currentUnit.function().getProgram().getReferenceManager();
|
||||
Reference[] refs = referenceManager.getReferencesFrom(inst.getAddress());
|
||||
for (Reference ref : refs) {
|
||||
Address fromAddress = ref.getToAddress();
|
||||
Instruction next = listing.getInstructionAt(fromAddress);
|
||||
if (next == null || next.getPcode().length == 0) {
|
||||
return list;
|
||||
}
|
||||
list.add(new StatementContext(next, next.getPcode()[0]));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public StatementContext next(Listing listing) {
|
||||
PcodeOp[] pcode = inst.getPcode();
|
||||
if (op != null) {
|
||||
int order = op.getSeqnum().getTime();
|
||||
if (order + 1 < pcode.length) {
|
||||
return new StatementContext(inst, inst.getPcode()[order + 1]);
|
||||
}
|
||||
}
|
||||
Instruction next = listing.getInstructionAt(inst.getAddress().add(inst.getLength()));
|
||||
while (next != null && next.getPcode().length == 0) {
|
||||
next = listing.getInstructionAt(next.getAddress().add(next.getLength()));
|
||||
}
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
return new StatementContext(next, next.getPcode()[0]);
|
||||
}
|
||||
|
||||
public PcodeContext ret() {
|
||||
if (opcode == PcodeOp.RETURN) {
|
||||
return new PcodeContext(op);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return inst.getAddress() + ": " + inst + ":" + op;
|
||||
}
|
||||
|
||||
public AddressFactory getAddressFactory() {
|
||||
return inst.getProgram().getAddressFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.contexts;
|
||||
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import it.unive.lisa.program.cfg.CodeLocation;
|
||||
|
||||
public class TernaryExprContext {
|
||||
|
||||
public PcodeOp op;
|
||||
public VarnodeContext left;
|
||||
public VarnodeContext middle;
|
||||
public VarnodeContext right;
|
||||
|
||||
public TernaryExprContext(PcodeContext ctx) {
|
||||
this.op = ctx.op;
|
||||
left = new VarnodeContext(op.getInput(0));
|
||||
middle = new VarnodeContext(op.getInput(1));
|
||||
right = new VarnodeContext(op.getInput(2));
|
||||
}
|
||||
|
||||
public int opcode() {
|
||||
return op.getOpcode();
|
||||
}
|
||||
|
||||
|
||||
public CodeLocation location() {
|
||||
return new PcodeLocation(op);
|
||||
}
|
||||
|
||||
public String mnemonic() {
|
||||
return op.getMnemonic();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,10 +15,17 @@
|
||||
*/
|
||||
package ghidra.lisa.pcode.contexts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.lisa.pcode.PcodeFrontend;
|
||||
import ghidra.lisa.pcode.locations.InstLocation;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import it.unive.lisa.program.CodeUnit;
|
||||
import it.unive.lisa.program.Program;
|
||||
import it.unive.lisa.program.SyntheticLocation;
|
||||
@@ -26,18 +33,11 @@ import it.unive.lisa.program.cfg.CodeLocation;
|
||||
|
||||
public class UnitContext {
|
||||
|
||||
private PcodeFrontend frontend;
|
||||
private Function function;
|
||||
private CodeUnit unit;
|
||||
private Address start;
|
||||
|
||||
public UnitContext(PcodeFrontend frontend, Program program, Function f) {
|
||||
this.frontend = frontend;
|
||||
this.function = f;
|
||||
unit = new CodeUnit(SyntheticLocation.INSTANCE, program, f.getName());
|
||||
start = function.getEntryPoint();
|
||||
}
|
||||
|
||||
protected PcodeFrontend frontend;
|
||||
protected Function function;
|
||||
protected CodeUnit unit;
|
||||
protected Address start;
|
||||
|
||||
public UnitContext(PcodeFrontend frontend, Program program, Function f, Address entry) {
|
||||
this.frontend = frontend;
|
||||
this.function = f;
|
||||
@@ -82,6 +82,56 @@ public class UnitContext {
|
||||
return inst == null ? null : new InstructionContext(function, inst);
|
||||
}
|
||||
|
||||
public List<StatementContext> branch(StatementContext ctx, Listing listing) {
|
||||
List<StatementContext> list = new ArrayList<>();
|
||||
if (ctx.opcode == PcodeOp.BRANCH || ctx.opcode == PcodeOp.CBRANCH) {
|
||||
Varnode vn = ctx.op.getInput(0);
|
||||
if (vn.getAddress().isConstantAddress()) {
|
||||
int order = ctx.op.getSeqnum().getTime();
|
||||
order += vn.getOffset();
|
||||
list.add(new StatementContext(ctx.inst.getPcode()[order]));
|
||||
}
|
||||
else {
|
||||
Instruction next = listing.getInstructionAt(vn.getAddress().getNewAddress(vn.getOffset()));
|
||||
if (next == null || next.getPcode().length == 0) {
|
||||
return list;
|
||||
}
|
||||
list.add(new StatementContext(next, next.getPcode()[0]));
|
||||
}
|
||||
}
|
||||
if (ctx.opcode == PcodeOp.BRANCHIND) {
|
||||
ReferenceManager referenceManager = function().getProgram().getReferenceManager();
|
||||
Reference[] refs = referenceManager.getReferencesFrom(ctx.inst.getAddress());
|
||||
for (Reference ref : refs) {
|
||||
Address fromAddress = ref.getToAddress();
|
||||
Instruction next = listing.getInstructionAt(fromAddress);
|
||||
if (next == null || next.getPcode().length == 0) {
|
||||
return list;
|
||||
}
|
||||
list.add(new StatementContext(next, next.getPcode()[0]));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public StatementContext next(StatementContext ctx, Listing listing) {
|
||||
PcodeOp[] pcode = ctx.inst.getPcode();
|
||||
if (ctx.op != null) {
|
||||
int order = ctx.op.getSeqnum().getTime();
|
||||
if (order+1 < pcode.length) {
|
||||
return new StatementContext(ctx.inst, ctx.inst.getPcode()[order+1]);
|
||||
}
|
||||
}
|
||||
Instruction next = listing.getInstructionAt(ctx.inst.getAddress().add(ctx.inst.getLength()));
|
||||
while (next != null && next.getPcode().length == 0) {
|
||||
next = listing.getInstructionAt(next.getAddress().add(next.getLength()));
|
||||
}
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
return new StatementContext(next, next.getPcode()[0]);
|
||||
}
|
||||
|
||||
public boolean contains(Address target) {
|
||||
return function.getBody().contains(target);
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.contexts;
|
||||
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import it.unive.lisa.program.cfg.CodeLocation;
|
||||
|
||||
public class VarargsExprContext {
|
||||
|
||||
public PcodeOp op;
|
||||
public VarnodeContext[] varargs;
|
||||
|
||||
public VarargsExprContext(PcodeContext ctx) {
|
||||
this.op = ctx.op;
|
||||
varargs = new VarnodeContext[op.getNumInputs()];
|
||||
for (int i = 0; i < op.getNumInputs(); i++) {
|
||||
varargs[i] = new VarnodeContext(op.getInput(i));
|
||||
}
|
||||
}
|
||||
|
||||
public int opcode() {
|
||||
return op.getOpcode();
|
||||
}
|
||||
|
||||
|
||||
public CodeLocation location() {
|
||||
return new PcodeLocation(op);
|
||||
}
|
||||
|
||||
public String mnemonic() {
|
||||
return op.getMnemonic();
|
||||
}
|
||||
|
||||
}
|
||||
+8
-3
@@ -44,8 +44,9 @@ public class PcodeBinaryExpression extends it.unive.lisa.program.cfg.statement.B
|
||||
case PcodeOp.BOOL_OR -> LogicalOr.INSTANCE;
|
||||
case PcodeOp.INT_EQUAL, PcodeOp.FLOAT_EQUAL -> ComparisonEq.INSTANCE;
|
||||
case PcodeOp.INT_NOTEQUAL, PcodeOp.FLOAT_NOTEQUAL -> ComparisonNe.INSTANCE;
|
||||
case PcodeOp.INT_LESSEQUAL, PcodeOp.FLOAT_LESSEQUAL -> ComparisonLe.INSTANCE;
|
||||
case PcodeOp.INT_LESS, PcodeOp.FLOAT_LESS -> ComparisonLt.INSTANCE;
|
||||
case PcodeOp.INT_LESSEQUAL, PcodeOp.INT_SLESSEQUAL, PcodeOp.FLOAT_LESSEQUAL -> ComparisonLe.INSTANCE;
|
||||
// NB: Some chance we're going to get burned by including SBORROW here
|
||||
case PcodeOp.INT_LESS, PcodeOp.INT_SLESS, PcodeOp.INT_SBORROW, PcodeOp.FLOAT_LESS -> ComparisonLt.INSTANCE;
|
||||
default -> new PcodeBinaryOperator(ctx.op);
|
||||
};
|
||||
}
|
||||
@@ -70,8 +71,12 @@ public class PcodeBinaryExpression extends it.unive.lisa.program.cfg.statement.B
|
||||
getStaticType(),
|
||||
left,
|
||||
right,
|
||||
operator,
|
||||
getOperator(),
|
||||
getLocation()),
|
||||
this);
|
||||
}
|
||||
|
||||
public BinaryOperator getOperator() {
|
||||
return operator;
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -20,10 +20,8 @@ import java.util.Set;
|
||||
|
||||
import ghidra.lisa.pcode.contexts.CallContext;
|
||||
import ghidra.lisa.pcode.locations.PcodeLocation;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.ProgramContext;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import it.unive.lisa.analysis.*;
|
||||
import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment;
|
||||
@@ -77,8 +75,6 @@ public class PcodeCallExpression extends UnresolvedCall {
|
||||
if (unaffected.isEmpty()) {
|
||||
return entryState;
|
||||
}
|
||||
ProgramContext programContext = function.getProgram().getProgramContext();
|
||||
Address entryPoint = function.getEntryPoint();
|
||||
if (entryState.getState() instanceof SimpleAbstractState sas) {
|
||||
ValueDomain<?> vDomain = sas.getValueState();
|
||||
if (vDomain instanceof ValueEnvironment vEnv) {
|
||||
@@ -102,25 +98,6 @@ public class PcodeCallExpression extends UnresolvedCall {
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (Register r : programContext.getRegisters()) {
|
||||
// RegisterValue rv = programContext.getRegisterValue(r, entryPoint);
|
||||
// if (rv != null && rv.hasAnyValue()) {
|
||||
// Variable v = new Variable(Untyped.INSTANCE,
|
||||
// r.getAddress().toString(), new InstLocation(entryPoint));
|
||||
// if (vEnv.lattice instanceof PcodeNonRelationalValueDomain pcodeDomain) {
|
||||
// PcodeNonRelationalValueDomain<?> value = pcodeDomain.getValue(rv);
|
||||
// if (value != null) {
|
||||
// vEnv.function.put(v, value);
|
||||
// }
|
||||
// }
|
||||
// if (vEnv.lattice instanceof PcodeDataflowConstantPropagation pcodeDomain) {
|
||||
// PcodeDataflowConstantPropagation value = pcodeDomain.getValue(v, rv);
|
||||
// if (value != null) {
|
||||
// vEnv.function.put(v, value);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
return entryState;
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.expressions;
|
||||
|
||||
import ghidra.lisa.pcode.contexts.TernaryExprContext;
|
||||
import ghidra.lisa.pcode.statements.PcodeTernaryOperator;
|
||||
import it.unive.lisa.analysis.*;
|
||||
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||
import it.unive.lisa.program.cfg.CFG;
|
||||
import it.unive.lisa.program.cfg.statement.Expression;
|
||||
import it.unive.lisa.program.cfg.statement.Statement;
|
||||
import it.unive.lisa.symbolic.SymbolicExpression;
|
||||
import it.unive.lisa.symbolic.value.TernaryExpression;
|
||||
import it.unive.lisa.symbolic.value.operator.ternary.TernaryOperator;
|
||||
|
||||
public class PcodeTernaryExpression extends it.unive.lisa.program.cfg.statement.TernaryExpression {
|
||||
|
||||
private TernaryOperator operator;
|
||||
|
||||
public PcodeTernaryExpression(
|
||||
CFG cfg,
|
||||
TernaryExprContext ctx,
|
||||
Expression left,
|
||||
Expression middle,
|
||||
Expression right) {
|
||||
super(cfg, ctx.location(), ctx.mnemonic(), cfg.getDescriptor().getUnit().getProgram().getTypes().getIntegerType(), left, middle, right);
|
||||
|
||||
this.operator = switch (ctx.op.getOpcode()) {
|
||||
default -> new PcodeTernaryOperator(ctx.op);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int compareSameClassAndParams(
|
||||
Statement o) {
|
||||
return 0; // no extra fields to compare
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends AbstractState<A>> AnalysisState<A> fwdTernarySemantics(
|
||||
InterproceduralAnalysis<A> interprocedural,
|
||||
AnalysisState<A> state,
|
||||
SymbolicExpression left,
|
||||
SymbolicExpression middle,
|
||||
SymbolicExpression right,
|
||||
StatementStore<A> expressions)
|
||||
throws SemanticException {
|
||||
|
||||
return state.smallStepSemantics(
|
||||
new TernaryExpression(
|
||||
getStaticType(),
|
||||
left,
|
||||
middle,
|
||||
right,
|
||||
operator,
|
||||
getLocation()),
|
||||
this);
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.expressions;
|
||||
|
||||
import ghidra.lisa.pcode.contexts.VarargsExprContext;
|
||||
import it.unive.lisa.analysis.*;
|
||||
import it.unive.lisa.analysis.lattices.ExpressionSet;
|
||||
import it.unive.lisa.interprocedural.InterproceduralAnalysis;
|
||||
import it.unive.lisa.program.cfg.CFG;
|
||||
import it.unive.lisa.program.cfg.statement.Expression;
|
||||
import it.unive.lisa.program.cfg.statement.Statement;
|
||||
import it.unive.lisa.symbolic.SymbolicExpression;
|
||||
import it.unive.lisa.symbolic.value.BinaryExpression;
|
||||
import it.unive.lisa.symbolic.value.operator.binary.LogicalOr;
|
||||
|
||||
public class PcodeVarargsExpression extends it.unive.lisa.program.cfg.statement.NaryExpression {
|
||||
|
||||
public PcodeVarargsExpression(CFG cfg,
|
||||
VarargsExprContext ctx,
|
||||
Expression[] exps) {
|
||||
super(cfg, ctx.location(), ctx.mnemonic(), cfg.getDescriptor().getUnit().getProgram().getTypes().getIntegerType(), exps);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int compareSameClassAndParams(
|
||||
Statement o) {
|
||||
return 0; // no extra fields to compare
|
||||
}
|
||||
|
||||
public <A extends AbstractState<A>> AnalysisState<A> fwdBinarySemantics(
|
||||
InterproceduralAnalysis<A> interprocedural,
|
||||
AnalysisState<A> state,
|
||||
SymbolicExpression left,
|
||||
SymbolicExpression right,
|
||||
StatementStore<A> expressions)
|
||||
throws SemanticException {
|
||||
|
||||
return state.smallStepSemantics(
|
||||
new BinaryExpression(
|
||||
getStaticType(),
|
||||
left,
|
||||
right,
|
||||
LogicalOr.INSTANCE,
|
||||
getLocation()),
|
||||
this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A extends AbstractState<A>> AnalysisState<A> forwardSemanticsAux(
|
||||
InterproceduralAnalysis<A> interprocedural,
|
||||
AnalysisState<A> state,
|
||||
ExpressionSet[] params,
|
||||
StatementStore<A> expressions)
|
||||
throws SemanticException {
|
||||
AnalysisState<A> result = state.bottom();
|
||||
for (SymbolicExpression expBase : params[0]) {
|
||||
for (int i = 1; i < params.length; i++) {
|
||||
for (SymbolicExpression exp : params[i]) {
|
||||
//result = result.lub(state.smallStepSemantics(exp, this));
|
||||
result = result.lub(
|
||||
fwdBinarySemantics(interprocedural, state, expBase, exp, expressions));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/* ###
|
||||
* 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 ghidra.lisa.pcode.statements;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import it.unive.lisa.symbolic.value.operator.ternary.TernaryOperator;
|
||||
import it.unive.lisa.type.*;
|
||||
|
||||
public class PcodeTernaryOperator implements TernaryOperator {
|
||||
|
||||
private PcodeOp op;
|
||||
|
||||
public PcodeTernaryOperator(PcodeOp op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public PcodeOp getOp() {
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return op.getMnemonic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> typeInference(
|
||||
TypeSystem types,
|
||||
Set<Type> left,
|
||||
Set<Type> middle,
|
||||
Set<Type> right) {
|
||||
Set<Type> set = NumericType.commonNumericalType(left, right);
|
||||
if (!set.isEmpty())
|
||||
return set;
|
||||
return Collections.singleton(types.getBooleanType());
|
||||
}
|
||||
}
|
||||
@@ -91,17 +91,23 @@ public class AbstractLisaTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
@Rule
|
||||
public TestName name = new TestName();
|
||||
|
||||
private int pgmIndex;
|
||||
|
||||
public AbstractLisaTest() {
|
||||
init();
|
||||
init(0);
|
||||
}
|
||||
|
||||
public AbstractLisaTest(int n) {
|
||||
init(n);
|
||||
}
|
||||
|
||||
protected String getProgramName() {
|
||||
return "static-" + getClass().getCanonicalName() + "." + name.getMethodName();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
|
||||
public void init(int n) {
|
||||
try {
|
||||
pgmIndex = n;
|
||||
env = new TestEnv();
|
||||
tool = env.getTool();
|
||||
programManager = tool.getService(ProgramManager.class);
|
||||
@@ -207,6 +213,235 @@ public class AbstractLisaTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
decompilerPanel = decompilerProvider.getDecompilerPanel();
|
||||
}
|
||||
|
||||
private AssemblyBuffer getProgram(int index) throws Throwable {
|
||||
return switch (index) {
|
||||
case 7 -> getProgram_v7();
|
||||
case 6 -> getProgram_v6();
|
||||
case 5 -> getProgram_v5();
|
||||
case 4 -> getProgram_v4();
|
||||
case 3 -> getProgram_v3();
|
||||
case 2 -> getProgram_v2();
|
||||
case 1 -> getProgram_v1();
|
||||
default -> getProgram_v0();
|
||||
};
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v0() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("PUSH RBP");
|
||||
buf.assemble("MOV RBP, RSP");
|
||||
buf.assemble("MOV RAX, 0x4");
|
||||
buf.assemble("SUB RAX, 0x5");
|
||||
buf.assemble("MOV RDX, RAX");
|
||||
buf.assemble("RET");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v1() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RCX, 0x3");
|
||||
buf.assemble("MOV RDX, 0x9");
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JLE 0x%s".formatted(tgt0.add(5)));
|
||||
buf.assemble("RET 0x8");
|
||||
buf.assemble("SUB RAX, RCX");
|
||||
buf.assemble("RET 0x8");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v2() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RCX, 0x3");
|
||||
buf.assemble("MOV RDX, 0x9");
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JLE 0x%s".formatted(tgt0.add(5)));
|
||||
buf.assemble("RET 0x8");
|
||||
buf.assemble("SUB RAX, RCX");
|
||||
buf.assemble("CMP EAX, EDX");
|
||||
Address tgt1 = buf.getNext();
|
||||
buf.assemble("JG 0x%s".formatted(tgt1.add(5))); // RAX > 9
|
||||
buf.assemble("RET 0x8");
|
||||
buf.assemble("ADD RCX, 0x3");
|
||||
buf.assemble("SUB RAX, RCX");
|
||||
buf.assemble("RET 0x8");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v3() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RAX, RAX");
|
||||
buf.assemble("MOV RCX, 0x3");
|
||||
buf.assemble("MOV RDX, 0x9");
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JLE 0x%s".formatted(tgt0.add(11))); // RAX >= 3
|
||||
buf.assemble("SUB RAX, 0x5"); // RAX <= 3
|
||||
buf.assemble("MOV RDX, RAX"); // RAX <= -2
|
||||
Address tgt1 = buf.getNext();
|
||||
buf.assemble("JMP 0x%s".formatted(tgt1.add(10)));
|
||||
buf.assemble("CMP EAX, EDX"); // RAX >= 3
|
||||
Address tgt2 = buf.getNext();
|
||||
buf.assemble("JGE 0x%s".formatted(tgt2.add(10))); // RAX >= 9
|
||||
buf.assemble("ADD RCX, 0x5"); // 3 <= RAX < 9
|
||||
buf.assemble("ADD RAX, 0x1F"); // RAX >= 3
|
||||
buf.assemble("SAR RAX, 0x3"); // RAX >= -2
|
||||
buf.assemble("AND RAX, -4");
|
||||
buf.assemble("SUB RAX, 0x5");
|
||||
buf.assemble("SUB RAX, RCX");
|
||||
buf.assemble("RET 0x8");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v4() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RAX, RAX");
|
||||
buf.assemble("MOV RCX, 0x2");
|
||||
buf.assemble("MOV RDX, 0x8");
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JL 0x%s".formatted(tgt0.add(11))); // RAX > 4
|
||||
buf.assemble("SUB RAX, 0x5"); // RAX <= 3
|
||||
buf.assemble("MOV RDX, RAX"); // RAX <= -2
|
||||
Address tgt1 = buf.getNext();
|
||||
buf.assemble("JMP 0x%s".formatted(tgt1.add(10)));
|
||||
buf.assemble("CMP EAX, EDX"); // RAX >= 4
|
||||
Address tgt2 = buf.getNext();
|
||||
buf.assemble("JG 0x%s".formatted(tgt2.add(10))); // RAX > 8
|
||||
buf.assemble("ADD RCX, 0x5"); // 3 <= RAX <= 8
|
||||
buf.assemble("ADD RAX, 0x1F");
|
||||
buf.assemble("SAR RAX, 0x3");
|
||||
buf.assemble("AND RAX, -4");
|
||||
buf.assemble("SUB RAX, 0x5");
|
||||
buf.assemble("SUB RAX, RCX");
|
||||
buf.assemble("RET 0x8");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v5() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RAX, RAX");
|
||||
buf.assemble("MOV RCX, 0x3");
|
||||
buf.assemble("MOV RDX, 0x8");
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JBE 0x%s".formatted(tgt0.add(11))); // RAX >= 3
|
||||
buf.assemble("SUB RAX, 0x5"); // RAX < 3
|
||||
buf.assemble("MOV RDX, RAX"); // RAX < 8
|
||||
Address tgt1 = buf.getNext();
|
||||
buf.assemble("JMP 0x%s".formatted(tgt1.add(10)));
|
||||
buf.assemble("CMP EAX, EDX"); // RAX >= 3
|
||||
Address tgt2 = buf.getNext();
|
||||
buf.assemble("JA 0x%s".formatted(tgt2.add(10))); // RAX >= 9
|
||||
buf.assemble("ADD RCX, 0x5"); // 3 <= RAX < 9
|
||||
buf.assemble("ADD RAX, 0x1F"); // RAX >= 3
|
||||
buf.assemble("SAR RAX, 0x3"); // RAX >= -2
|
||||
buf.assemble("AND RAX, -4");
|
||||
buf.assemble("SUB RAX, 0x5");
|
||||
buf.assemble("SUB RAX, RCX");
|
||||
buf.assemble("RET 0x8");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v6() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RAX, RAX");
|
||||
buf.assemble("MOV RCX, 0x3");
|
||||
buf.assemble("MOV RDX, 0x8");
|
||||
buf.assemble("MOV RBX, 0x6");
|
||||
buf.assemble("CMP EAX, ECX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JLE 0x%s".formatted(tgt0.add(32)));
|
||||
Address tgt1 = buf.getNext();
|
||||
buf.assemble("CMP RAX, RDX"); // RAX >= 4
|
||||
buf.assemble("JG 0x%s".formatted(tgt1.add(27)));
|
||||
buf.assemble("CMP AX, BX"); // 8 >= RAX >= 4
|
||||
Address tgt2 = buf.getNext();
|
||||
buf.assemble("JGE 0x%s".formatted(tgt2.add(16)));
|
||||
buf.assemble("ADD RCX, 0x1"); // 5 >= RAX >= 4
|
||||
buf.assemble("CMP EAX, ECX");
|
||||
Address tgt3 = buf.getNext();
|
||||
buf.assemble("JA 0x%s".formatted(tgt3.add(5)));
|
||||
buf.assemble("RET 0x8"); // RAX == 4
|
||||
buf.assemble("RET 0x8"); // RAX == 5
|
||||
buf.assemble("RET 0x8"); // 8 >= RAX >= 6
|
||||
buf.assemble("RET 0x8"); // RAX >= 9
|
||||
buf.assemble("SUB RCX, 0x3"); // 3 >= RAX
|
||||
buf.assemble("CMP EAX, ECX");
|
||||
Address tgt4 = buf.getNext();
|
||||
buf.assemble("JL 0x%s".formatted(tgt4.add(5)));
|
||||
buf.assemble("RET 0x8"); // 3 >= RAX >= 1
|
||||
buf.assemble("CMP AX, BX"); // -1 >= RAX
|
||||
Address tgt5 = buf.getNext();
|
||||
buf.assemble("JBE 0x%s".formatted(tgt5.add(5)));
|
||||
buf.assemble("RET 0x8"); // bottom
|
||||
buf.assemble("RET 0x8"); // -1 >= RAX
|
||||
buf.assemble("RET 0x8"); //
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected AssemblyBuffer getProgram_v7() throws Throwable {
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
Address entry = addr(program, 0x00400000);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("MOV RAX, RAX");
|
||||
buf.assemble("MOV RCX, -0x3");
|
||||
buf.assemble("MOV RDX, -0x8");
|
||||
buf.assemble("MOV RBX, -0x6");
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt0 = buf.getNext();
|
||||
buf.assemble("JLE 0x%s".formatted(tgt0.add(32)));
|
||||
Address tgt1 = buf.getNext();
|
||||
buf.assemble("CMP RDX, RAX"); // RAX >= 4
|
||||
buf.assemble("JG 0x%s".formatted(tgt1.add(27)));
|
||||
buf.assemble("CMP BX, AX"); // 8 >= RAX >= 4
|
||||
Address tgt2 = buf.getNext();
|
||||
buf.assemble("JGE 0x%s".formatted(tgt2.add(16)));
|
||||
buf.assemble("SUB RCX, 0x1"); // 5 >= RAX >= 4
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt3 = buf.getNext();
|
||||
buf.assemble("JA 0x%s".formatted(tgt3.add(5)));
|
||||
buf.assemble("RET 0x8"); // RAX == 4
|
||||
buf.assemble("RET 0x8"); // RAX == 5
|
||||
buf.assemble("RET 0x8"); // 8 >= RAX >= 6
|
||||
buf.assemble("RET 0x8"); // RAX >= 9
|
||||
buf.assemble("ADD RCX, 0x3"); // 3 >= RAX
|
||||
buf.assemble("CMP ECX, EAX");
|
||||
Address tgt4 = buf.getNext();
|
||||
buf.assemble("JL 0x%s".formatted(tgt4.add(5)));
|
||||
buf.assemble("RET 0x8"); // 3 >= RAX >= 1
|
||||
buf.assemble("CMP BX, AX"); // -1 >= RAX
|
||||
Address tgt5 = buf.getNext();
|
||||
buf.assemble("JBE 0x%s".formatted(tgt5.add(5)));
|
||||
buf.assemble("RET 0x8"); // bottom
|
||||
buf.assemble("RET 0x8"); // -1 >= RAX
|
||||
buf.assemble("RET 0x8"); //
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected Function createSimpleProgramX86_64() throws Throwable {
|
||||
createProgram("x86:LE:64:default", "gcc");
|
||||
intoProject(program);
|
||||
@@ -224,15 +459,7 @@ public class AbstractLisaTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
program.getMemory()
|
||||
.createInitializedBlock(".text", entry, 0x1000, (byte) 0, monitor, false);
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(program.getLanguage(), NO_16BIT_CALLS);
|
||||
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||
|
||||
buf.assemble("PUSH RBP");
|
||||
buf.assemble("MOV RBP, RSP");
|
||||
buf.assemble("MOV RAX, 0x4");
|
||||
buf.assemble("SUB AX, 0x5");
|
||||
buf.assemble("MOV RDX, RAX");
|
||||
buf.assemble("RET");
|
||||
AssemblyBuffer buf = getProgram(pgmIndex);
|
||||
Address end = buf.getNext();
|
||||
|
||||
program.getMemory().setBytes(entry, buf.getBytes());
|
||||
|
||||
@@ -29,8 +29,8 @@ public class NumericAnalysesTest extends AbstractLisaTest {
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_CONSTPROP);
|
||||
runTest();
|
||||
equalsAssert(valueOf("0040000b:0:register:00000000"), "4"); //SUB AX, 0x5
|
||||
equalsAssert(valueOf("0040000f:0:register:00000000"), "ffff"); //MOV RDX, RAX
|
||||
equalsAssert(valueOf("00400012:0:register:00000010"), "ffff"); //RET
|
||||
equalsAssert(valueOf("0040000f:0:register:00000000"), "-1"); //MOV RDX, RAX
|
||||
equalsAssert(valueOf("00400012:0:register:00000010"), "-1"); //RET
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@@ -67,7 +67,6 @@ public class NumericAnalysesTest extends AbstractLisaTest {
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testPentagons() {
|
||||
init();
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_PENTAGON);
|
||||
runTest();
|
||||
equalsAssert(valueOf("0040000b:0:register:00000000"), "[4, 4]"); //SUB AX, 0x5
|
||||
@@ -94,4 +93,149 @@ public class NumericAnalysesTest extends AbstractLisaTest {
|
||||
equalsAssert(valueOf("0040000f:0:register:00000000"), "{}"); //MOV RDX, RAX
|
||||
equalsAssert(valueOf("00400012:0:register:00000010"), "{}"); //RET
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalHighPcodePreState() {
|
||||
init(1);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL);
|
||||
lisaOptions.setPostState(false);
|
||||
lisaOptions.setHighPcode(true);
|
||||
runTest();
|
||||
equalsAssert(valueOf("0040000e:37:register:00000000"), null);
|
||||
equalsAssert(valueOf("00400010:13:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("00400018:47:register:00000000"), "[0, +Inf]"); // [3, +Inf] optimized out
|
||||
equalsAssert(valueOf("00400012:46:register:00000000"), "[-Inf, 2]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalHighPcodePostState() {
|
||||
init(2);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(true);
|
||||
runTest();
|
||||
equalsAssert(valueOf("0040000e:76:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("00400012:18:register:00000000"), "[-Inf, 2]");
|
||||
equalsAssert(valueOf("00400018:78:register:00000000"), "[0, +Inf]"); // [3, +Inf] optimized out
|
||||
equalsAssert(valueOf("0040001c:45:register:00000000"), "[0, 9]");
|
||||
equalsAssert(valueOf("00400026:67:register:00000000"), "[1, +Inf]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalHighPcodePhi() {
|
||||
init(3);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(true);
|
||||
runTest();
|
||||
equalsAssert(valueOf("0040000a:2:register:00000000"), null);
|
||||
equalsAssert(valueOf("00400011:142:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("0040001c:26:register:00000000"), "[-Inf, -3]");
|
||||
equalsAssert(valueOf("00400011:142:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("00400020:111:register:00000000"), "[3, +Inf]");
|
||||
equalsAssert(valueOf("00400022:115:register:00000000"), "[3, 8]");
|
||||
equalsAssert(valueOf("00400026:29:register:00000000"), "[-Inf, 39]");
|
||||
equalsAssert(valueOf("0040002a:38:register:00000000"), "[-Inf, +Inf]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalLowPcodePhiJLE() {
|
||||
init(3);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL_LX86);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(false);
|
||||
runTest();
|
||||
equalsAssert(valueOf("00400011:0:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("0040001c:0:register:00000000"), "[-Inf, -3]");
|
||||
equalsAssert(valueOf("0040001e:0:register:00000000"), "[3, +Inf]");
|
||||
equalsAssert(valueOf("00400022:0:register:00000000"), "[3, 8]");
|
||||
equalsAssert(valueOf("00400026:0:register:00000000"), "[-Inf, 8]");
|
||||
equalsAssert(valueOf("00400026:3:register:00000000"), "[-Inf, 39]");
|
||||
equalsAssert(valueOf("0040002a:0:register:00000000"), "[-Inf, +Inf]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalLowPcodePhiJL() {
|
||||
init(4);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL_LX86);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(false);
|
||||
runTest();
|
||||
equalsAssert(valueOf("00400011:0:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("0040001c:0:register:00000000"), "[-Inf, -3]");
|
||||
equalsAssert(valueOf("0040001e:0:register:00000000"), "[3, +Inf]");
|
||||
equalsAssert(valueOf("00400022:0:register:00000000"), "[3, 8]");
|
||||
equalsAssert(valueOf("00400026:0:register:00000000"), "[-Inf, 8]");
|
||||
equalsAssert(valueOf("00400026:3:register:00000000"), "[-Inf, 39]");
|
||||
equalsAssert(valueOf("0040002a:0:register:00000000"), "[-Inf, +Inf]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalLowPcodePhiJBE() {
|
||||
init(5);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL_LX86);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(false);
|
||||
runTest();
|
||||
equalsAssert(valueOf("00400011:0:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("0040001c:0:register:00000000"), "[-Inf, -3]");
|
||||
equalsAssert(valueOf("0040001e:0:register:00000000"), "[3, +Inf]");
|
||||
equalsAssert(valueOf("00400022:0:register:00000000"), "[3, 8]");
|
||||
equalsAssert(valueOf("00400026:0:register:00000000"), "[-Inf, 8]");
|
||||
equalsAssert(valueOf("00400026:3:register:00000000"), "[-Inf, 39]");
|
||||
equalsAssert(valueOf("0040002a:0:register:00000000"), "[-Inf, +Inf]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalLowAll() {
|
||||
init(6);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL_LX86);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(false);
|
||||
runTest();
|
||||
equalsAssert(valueOf("00400011:0:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("0040001c:0:register:00000000"), "[4, +Inf]");
|
||||
equalsAssert(valueOf("00400021:0:register:00000000"), "[4, 8]");
|
||||
equalsAssert(valueOf("00400026:0:register:00000000"), "[4, 5]");
|
||||
equalsAssert(valueOf("0040002e:0:register:00000000"), "[4, 4]");
|
||||
equalsAssert(valueOf("00400031:0:register:00000000"), "[5, 5]");
|
||||
equalsAssert(valueOf("00400034:0:register:00000000"), "[6, 8]");
|
||||
equalsAssert(valueOf("00400037:0:register:00000000"), "[9, +Inf]");
|
||||
equalsAssert(valueOf("0040003a:0:register:00000000"), "[-Inf, 3]");
|
||||
equalsAssert(valueOf("00400042:0:register:00000000"), "[0, 3]");
|
||||
equalsAssert(valueOf("00400045:0:register:00000000"), "[-Inf, -1]");
|
||||
equalsAssert(valueOf("0040004a:0:register:00000000"), "[-Inf, -1]"); // Should be _|_
|
||||
equalsAssert(valueOf("0040004d:0:register:00000000"), "[-Inf, -1]");
|
||||
}
|
||||
|
||||
@Category(AbstractLisaTest.class)
|
||||
@Test
|
||||
public void testIntervalLowAllRev() {
|
||||
init(7);
|
||||
lisaOptions.setValueDomain(ValueDomainOption.VALUE_INTERVAL_LX86);
|
||||
lisaOptions.setPostState(true);
|
||||
lisaOptions.setHighPcode(false);
|
||||
runTest();
|
||||
equalsAssert(valueOf("00400011:0:register:00000000"), "[-Inf, +Inf]");
|
||||
equalsAssert(valueOf("0040001c:0:register:00000000"), "[-Inf, -4]");
|
||||
equalsAssert(valueOf("00400021:0:register:00000000"), "[-8, -4]");
|
||||
equalsAssert(valueOf("00400026:0:register:00000000"), "[-5, -4]");
|
||||
equalsAssert(valueOf("0040002e:0:register:00000000"), "[-4, -4]");
|
||||
equalsAssert(valueOf("00400031:0:register:00000000"), "[-5, -5]");
|
||||
equalsAssert(valueOf("00400034:0:register:00000000"), "[-8, -6]");
|
||||
equalsAssert(valueOf("00400037:0:register:00000000"), "[-Inf, -9]");
|
||||
equalsAssert(valueOf("0040003a:0:register:00000000"), "[-3, +Inf]");
|
||||
equalsAssert(valueOf("00400042:0:register:00000000"), "[-3, 0]");
|
||||
equalsAssert(valueOf("00400045:0:register:00000000"), "[1, +Inf]");
|
||||
equalsAssert(valueOf("0040004a:0:register:00000000"), "_|_");
|
||||
equalsAssert(valueOf("0040004d:0:register:00000000"), "[1, +Inf]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user