GP-6129: post-review(2)

GP-6129: post-review
GP-6129: post-review
GP-6129: typo
GP-6129: first bits
GP-6129: first resultsGP-6129: more or less functional sarif tableGP-6129: pre-refactorGP-6129: tagging taint with opGP-6129: fix for composed libsGP-6129: taint action workingGP-6129: fqname errorGP-6129: error handlingGP-6129: error handling
This commit is contained in:
d-millar
2025-11-24 15:11:54 -05:00
parent 4c7ea237c3
commit cba3d5a963
23 changed files with 883 additions and 68 deletions
@@ -0,0 +1,453 @@
/* ###
* 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.app.plugin.core.debug.taint;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import com.google.gson.*;
import com.google.gson.stream.JsonWriter;
import docking.action.builder.ActionBuilder;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.location.DefaultDecompilerLocation;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.service.breakpoint.PlaceEmuBreakpointActionItem;
import ghidra.app.plugin.core.decompiler.taint.*;
import ghidra.app.script.AskDialog;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.ConsoleService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.exec.PcodeProgram;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.util.*;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.property.*;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import sarif.SarifService;
import sarif.export.SarifWriterTask;
import sarif.export.WrappedLogicalLocation;
import sarif.managers.SarifMgr;
/**
* Container for all the decompiler elements the users "selects" via the menu.
* This data is used to build queries.
*/
public class EmulatorTaintState extends AbstractTaintState {
private DebuggerTraceManagerService traceManager;
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private static final String SARIF_URL =
"https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json";
private static final String SARIF_VERSION = "2.1.0";
private int llIndex = 0;
private Map<String, WrappedLogicalLocation> logicalLocations = new HashMap<>();
//private Set<VariableRef> annotationSet = new HashSet<>();
private Map<String, Map<Integer, String>> registerNames = new HashMap<>();
private Object lastValue;
public record KTV(String key, String type, String value, String displayName) {}
public interface SetTaintAction {
String NAME = "Set Taint";
String DESCRIPTION = "Set taint for given varnode";
String GROUP = DebuggerResources.GROUP_GENERAL;
String HELP_ANCHOR = "set_taint";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarGroup(GROUP)
.menuGroup(GROUP)
.popupMenuGroup(GROUP)
.popupMenuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
public EmulatorTaintState(TaintPlugin plugin) {
super(plugin);
ENGINE_NAME = "emulator";
plugin.setTaintState(this);
SetTaintAction.builder(plugin)
.withContext(ProgramLocationActionContext.class)
.onAction(this::setTaint)
.buildAndInstall(plugin.getTool());
}
private void setTaint(ProgramLocationActionContext context) {
Program currentProgram = plugin.getCurrentProgram();
Address addr = context.getAddress();
FunctionManager functionManager = currentProgram.getFunctionManager();
Function f = functionManager.getFunctionContaining(addr);
ProgramLocation location = context.getLocation();
int row = location.getRow();
int offset = location.getCharOffset();
String tokenId = null;
if (location instanceof PcodeFieldLocation pfl) {
List<String> pcodeStrings = pfl.getPcodeStrings();
String test = pcodeStrings.get(row);
int lastSpace = test.lastIndexOf(" ");
int index = offset > lastSpace ? 1 : 0;
Instruction inst = currentProgram.getListing().getInstructionContaining(addr);
PcodeOp[] pcode = inst.getPcode();
PcodeOp op = pcode[row];
if (index >= op.getNumInputs()) {
index--;
}
Varnode vn = op.getInput(index);
tokenId = vn2oper(vn);
sources.add(new TaintLabel(MarkType.SOURCE, f.getName(), addr, vn.getAddress()));
}
else if (location instanceof OperandFieldLocation ofl) {
Address refAddress = ofl.getRefAddress();
sources.add(new TaintLabel(MarkType.SOURCE, f.getName(), addr, refAddress));
if (refAddress == null) {
tokenId = ofl.getOperandRepresentation();
}
else {
tokenId = addr2oper(refAddress, refAddress.getSize() / 8);
}
}
else if (location instanceof DefaultDecompilerLocation ddl) {
ClangToken token = ddl.getToken();
plugin.toggleIcon(MarkType.SOURCE, token, false);
return; // taint is set via the token
}
else {
AskDialog<String> dialog = new AskDialog<>("Emulator Taint",
"Varnode address", AskDialog.STRING, lastValue);
if (dialog.isCanceled()) {
return;
}
tokenId = dialog.getValueAsString();
}
setTaint(MarkType.SOURCE, f, addr, tokenId);
}
private boolean init() {
PluginTool tool = plugin.getTool();
traceManager = tool.getService(DebuggerTraceManagerService.class);
current = traceManager.getCurrent();
return current.getTrace() != null;
}
/**
* Build the query string, save it to a file the users selects, and run the
* engine using the index and the query that is saved to the file.
*/
@Override
public boolean queryIndex(Program program, PluginTool tool, QueryType queryType) {
if (!init()) {
return false;
}
taintOptions = plugin.getOptions();
TraceAddressPropertyManager mgr = current.getTrace().getAddressPropertyManager();
TracePropertyMap<String> propertyMap = mgr.getPropertyMap("Taint", String.class);
readQueryResultsIntoDataFrame(program, propertyMap);
return true;
}
@Override
public TaintLabel toggleMark(MarkType mtype, ClangToken token) throws PcodeException {
TaintLabel mark = super.toggleMark(mtype, token);
setTaint(mtype, mark);
return mark;
}
private void setTaint(MarkType source, Function f, Address target, String tokenId) {
if (!init()) {
return;
}
String taint = "%s = taint_arr(%s);".formatted(tokenId, tokenId);
String sleigh = """
%s
emu_exec_decoded();
""".formatted(taint);
injectTaint(target, sleigh);
}
public void setTaint(MarkType type, TaintLabel mark) {
if (!init()) {
return;
}
Address target = mark.getAddress();
String opnd = vn2oper(mark.getVnode());
String taint = "%s = taint_arr(%s);".formatted(opnd, opnd);
String sleigh = """
%s
emu_exec_decoded();
""".formatted(taint);
injectTaint(target, sleigh);
}
private void injectTaint(Address target, String sleigh) {
PlaceEmuBreakpointActionItem item = new PlaceEmuBreakpointActionItem(current.getTrace(),
current.getSnap(), target, 1, Set.of(TraceBreakpointKind.SW_EXECUTE),
sleigh);
item.execute();
}
public PcodeProgram rebase(Address target, PcodeProgram orig) {
List<PcodeOp> origCode = orig.getCode();
List<PcodeOp> code = new ArrayList<>();
for (PcodeOp op : origCode) {
code.add(new PcodeOp(target, op.getSeqnum().getTime(), op.getOpcode(), op.getInputs(),
op.getOutput()));
}
return new PcodeProgram(orig, code);
}
private Writer getWriter() {
return new StringWriter(10000);
}
protected void readQueryResultsIntoDataFrame(Program program,
TracePropertyMap<String> propertyMap) {
taintAddressSet.clear();
taintVarnodeMap.clear();
logicalLocations.clear();
llIndex = 0;
try {
Writer baseWriter = getWriter();
JsonWriter writer = new JsonWriter(baseWriter);
writer.setIndent(" ");
Gson gson = new GsonBuilder().setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.serializeNulls()
.disableHtmlEscaping()
.create();
JsonObject sarif = new JsonObject();
JsonArray results = new JsonArray();
JsonArray llocs = new JsonArray();
writeSarifHeader(program, sarif, results, llocs);
registerNames = generateRegisterMap(program);
AddressSetView addressSetView =
propertyMap.getAddressSetView(Lifespan.toNow(current.getSnap()));
for (AddressRange addressRange : addressSetView) {
TracePropertyMapSpace<String> space =
propertyMap.getPropertyMapSpace(addressRange.getAddressSpace(), false);
Collection<Entry<TraceAddressSnapRange, String>> entries =
space.getEntries(Lifespan.toNow(current.getSnap()), addressRange);
for (Entry<TraceAddressSnapRange, String> entry : entries) {
writeResult(program.getFunctionManager(), results, llocs, entry);
}
}
monitor.setMessage("Results written...exporting to JSON");
gson.toJson(sarif, writer);
monitor.setMessage("JSON completed");
StringWriter w = (StringWriter) baseWriter;
StringBuffer sb = w.getBuffer();
SarifService sarifService = plugin.getSarifService();
SarifMgr.getColumnKeys().put("displayName", true);
currentQueryData = sarifService.readSarif(sb.toString());
}
catch (IOException e) {
Msg.error(this, e.getMessage());
}
}
private Map<String, Map<Integer, String>> generateRegisterMap(Program program) {
Language language = program.getLanguage();
for (Register r : language.getRegisters()) {
Map<Integer, String> sizeMap = registerNames.computeIfAbsent(r.getAddress().toString(),
a -> new HashMap<Integer, String>());
sizeMap.put(r.getBitLength(), r.getName());
if (r.equals(r.getBaseRegister())) {
sizeMap.put(0, r.getName());
}
}
return registerNames;
}
private void writeSarifHeader(Program program, JsonObject sarif, JsonArray results,
JsonArray llocs) {
sarif.addProperty("$schema", SARIF_URL);
sarif.addProperty("version", SARIF_VERSION);
sarif.add("properties", new JsonObject());
JsonArray runs = new JsonArray();
sarif.add("runs", runs);
JsonObject run = new JsonObject();
runs.add(run);
writeToolInfo(program, run);
run.add("results", results);
run.add("logicalLocations", llocs);
}
private void writeToolInfo(Program program, JsonObject run) {
JsonObject tool = new JsonObject();
run.add("tool", tool);
JsonObject driver = new JsonObject();
tool.add("driver", driver);
driver.addProperty("name", "emulator");
driver.addProperty("version", "0.1");
driver.addProperty("informationUri", "https://github.com/NationalSecurityAgency/ghidra");
JsonArray artifacts = new JsonArray();
run.add("artifacts", artifacts);
JsonObject artifact = new JsonObject();
artifacts.add(artifact);
JsonObject location = new JsonObject();
artifact.add("location", location);
location.addProperty("uri", program.getExecutablePath());
JsonObject properties = new JsonObject();
artifact.add("properties", properties);
JsonObject additionalProperties = new JsonObject();
properties.add("additionalProperties", additionalProperties);
additionalProperties.addProperty("imageBase", program.getImageBase().toString());
artifact.addProperty("sourceLanguage", program.getLanguageID().getIdAsString());
JsonObject description = new JsonObject();
artifact.add("description", description);
description.addProperty("text", program.getMetadata().get("Compiler ID"));
}
private void writeResult(FunctionManager fmgr, JsonArray results, JsonArray llocs,
Entry<TraceAddressSnapRange, String> entry) {
try {
SarifWriterTask task;
SarifLogicalLocationWriter writer = new SarifLogicalLocationWriter(entry, fmgr);
Address address = writer.getAddress();
if (address != null) {
taintAddressSet.add(address);
}
WrappedLogicalLocation wll = writer.getLogicalLocation();
String llkey = wll.getLogicalLocation().getFullyQualfiedName();
if (!logicalLocations.containsKey(llkey)) {
wll.setIndex(llIndex++);
logicalLocations.put(llkey, wll);
task = new SarifWriterTask("taint", writer, llocs);
task.monitoredRun(monitor);
}
wll = logicalLocations.get(llkey);
String displayName = generateDisplayName(writer.getKey(), writer.getType());
KTV ktv = new KTV(writer.getKey(), writer.getType(), writer.getValue(), displayName);
SarifKeyValueWriter kvwriter = new SarifKeyValueWriter(ktv, wll);
task = new SarifWriterTask("taint", kvwriter, results);
task.monitoredRun(monitor);
}
catch (IOException e) {
Msg.error(this, e.getMessage());
}
}
private String generateDisplayName(String key, String type) {
String displayName = key;
String searchKey = key.startsWith("ram@") ? key.substring(4) : key;
if (registerNames.containsKey(searchKey)) {
Map<Integer, String> sizeMap = registerNames.get(searchKey);
Integer size = switch (type) {
case "(int64)" -> 64;
case "(int32)" -> 32;
case "(int16)" -> 16;
case "(int8)" -> 8;
default -> 0;
};
String res = sizeMap.get(size);
if (res == null) {
res = sizeMap.get(0);
}
displayName = key.startsWith("ram@") ? "ram@" + res : res;
}
return displayName;
}
private String vn2oper(Varnode vn) {
Address vnAddr = vn.getAddress();
return addr2oper(vnAddr, vn.getSize());
}
private String addr2oper(Address addr, int size) {
AddressSpace space = addr.getAddressSpace();
return "*[%s]:%d 0x%s:%d".formatted(space.getName(), size,
addr.toString(false), addr.getSize() / 8);
}
@Override
public String getQueryName() {
return "emulator";
}
@Override
public GhidraScript getExportScript(ConsoleService console, boolean perFunction) {
return null;
}
@Override
public void buildQuery(List<String> paramList, String enginePath, File indexDBFile,
String indexDirectory) {
//UNNEEDED
}
@Override
public void buildIndex(List<String> paramList, String enginePath, String factsPath,
String indexDirectory) {
//UNNEEDED
}
@Override
protected void writeHeader(PrintWriter writer) {
//UNNEEDED
}
@Override
protected void writeRule(PrintWriter writer, TaintLabel mark, boolean isSource) {
//UNNEEDED
}
@Override
protected void writeGate(PrintWriter writer, TaintLabel mark) {
//UNNEEDED
}
@Override
protected void writeFooter(PrintWriter writer) {
//UNNEEDED
}
}
@@ -0,0 +1,41 @@
/* ###
* 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.app.plugin.core.debug.taint;
import ghidra.app.plugin.core.debug.taint.EmulatorTaintState.KTV;
import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
import ghidra.program.model.data.ISF.IsfObject;
public class ExtKeyValue implements IsfObject {
String name;
String displayName;
String type;
String value;
String taintLabels;
@Exclude
private int index;
public ExtKeyValue(KTV ktv) {
this.name = ktv.key();
this.displayName = ktv.displayName();
this.type = "Instruction";
this.value = ktv.value();
this.taintLabels = "[" + ktv.value() + "]";
}
}
@@ -0,0 +1,50 @@
/* ###
* 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.app.plugin.core.debug.taint;
import java.io.IOException;
import ghidra.app.plugin.core.debug.taint.EmulatorTaintState.KTV;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import sarif.export.*;
public class SarifKeyValueWriter extends AbstractExtWriter {
private ExtKeyValue isf;
private WrappedLogicalLocation wll;
public SarifKeyValueWriter(KTV ktv, WrappedLogicalLocation wll)
throws IOException {
super(null);
this.isf = new ExtKeyValue(ktv);
this.wll = wll;
}
@Override
protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
genData(monitor);
root.add("structuredObject", objects);
}
private void genData(TaskMonitor monitor) {
ExtLogicalLocation lloc = wll.getLogicalLocation();
SarifObject sarif = new SarifObject(lloc.getDecoratedName(), "VALUE", lloc, getTree(isf),
wll.getAddress(), wll.getIndex());
objects.add(getTree(sarif));
}
}
@@ -0,0 +1,102 @@
/* ###
* 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.app.plugin.core.debug.taint;
import java.io.IOException;
import java.util.Map.Entry;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFormatException;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import sarif.export.*;
public class SarifLogicalLocationWriter extends AbstractExtWriter {
private WrappedLogicalLocation lloc;
private String key;
private String type;
private String value;
private Address addr;
public SarifLogicalLocationWriter(Entry<TraceAddressSnapRange, String> entry,
FunctionManager fmgr)
throws IOException {
super(null);
Function f = null;
String location = "UNKNOWN";
Address min = entry.getKey().getX1();
key = min.toString(true);
type = min.getAddressSpace().getName();
value = entry.getValue();
if (value.contains("@")) {
String[] split = value.split("@");
value = split[0];
String[] vSplit = split[1].split(",");
try {
String seq = vSplit[1].substring(3).trim() + ":" + vSplit[2].trim();
addr = min.getAddress(vSplit[1].trim());
f = fmgr.getFunctionContaining(addr);
if (f != null) {
location = f.getName() + ":" + key;
location += "@" + f.getEntryPoint();
location += ":" + seq;
}
}
catch (AddressFormatException e) {
e.printStackTrace();
}
}
ExtLogicalLocation ext = new ExtLogicalLocation(key, f, location, "");
lloc = new WrappedLogicalLocation(ext, addr);
}
@Override
protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
genData(monitor);
root.add("logicalLocation", objects);
}
private void genData(TaskMonitor monitor) {
objects.add(getTree(lloc.getLogicalLocation()));
}
public WrappedLogicalLocation getLogicalLocation() {
return lloc;
}
public Address getAddress() {
return addr;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
public String getType() {
return type;
}
}
@@ -98,6 +98,11 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
* carries. All others, we assume every byte could be tainted by any other byte in the vector, * carries. All others, we assume every byte could be tainted by any other byte in the vector,
* so we union and broadcast. * so we union and broadcast.
*/ */
@Override
public TaintVec unaryOp(PcodeOp op, TaintVec in1) {
return PcodeArithmetic.super.unaryOp(op, in1).withOp(op);
}
@Override @Override
public TaintVec unaryOp(int opcode, int sizeout, int sizein1, TaintVec in1) { public TaintVec unaryOp(int opcode, int sizeout, int sizein1, TaintVec in1) {
return switch (opcode) { return switch (opcode) {
@@ -128,35 +133,20 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
switch (op.getOpcode()) { switch (op.getOpcode()) {
case PcodeOp.INT_XOR, PcodeOp.INT_SUB, PcodeOp.BOOL_XOR -> { case PcodeOp.INT_XOR, PcodeOp.INT_SUB, PcodeOp.BOOL_XOR -> {
if (Objects.equals(op.getInput(0), op.getInput(1))) { if (Objects.equals(op.getInput(0), op.getInput(1))) {
return fromConst(0, op.getOutput().getSize()); return fromConst(0, op.getOutput().getSize()); // NB: withOp unneeded, as this essentially removes taint
} }
} }
} }
return PcodeArithmetic.super.binaryOp(op, in1, in2); int sizein2 = op.getInput(1).getSize();
} int sizeout = op.getOutput().getSize();
return switch (op.getOpcode()) {
/**
* {@inheritDoc}
*
* <p>
* For bitwise operations, we pair-wise union corresponding elements of the two input taint
* vectors. For integer add and subtract, we do the same, but account for the carry bits
* possibly cascading into bytes of higher significance. For {@link PcodeOp#PIECE}, we perform
* the analog as on concrete state, since the operand sizes are constant. For all others, we
* must consider that every output byte is potentially affected by any or all bytes of both
* input operands. Thus, we union and broadcast.
*/
@Override
public TaintVec binaryOp(int opcode, int sizeout, int sizein1, TaintVec in1,
int sizein2, TaintVec in2) {
return switch (opcode) {
case PcodeOp.BOOL_AND, PcodeOp.BOOL_OR, PcodeOp.BOOL_XOR, PcodeOp.INT_AND, // case PcodeOp.BOOL_AND, PcodeOp.BOOL_OR, PcodeOp.BOOL_XOR, PcodeOp.INT_AND, //
PcodeOp.INT_OR, PcodeOp.INT_XOR -> { PcodeOp.INT_OR, PcodeOp.INT_XOR -> {
yield in1.zipUnion(in2); yield in1.zipUnion(in2).withOp(op);
} }
case PcodeOp.INT_ADD, PcodeOp.INT_SUB -> { case PcodeOp.INT_ADD, PcodeOp.INT_SUB -> {
TaintVec temp = in1.zipUnion(in2); TaintVec temp = in1.zipUnion(in2);
yield temp.setCascade(endian.isBigEndian()); yield temp.setCascade(endian.isBigEndian()).withOp(op);
} }
case PcodeOp.INT_SLESS, PcodeOp.INT_SLESSEQUAL, // case PcodeOp.INT_SLESS, PcodeOp.INT_SLESSEQUAL, //
PcodeOp.INT_LESS, PcodeOp.INT_LESSEQUAL, // PcodeOp.INT_LESS, PcodeOp.INT_LESSEQUAL, //
@@ -164,30 +154,42 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
PcodeOp.FLOAT_LESS, PcodeOp.FLOAT_LESSEQUAL, // PcodeOp.FLOAT_LESS, PcodeOp.FLOAT_LESSEQUAL, //
PcodeOp.FLOAT_EQUAL, PcodeOp.FLOAT_NOTEQUAL -> { PcodeOp.FLOAT_EQUAL, PcodeOp.FLOAT_NOTEQUAL -> {
TaintSet temp = in1.union().union(in2.union()); TaintSet temp = in1.union().union(in2.union());
yield TaintVec.copies(temp, sizeout); yield TaintVec.copies(temp, sizeout).withOp(op);
} }
case PcodeOp.PIECE -> { case PcodeOp.PIECE -> {
TaintVec temp = in1.extended(sizeout, endian.isBigEndian(), false); TaintVec temp = in1.extended(sizeout, endian.isBigEndian(), false);
temp.setShifted(endian.isBigEndian() ? -sizein2 : sizein2, ShiftMode.UNBOUNDED); temp.setShifted(endian.isBigEndian() ? -sizein2 : sizein2, ShiftMode.UNBOUNDED);
yield temp.set(endian.isBigEndian() ? sizeout - sizein2 : 0, in2); yield temp.set(endian.isBigEndian() ? sizeout - sizein2 : 0, in2).withOp(op);
} }
default -> { default -> {
TaintVec temp = in1.zipUnion(in2); TaintVec temp = in1.zipUnion(in2).truncated(sizeout, endian.isBigEndian());
yield temp.setCopies(temp.union()); yield temp.setCopies(temp.union()).withOp(op);
} }
}; };
} }
@Override
public TaintVec binaryOp(int opcode, int sizeout, int sizein1, TaintVec in1,
int sizein2, TaintVec in2) {
throw new RuntimeException("Not supported");
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* *
* <p> * <p>
* Here we handle indirect taint for indirect writes * Here we handle indirect taint for indirect writes
*/ */
@Override
public TaintVec modBeforeStore(PcodeOp op, AddressSpace space, TaintVec inOffset,
TaintVec inValue) {
return inValue.tagIndirectWrite(inOffset).withOp(op);
}
@Override @Override
public TaintVec modBeforeStore(int sizeinOffset, AddressSpace space, TaintVec inOffset, public TaintVec modBeforeStore(int sizeinOffset, AddressSpace space, TaintVec inOffset,
int sizeinValue, TaintVec inValue) { int sizeinValue, TaintVec inValue) {
return inValue.tagIndirectWrite(inOffset); throw new RuntimeException("Not supported");
} }
/** /**
@@ -196,10 +198,16 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
* <p> * <p>
* Here we handle indirect taint for indirect reads * Here we handle indirect taint for indirect reads
*/ */
@Override
public TaintVec modAfterLoad(PcodeOp op, AddressSpace space, TaintVec inOffset,
TaintVec inValue) {
return inValue.tagIndirectRead(inOffset).withOp(op);
}
@Override @Override
public TaintVec modAfterLoad(int sizeinOffset, AddressSpace space, TaintVec inOffset, public TaintVec modAfterLoad(int sizeinOffset, AddressSpace space, TaintVec inOffset,
int sizeinValue, TaintVec inValue) { int sizeinValue, TaintVec inValue) {
return inValue.tagIndirectRead(inOffset); throw new RuntimeException("Not supported");
} }
/** /**
@@ -19,7 +19,11 @@ import java.util.Set;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary; import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary;
import ghidra.pcode.exec.PcodeExecutor;
import ghidra.program.model.address.Address;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.taint.model.*; import ghidra.taint.model.*;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
@@ -59,8 +63,15 @@ public class TaintPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary<Pair<by
* @return the same value, with the generated taint unioned in * @return the same value, with the generated taint unioned in
*/ */
@PcodeUserop @PcodeUserop
public Pair<byte[], TaintVec> taint_var(Pair<byte[], TaintVec> in) { public Pair<byte[], TaintVec> taint_var(Pair<byte[], TaintVec> in, @OpOp PcodeOp op,
return Pair.of(in.getLeft(), in.getRight().eachUnion(nextVar())); @OpExecutor PcodeExecutor executor) {
if (executor instanceof PcodeThreadExecutor te) {
Address counter = te.getThread().getCounter();
op = new PcodeOp(counter, op.getSeqnum().getTime(), op.getOpcode(), op.getInputs(),
op.getOutput());
}
return Pair.of(in.getLeft(), in.getRight().eachUnion(nextVar()).withOp(op));
} }
/** /**
@@ -73,12 +84,22 @@ public class TaintPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary<Pair<by
* [arr_0_0][arr_0_1]...[arr_0_7]. * [arr_0_0][arr_0_1]...[arr_0_7].
* *
* @param in the input value * @param in the input value
* @param op the taint source
* @param executor the current executor
* @return the same value, with the generated taint unioned in * @return the same value, with the generated taint unioned in
*/ */
@PcodeUserop @PcodeUserop
public Pair<byte[], TaintVec> taint_arr(Pair<byte[], TaintVec> in) { public Pair<byte[], TaintVec> taint_arr(Pair<byte[], TaintVec> in, @OpOp PcodeOp op,
@OpExecutor PcodeExecutor executor) {
if (executor instanceof PcodeThreadExecutor te) {
Address counter = te.getThread().getCounter();
op = new PcodeOp(counter, op.getSeqnum().getTime(), op.getOpcode(), op.getInputs(),
op.getOutput());
}
TaintVec taint = in.getRight(); TaintVec taint = in.getRight();
taint = taint.zipUnion(TaintVec.array(nextArrName(), 0, taint.length)); taint = taint.zipUnion(TaintVec.array(nextArrName(), 0, taint.length)).withOp(op);
return Pair.of(in.getLeft(), taint); return Pair.of(in.getLeft(), taint);
} }
} }
@@ -78,6 +78,10 @@ public class TaintPieceHandler extends AbstractPropertyBasedPieceHandler<byte[],
@Override @Override
protected void decodeFrom(PcodeExecutorStatePiece<byte[], TaintVec> piece, AddressSetView limit, protected void decodeFrom(PcodeExecutorStatePiece<byte[], TaintVec> piece, AddressSetView limit,
AddressRange range, String propertyValue) { AddressRange range, String propertyValue) {
if (propertyValue.contains("@")) {
// FIXME: WOuld be nice to actually decode the p-code op
propertyValue = propertyValue.substring(0, propertyValue.indexOf("@"));
}
TaintVec vec = TaintVec.copies(TaintSet.parse(propertyValue), (int) range.getLength()); TaintVec vec = TaintVec.copies(TaintSet.parse(propertyValue), (int) range.getLength());
if (limit.contains(range.getMaxAddress(), range.getMaxAddress())) { if (limit.contains(range.getMaxAddress(), range.getMaxAddress())) {
piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(), piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(),
@@ -115,7 +119,11 @@ public class TaintPieceHandler extends AbstractPropertyBasedPieceHandler<byte[],
property.clear(new AddressRangeImpl(address, address)); property.clear(new AddressRangeImpl(address, address));
} }
else { else {
property.put(address, s.toString()); String desc = s.toString();
if (value.getOriginatingOp() != null) {
desc += "@" + value.getOriginatingOp().getSeqnum().toString();
}
property.put(address, desc);
} }
} }
} }
@@ -21,6 +21,7 @@ import java.util.Map.Entry;
import ghidra.pcode.exec.PcodeStateCallbacks; import ghidra.pcode.exec.PcodeStateCallbacks;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.taint.model.TaintSet; import ghidra.taint.model.TaintSet;
import ghidra.taint.model.TaintVec; import ghidra.taint.model.TaintVec;
import ghidra.util.MathUtilities; import ghidra.util.MathUtilities;
@@ -39,6 +40,7 @@ public class TaintSpace {
protected final TaintPcodeExecutorStatePiece piece; protected final TaintPcodeExecutorStatePiece piece;
// TODO: There must be a better way. Similar to SemisparseByteArray? // TODO: There must be a better way. Similar to SemisparseByteArray?
protected final NavigableMap<Long, TaintSet> taints = new TreeMap<>(Long::compareUnsigned); protected final NavigableMap<Long, TaintSet> taints = new TreeMap<>(Long::compareUnsigned);
protected final NavigableMap<Long, PcodeOp> ops = new TreeMap<>(Long::compareUnsigned);
public TaintSpace(AddressSpace space, TaintPcodeExecutorStatePiece piece) { public TaintSpace(AddressSpace space, TaintPcodeExecutorStatePiece piece) {
this.space = space; this.space = space;
@@ -59,6 +61,7 @@ public class TaintSpace {
* @param cb callbacks to receive emulation events * @param cb callbacks to receive emulation events
*/ */
public void set(long offset, TaintVec val, PcodeStateCallbacks cb) { public void set(long offset, TaintVec val, PcodeStateCallbacks cb) {
ops.put(offset, val.getOriginatingOp());
for (int i = 0; i < val.length; i++) { for (int i = 0; i < val.length; i++) {
TaintSet s = val.get(i); TaintSet s = val.get(i);
/* /*
@@ -148,7 +151,8 @@ public class TaintSpace {
while (taints.get(end) != null) { while (taints.get(end) != null) {
end++; end++;
} }
TaintVec vec = new TaintVec(MathUtilities.unsignedMin(1024, end - offset)); PcodeOp pcodeOp = ops.get(offset); // Needed here to generate the TaintVec
TaintVec vec = new TaintVec(MathUtilities.unsignedMin(1024, end - offset), pcodeOp);
getInto(offset, vec, PcodeStateCallbacks.NONE); getInto(offset, vec, PcodeStateCallbacks.NONE);
return Map.entry(offset, vec); return Map.entry(offset, vec);
} }
@@ -20,6 +20,8 @@ import java.util.function.BinaryOperator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import ghidra.program.model.pcode.PcodeOp;
/** /**
* A mutable, but fixed-size, buffer of taint sets * A mutable, but fixed-size, buffer of taint sets
* *
@@ -33,8 +35,15 @@ import java.util.stream.Stream;
*/ */
public class TaintVec { public class TaintVec {
public static TaintVec of(TaintSet... taints) { /**
return new TaintVec(taints); * Create a vector of taint sets
*
* @param op the originating p-code op
* @param taints the taint set
* @return the new vector
*/
public static TaintVec of(PcodeOp op, TaintSet... taints) {
return new TaintVec(taints, op);
} }
/** /**
@@ -79,11 +88,13 @@ public class TaintVec {
private TaintSet[] sets; private TaintSet[] sets;
private List<TaintSet> setsView; private List<TaintSet> setsView;
public final int length; public final int length;
private final PcodeOp originatingOp;
private TaintVec(TaintSet[] sets) { private TaintVec(TaintSet[] sets, PcodeOp op) {
this.sets = sets; this.sets = sets;
this.setsView = Collections.unmodifiableList(Arrays.asList(sets)); this.setsView = Collections.unmodifiableList(Arrays.asList(sets));
this.length = sets.length; this.length = sets.length;
this.originatingOp = op;
} }
/** /**
@@ -92,7 +103,17 @@ public class TaintVec {
* @param length the length * @param length the length
*/ */
public TaintVec(int length) { public TaintVec(int length) {
this(new TaintSet[length]); this(new TaintSet[length], null);
}
/**
* Create a new uninitialized taint vector of the given length
*
* @param length the length
* @param op the originating op
*/
public TaintVec(int length, PcodeOp op) {
this(new TaintSet[length], op);
} }
@Override @Override
@@ -573,4 +594,22 @@ public class TaintVec {
} }
return vec; return vec;
} }
/**
* @return the originating op
*/
public PcodeOp getOriginatingOp() {
return originatingOp;
}
/**
* Supply the originating op
*
* @param the originating op
* @return the tagged TaintVec
*
*/
public TaintVec withOp(PcodeOp op) {
return new TaintVec(sets, op);
}
} }
@@ -29,6 +29,7 @@ public class TaintLabel {
private HighFunction hfun; private HighFunction hfun;
private HighVariable hvar; private HighVariable hvar;
private Varnode vnode; private Varnode vnode;
private Address vnAddr;
private boolean active; private boolean active;
private String label; private String label;
private boolean isGlobal = false; private boolean isGlobal = false;
@@ -40,7 +41,7 @@ public class TaintLabel {
private int size = 0; private int size = 0;
public TaintLabel(MarkType mtype, ClangToken token) throws PcodeException { public TaintLabel(MarkType mtype, ClangToken token) throws PcodeException {
HighVariable highVar = token.getHighVariable(); HighVariable highVar = token.getHighVariable();
if (highVar == null) { if (highVar == null) {
hfun = token.getClangFunction().getHighFunction(); hfun = token.getClangFunction().getHighFunction();
@@ -54,22 +55,24 @@ public class TaintLabel {
} }
this.vnode = token.getVarnode(); this.vnode = token.getVarnode();
if (vnode != null) { // The user pointed at a particular usage, not just the vardecl
String fn = token instanceof ClangFuncNameToken ftoken ? ftoken.getText()
: hfun.getFunction().getName();
PcodeOp pcodeOp = token.getPcodeOp();
Address target =
pcodeOp == null ? hfun.getFunction().getEntryPoint() : pcodeOp.getSeqnum().getTarget();
if (vnode == null && pcodeOp != null) {
vnode = pcodeOp.getOutput();
}
if (vnode != null) {
vnAddr = vnode.getAddress();
HighVariable high = vnode.getHigh(); HighVariable high = vnode.getHigh();
if (high instanceof HighLocal) { if (high instanceof HighLocal) {
highVar = hfun.splitOutMergeGroup(high, vnode); highVar = hfun.splitOutMergeGroup(high, vnode);
} }
} }
String fn = token instanceof ClangFuncNameToken ftoken ? ftoken.getText()
: hfun.getFunction().getName();
PcodeOp pcodeOp = token.getPcodeOp();
Address target = pcodeOp == null ? hfun.getFunction().getEntryPoint() : pcodeOp.getSeqnum().getTarget();
if (vnode == null && pcodeOp != null) {
vnode = pcodeOp.getOutput();
highVar = vnode.getHigh();
}
this.mtype = mtype; this.mtype = mtype;
this.token = token; this.token = token;
this.fname = fn; this.fname = fn;
@@ -85,6 +88,41 @@ public class TaintLabel {
this.label = mtype.toString(); this.label = mtype.toString();
} }
public TaintLabel(MarkType mtype, ClangToken token, HighVariable hvar,
String fn, Address target) {
this.mtype = mtype;
this.token = token;
if (token != null) {
this.clangLine = token.getLineParent();
}
this.hvar = hvar;
if (hvar == null) {
if (token != null) {
hfun = token.getClangFunction().getHighFunction();
}
}
else {
size = hvar.getSize();
hfun = hvar.getHighFunction();
HighSymbol symbol = hvar.getSymbol();
if (symbol != null) {
isGlobal = symbol.isGlobal();
}
}
this.fname = fn;
this.addr = target;
active = true;
// Initial label is one of SOURCE, SINK, or GATE
label = mtype.toString();
}
public TaintLabel(MarkType mtype, String fn, Address target, Address refAddress) {
this(mtype, null, null, fn, target);
vnAddr = refAddress;
}
public ClangLine getClangLine() { public ClangLine getClangLine() {
return this.clangLine; return this.clangLine;
} }
@@ -155,7 +193,7 @@ public class TaintLabel {
public void activate() { public void activate() {
active = true; active = true;
} }
public int getSize() { public int getSize() {
return size; return size;
} }
@@ -231,12 +269,12 @@ public class TaintLabel {
} }
return true; return true;
} }
public Address getVarnodeAddress() { public Address getVarnodeAddress() {
if (vnode != null) { return vnAddr;
return vnode.getAddress();
}
return null;
} }
public Varnode getVnode() {
return vnode;
}
} }
@@ -220,6 +220,9 @@ public class SarifTaintResultHandler extends SarifResultHandler {
for (int row : selected) { for (int row : selected) {
Map<String, Object> r = tableProvider.getRow(row); Map<String, Object> r = tableProvider.getRow(row);
String kind = (String) r.get("kind"); String kind = (String) r.get("kind");
if (kind == null) {
continue;
}
if (kind.equals("member") || kind.startsWith("path ")) { if (kind.equals("member") || kind.startsWith("path ")) {
getTaintedInstruction(map, r); getTaintedInstruction(map, r);
} }
@@ -268,7 +268,7 @@ public class SarifUtils {
} }
addr = subparts[0]; addr = subparts[0];
} }
return program.getAddressFactory().getAddress(addr); return addr == null ? null : program.getAddressFactory().getAddress(addr);
} }
public static List<Address> extractFQNameAddrPair(Program program, String fqname) { public static List<Address> extractFQNameAddrPair(Program program, String fqname) {
@@ -31,7 +31,7 @@ public class ExtLogicalLocation implements IsfObject {
this.kind = "variable"; this.kind = "variable";
this.decoratedName = op; this.decoratedName = op;
this.fullyQualifiedName = location + ":" + name; this.fullyQualifiedName = location + ":" + name;
this.uri = function.getProgram().getExecutablePath(); this.uri = function == null ? "UNKNOWN" : function.getProgram().getExecutablePath();
} }
public String getName() { public String getName() {
@@ -30,6 +30,7 @@ import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
@@ -167,7 +168,7 @@ public interface EmuSyscallLibrary<T> extends PcodeUseropLibrary<T> {
@Override @Override
public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
Varnode outVar, List<Varnode> inVars) { PcodeOp op, Varnode outVar, List<Varnode> inVars) {
syslib.syscall(executor, library); syslib.syscall(executor, library);
} }
@@ -31,6 +31,7 @@ import ghidra.pcode.memstate.MemoryBank;
import ghidra.pcode.memstate.MemoryState; import ghidra.pcode.memstate.MemoryState;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.util.Msg; import ghidra.util.Msg;
@@ -121,7 +122,7 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
@Override @Override
public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
Varnode outVar, List<Varnode> inVars) { PcodeOp op, Varnode outVar, List<Varnode> inVars) {
behavior.evaluate(emulate, outVar, inVars.toArray(Varnode[]::new)); behavior.evaluate(emulate, outVar, inVars.toArray(Varnode[]::new));
} }
@@ -67,7 +67,8 @@ public abstract class AuxPcodeEmulator<U> extends AbstractPcodeMachine<Pair<byte
@Override @Override
protected PcodeUseropLibrary<Pair<byte[], U>> createThreadStubLibrary() { protected PcodeUseropLibrary<Pair<byte[], U>> createThreadStubLibrary() {
return getPartsFactory().createLocalUseropStub(this); return super.createThreadStubLibrary()
.compose(getPartsFactory().createLocalUseropStub(this));
} }
@Override @Override
@@ -102,7 +102,7 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
@Override @Override
public void execute(PcodeExecutor<JitVal> executor, PcodeUseropLibrary<JitVal> library, public void execute(PcodeExecutor<JitVal> executor, PcodeUseropLibrary<JitVal> library,
Varnode outVar, List<Varnode> inVars) { PcodeOp op, Varnode outVar, List<Varnode> inVars) {
throw new AssertionError(); throw new AssertionError();
} }
@@ -79,7 +79,7 @@ public class DecoderUseropLibrary extends AnnotatedPcodeUseropLibrary<Object> {
@Override @Override
public void execute(PcodeExecutor<Object> executor, PcodeUseropLibrary<Object> library, public void execute(PcodeExecutor<Object> executor, PcodeUseropLibrary<Object> library,
Varnode outVar, List<Varnode> inVars) { PcodeOp op, Varnode outVar, List<Varnode> inVars) {
throw new AssertionError(); throw new AssertionError();
} }
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.reflect.TypeUtils;
import ghidra.pcode.exec.PcodeArithmetic.Purpose; import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import utilities.util.AnnotationUtilities; import utilities.util.AnnotationUtilities;
@@ -92,6 +93,17 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
void setPos(AnnotatedPcodeUseropDefinition<?> opdef, int pos) { void setPos(AnnotatedPcodeUseropDefinition<?> opdef, int pos) {
opdef.posOut = pos; opdef.posOut = pos;
} }
},
OP(OpOp.class, PcodeOp.class) {
@Override
int getPos(AnnotatedPcodeUseropDefinition<?> opdef) {
return opdef.posOp;
}
@Override
void setPos(AnnotatedPcodeUseropDefinition<?> opdef, int pos) {
opdef.posOp = pos;
}
}; };
static boolean processParameter(AnnotatedPcodeUseropDefinition<?> opdef, Type declClsOpType, static boolean processParameter(AnnotatedPcodeUseropDefinition<?> opdef, Type declClsOpType,
@@ -243,6 +255,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
private int posState = -1; private int posState = -1;
private int posLib = -1; private int posLib = -1;
private int posOut = -1; private int posOut = -1;
private int posOp = -1;
public AnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary<T> library, Type opType, public AnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary<T> library, Type opType,
Lookup lookup, Method method, PcodeUserop annot) { Lookup lookup, Method method, PcodeUserop annot) {
@@ -290,7 +303,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
@Override @Override
public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
Varnode outVar, List<Varnode> inVars) { PcodeOp op, Varnode outVar, List<Varnode> inVars) {
validateInputs(inVars); validateInputs(inVars);
PcodeExecutorStatePiece<T, T> state = executor.getState(); PcodeExecutorStatePiece<T, T> state = executor.getState();
@@ -308,6 +321,9 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
if (posOut != -1) { if (posOut != -1) {
args.set(posOut, outVar); args.set(posOut, outVar);
} }
if (posOp != -1) {
args.set(posOp, op);
}
placeInputs(executor, args, inVars); placeInputs(executor, args, inVars);
try { try {
@@ -792,6 +808,17 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
public @interface OpOutput { public @interface OpOutput {
} }
/**
* An annotation to receive the CALLOTHER p-code op into a parameter
*
* <p>
* The annotated parameter must have type {@link PcodeOp}).
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface OpOp {
}
protected Map<String, PcodeUseropDefinition<T>> ops = new HashMap<>(); protected Map<String, PcodeUseropDefinition<T>> ops = new HashMap<>();
private Map<String, PcodeUseropDefinition<T>> unmodifiableOps = private Map<String, PcodeUseropDefinition<T>> unmodifiableOps =
Collections.unmodifiableMap(ops); Collections.unmodifiableMap(ops);
@@ -131,8 +131,25 @@ public interface PcodeUseropLibrary<T> {
* @param inVars the input varnodes as ordered in the source. * @param inVars the input varnodes as ordered in the source.
* @see AnnotatedPcodeUseropLibrary.AnnotatedPcodeUseropDefinition * @see AnnotatedPcodeUseropLibrary.AnnotatedPcodeUseropDefinition
*/ */
void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, Varnode outVar, default void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
List<Varnode> inVars); Varnode outVar, List<Varnode> inVars) {
execute(executor, library, null, outVar, inVars);
}
/**
* Invoke/execute the userop.
*
* @param executor the executor invoking this userop.
* @param library the complete library for this execution. Note the library may have been
* composed from more than the one defining this userop.
* @param op the CALLOTHER p-code op
* @param outVar if invoked as an rval, the destination varnode for the userop's output.
* Otherwise, {@code null}.
* @param inVars the input varnodes as ordered in the source.
* @see AnnotatedPcodeUseropLibrary.AnnotatedPcodeUseropDefinition
*/
void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, PcodeOp op,
Varnode outVar, List<Varnode> inVars);
/** /**
* Invoke/execute the raw userop. * Invoke/execute the raw userop.
@@ -147,7 +164,7 @@ public interface PcodeUseropLibrary<T> {
* @param op the {@link PcodeOp#CALLOTHER} op * @param op the {@link PcodeOp#CALLOTHER} op
*/ */
default void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, PcodeOp op) { default void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, PcodeOp op) {
execute(executor, library, op.getOutput(), execute(executor, library, op, op.getOutput(),
Arrays.asList(op.getInputs()).subList(1, op.getNumInputs())); Arrays.asList(op.getInputs()).subList(1, op.getNumInputs()));
} }
@@ -20,6 +20,7 @@ import java.util.*;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition; import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
/** /**
@@ -170,7 +171,7 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
@Override @Override
public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, public void execute(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
Varnode outArg, List<Varnode> inArgs) { PcodeOp op, Varnode outArg, List<Varnode> inArgs) {
PcodeProgram program = programFor(outArg, inArgs, library); PcodeProgram program = programFor(outArg, inArgs, library);
executor.execute(program, library); executor.execute(program, library);
} }
@@ -1149,7 +1149,7 @@ emulator:</p>
<ul> <ul>
<li>For memory state: <a <li>For memory state: <a
href="../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintFieldFactory.java">TaintFieldFactory</a></li> href="../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintFieldFactory.java">TaintFieldFactory</a></li>
<li>For regsiter state: <a <li>For register state: <a
href="../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java">TaintDebuggerRegisterColumnFactory</a></li> href="../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java">TaintDebuggerRegisterColumnFactory</a></li>
</ul> </ul>
<p>Anything more than that would require completely custom providers, <p>Anything more than that would require completely custom providers,
@@ -889,6 +889,6 @@ Since string-based serialization may be a common case, we may eventually provide
For now, we refer you to the implementations for the Taint-augmented emulator: For now, we refer you to the implementations for the Taint-augmented emulator:
* For memory state: [TaintFieldFactory](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintFieldFactory.java) * For memory state: [TaintFieldFactory](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintFieldFactory.java)
* For regsiter state: [TaintDebuggerRegisterColumnFactory](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java) * For register state: [TaintDebuggerRegisterColumnFactory](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java)
Anything more than that would require completely custom providers, plugins, etc. Anything more than that would require completely custom providers, plugins, etc.