diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/EmulatorTaintState.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/EmulatorTaintState.java new file mode 100644 index 0000000000..2980aaf491 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/EmulatorTaintState.java @@ -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 logicalLocations = new HashMap<>(); + //private Set annotationSet = new HashSet<>(); + + private Map> 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 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 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 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 origCode = orig.getCode(); + List 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 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 space = + propertyMap.getPropertyMapSpace(addressRange.getAddressSpace(), false); + Collection> entries = + space.getEntries(Lifespan.toNow(current.getSnap()), addressRange); + for (Entry 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> generateRegisterMap(Program program) { + Language language = program.getLanguage(); + for (Register r : language.getRegisters()) { + Map sizeMap = registerNames.computeIfAbsent(r.getAddress().toString(), + a -> new HashMap()); + 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 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 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 paramList, String enginePath, File indexDBFile, + String indexDirectory) { + //UNNEEDED + } + + @Override + public void buildIndex(List 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 + } + +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/ExtKeyValue.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/ExtKeyValue.java new file mode 100644 index 0000000000..e0ab49210f --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/ExtKeyValue.java @@ -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() + "]"; + } + +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/SarifKeyValueWriter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/SarifKeyValueWriter.java new file mode 100644 index 0000000000..48655c683f --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/SarifKeyValueWriter.java @@ -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)); + } + +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/SarifLogicalLocationWriter.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/SarifLogicalLocationWriter.java new file mode 100644 index 0000000000..664e0484b1 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/SarifLogicalLocationWriter.java @@ -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 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; + } + +} diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeArithmetic.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeArithmetic.java index 3f83bf7f08..4c4da56cf5 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeArithmetic.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeArithmetic.java @@ -98,6 +98,11 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic { * carries. All others, we assume every byte could be tainted by any other byte in the vector, * so we union and broadcast. */ + @Override + public TaintVec unaryOp(PcodeOp op, TaintVec in1) { + return PcodeArithmetic.super.unaryOp(op, in1).withOp(op); + } + @Override public TaintVec unaryOp(int opcode, int sizeout, int sizein1, TaintVec in1) { return switch (opcode) { @@ -128,35 +133,20 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic { switch (op.getOpcode()) { case PcodeOp.INT_XOR, PcodeOp.INT_SUB, PcodeOp.BOOL_XOR -> { 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); - } - - /** - * {@inheritDoc} - * - *

- * 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) { + int sizein2 = op.getInput(1).getSize(); + int sizeout = op.getOutput().getSize(); + return switch (op.getOpcode()) { case PcodeOp.BOOL_AND, PcodeOp.BOOL_OR, PcodeOp.BOOL_XOR, PcodeOp.INT_AND, // PcodeOp.INT_OR, PcodeOp.INT_XOR -> { - yield in1.zipUnion(in2); + yield in1.zipUnion(in2).withOp(op); } case PcodeOp.INT_ADD, PcodeOp.INT_SUB -> { TaintVec temp = in1.zipUnion(in2); - yield temp.setCascade(endian.isBigEndian()); + yield temp.setCascade(endian.isBigEndian()).withOp(op); } case PcodeOp.INT_SLESS, PcodeOp.INT_SLESSEQUAL, // PcodeOp.INT_LESS, PcodeOp.INT_LESSEQUAL, // @@ -164,30 +154,42 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic { PcodeOp.FLOAT_LESS, PcodeOp.FLOAT_LESSEQUAL, // PcodeOp.FLOAT_EQUAL, PcodeOp.FLOAT_NOTEQUAL -> { TaintSet temp = in1.union().union(in2.union()); - yield TaintVec.copies(temp, sizeout); + yield TaintVec.copies(temp, sizeout).withOp(op); } case PcodeOp.PIECE -> { TaintVec temp = in1.extended(sizeout, endian.isBigEndian(), false); 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 -> { - TaintVec temp = in1.zipUnion(in2); - yield temp.setCopies(temp.union()); + TaintVec temp = in1.zipUnion(in2).truncated(sizeout, endian.isBigEndian()); + 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} * *

* 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 public TaintVec modBeforeStore(int sizeinOffset, AddressSpace space, TaintVec inOffset, int sizeinValue, TaintVec inValue) { - return inValue.tagIndirectWrite(inOffset); + throw new RuntimeException("Not supported"); } /** @@ -196,10 +198,16 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic { *

* 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 public TaintVec modAfterLoad(int sizeinOffset, AddressSpace space, TaintVec inOffset, int sizeinValue, TaintVec inValue) { - return inValue.tagIndirectRead(inOffset); + throw new RuntimeException("Not supported"); } /** diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeUseropLibrary.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeUseropLibrary.java index d269987ffc..386d6e2cf9 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeUseropLibrary.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeUseropLibrary.java @@ -19,7 +19,11 @@ import java.util.Set; import org.apache.commons.lang3.tuple.Pair; +import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor; 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.trace.model.time.schedule.TraceSchedule; @@ -59,8 +63,15 @@ public class TaintPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary taint_var(Pair in) { - return Pair.of(in.getLeft(), in.getRight().eachUnion(nextVar())); + public Pair taint_var(Pair 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()); + } + + return Pair.of(in.getLeft(), in.getRight().eachUnion(nextVar()).withOp(op)); } /** @@ -73,12 +84,22 @@ public class TaintPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary taint_arr(Pair in) { + public Pair taint_arr(Pair 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(); - 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); } + } diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPieceHandler.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPieceHandler.java index e5ec000a19..61c17dd893 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPieceHandler.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/state/TaintPieceHandler.java @@ -78,6 +78,10 @@ public class TaintPieceHandler extends AbstractPropertyBasedPieceHandler piece, AddressSetView limit, 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()); if (limit.contains(range.getMaxAddress(), range.getMaxAddress())) { piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(), @@ -115,7 +119,11 @@ public class TaintPieceHandler extends AbstractPropertyBasedPieceHandler taints = new TreeMap<>(Long::compareUnsigned); + protected final NavigableMap ops = new TreeMap<>(Long::compareUnsigned); public TaintSpace(AddressSpace space, TaintPcodeExecutorStatePiece piece) { this.space = space; @@ -59,6 +61,7 @@ public class TaintSpace { * @param cb callbacks to receive emulation events */ public void set(long offset, TaintVec val, PcodeStateCallbacks cb) { + ops.put(offset, val.getOriginatingOp()); for (int i = 0; i < val.length; i++) { TaintSet s = val.get(i); /* @@ -148,7 +151,8 @@ public class TaintSpace { while (taints.get(end) != null) { 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); return Map.entry(offset, vec); } diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/model/TaintVec.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/model/TaintVec.java index eade39ed16..96629e020f 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/model/TaintVec.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/model/TaintVec.java @@ -20,6 +20,8 @@ import java.util.function.BinaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; +import ghidra.program.model.pcode.PcodeOp; + /** * A mutable, but fixed-size, buffer of taint sets * @@ -33,8 +35,15 @@ import java.util.stream.Stream; */ 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 List setsView; public final int length; + private final PcodeOp originatingOp; - private TaintVec(TaintSet[] sets) { + private TaintVec(TaintSet[] sets, PcodeOp op) { this.sets = sets; this.setsView = Collections.unmodifiableList(Arrays.asList(sets)); this.length = sets.length; + this.originatingOp = op; } /** @@ -92,7 +103,17 @@ public class TaintVec { * @param length the 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 @@ -573,4 +594,22 @@ public class TaintVec { } 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); + } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java index e2f6a554c3..7b809682e8 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java @@ -29,6 +29,7 @@ public class TaintLabel { private HighFunction hfun; private HighVariable hvar; private Varnode vnode; + private Address vnAddr; private boolean active; private String label; private boolean isGlobal = false; @@ -40,7 +41,7 @@ public class TaintLabel { private int size = 0; public TaintLabel(MarkType mtype, ClangToken token) throws PcodeException { - + HighVariable highVar = token.getHighVariable(); if (highVar == null) { hfun = token.getClangFunction().getHighFunction(); @@ -54,22 +55,24 @@ public class TaintLabel { } 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(); if (high instanceof HighLocal) { 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.token = token; this.fname = fn; @@ -85,6 +88,41 @@ public class TaintLabel { 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() { return this.clangLine; } @@ -155,7 +193,7 @@ public class TaintLabel { public void activate() { active = true; } - + public int getSize() { return size; } @@ -231,12 +269,12 @@ public class TaintLabel { } return true; } - + public Address getVarnodeAddress() { - if (vnode != null) { - return vnode.getAddress(); - } - return null; + return vnAddr; } + public Varnode getVnode() { + return vnode; + } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java index 41de9cdaab..06dc41f031 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java @@ -220,6 +220,9 @@ public class SarifTaintResultHandler extends SarifResultHandler { for (int row : selected) { Map r = tableProvider.getRow(row); String kind = (String) r.get("kind"); + if (kind == null) { + continue; + } if (kind.equals("member") || kind.startsWith("path ")) { getTaintedInstruction(map, r); } diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java index 9192e8a5d3..a8ec089634 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java @@ -268,7 +268,7 @@ public class SarifUtils { } addr = subparts[0]; } - return program.getAddressFactory().getAddress(addr); + return addr == null ? null : program.getAddressFactory().getAddress(addr); } public static List

extractFQNameAddrPair(Program program, String fqname) { diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ExtLogicalLocation.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ExtLogicalLocation.java index f8a43dccf6..734f0cc474 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/export/ExtLogicalLocation.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ExtLogicalLocation.java @@ -31,7 +31,7 @@ public class ExtLogicalLocation implements IsfObject { this.kind = "variable"; this.decoratedName = op; this.fullyQualifiedName = location + ":" + name; - this.uri = function.getProgram().getExecutablePath(); + this.uri = function == null ? "UNKNOWN" : function.getProgram().getExecutablePath(); } public String getName() { diff --git a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/sys/EmuSyscallLibrary.java b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/sys/EmuSyscallLibrary.java index ed15729210..f6a6268091 100644 --- a/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/sys/EmuSyscallLibrary.java +++ b/Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/sys/EmuSyscallLibrary.java @@ -30,6 +30,7 @@ import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; +import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.*; @@ -167,7 +168,7 @@ public interface EmuSyscallLibrary extends PcodeUseropLibrary { @Override public void execute(PcodeExecutor executor, PcodeUseropLibrary library, - Varnode outVar, List inVars) { + PcodeOp op, Varnode outVar, List inVars) { syslib.syscall(executor, library); } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java index 269ba3c924..cd7a58858f 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java @@ -31,6 +31,7 @@ import ghidra.pcode.memstate.MemoryBank; import ghidra.pcode.memstate.MemoryState; import ghidra.program.model.address.Address; import ghidra.program.model.lang.*; +import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; import ghidra.util.Msg; @@ -121,7 +122,7 @@ public class ModifiedPcodeThread extends DefaultPcodeThread { @Override public void execute(PcodeExecutor executor, PcodeUseropLibrary library, - Varnode outVar, List inVars) { + PcodeOp op, Varnode outVar, List inVars) { behavior.evaluate(emulate, outVar, inVars.toArray(Varnode[]::new)); } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/auxiliary/AuxPcodeEmulator.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/auxiliary/AuxPcodeEmulator.java index 86e76ba505..fa96b99871 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/auxiliary/AuxPcodeEmulator.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/auxiliary/AuxPcodeEmulator.java @@ -67,7 +67,8 @@ public abstract class AuxPcodeEmulator extends AbstractPcodeMachine> createThreadStubLibrary() { - return getPartsFactory().createLocalUseropStub(this); + return super.createThreadStubLibrary() + .compose(getPartsFactory().createLocalUseropStub(this)); } @Override diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/analysis/JitDataFlowUseropLibrary.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/analysis/JitDataFlowUseropLibrary.java index 313319f6e0..d6e42cbe6b 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/analysis/JitDataFlowUseropLibrary.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/analysis/JitDataFlowUseropLibrary.java @@ -102,7 +102,7 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary { @Override public void execute(PcodeExecutor executor, PcodeUseropLibrary library, - Varnode outVar, List inVars) { + PcodeOp op, Varnode outVar, List inVars) { throw new AssertionError(); } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/decode/DecoderUseropLibrary.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/decode/DecoderUseropLibrary.java index 9337a09f6f..9b52b8dd9e 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/decode/DecoderUseropLibrary.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/jit/decode/DecoderUseropLibrary.java @@ -79,7 +79,7 @@ public class DecoderUseropLibrary extends AnnotatedPcodeUseropLibrary { @Override public void execute(PcodeExecutor executor, PcodeUseropLibrary library, - Varnode outVar, List inVars) { + PcodeOp op, Varnode outVar, List inVars) { throw new AssertionError(); } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AnnotatedPcodeUseropLibrary.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AnnotatedPcodeUseropLibrary.java index 455547713b..747a934edf 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AnnotatedPcodeUseropLibrary.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AnnotatedPcodeUseropLibrary.java @@ -29,6 +29,7 @@ import org.apache.commons.lang3.reflect.TypeUtils; import ghidra.pcode.exec.PcodeArithmetic.Purpose; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; import utilities.util.AnnotationUtilities; @@ -92,6 +93,17 @@ public abstract class AnnotatedPcodeUseropLibrary implements PcodeUseropLibra void setPos(AnnotatedPcodeUseropDefinition opdef, int 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, @@ -243,6 +255,7 @@ public abstract class AnnotatedPcodeUseropLibrary implements PcodeUseropLibra private int posState = -1; private int posLib = -1; private int posOut = -1; + private int posOp = -1; public AnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary library, Type opType, Lookup lookup, Method method, PcodeUserop annot) { @@ -290,7 +303,7 @@ public abstract class AnnotatedPcodeUseropLibrary implements PcodeUseropLibra @Override public void execute(PcodeExecutor executor, PcodeUseropLibrary library, - Varnode outVar, List inVars) { + PcodeOp op, Varnode outVar, List inVars) { validateInputs(inVars); PcodeExecutorStatePiece state = executor.getState(); @@ -308,6 +321,9 @@ public abstract class AnnotatedPcodeUseropLibrary implements PcodeUseropLibra if (posOut != -1) { args.set(posOut, outVar); } + if (posOp != -1) { + args.set(posOp, op); + } placeInputs(executor, args, inVars); try { @@ -792,6 +808,17 @@ public abstract class AnnotatedPcodeUseropLibrary implements PcodeUseropLibra public @interface OpOutput { } + /** + * An annotation to receive the CALLOTHER p-code op into a parameter + * + *

+ * The annotated parameter must have type {@link PcodeOp}). + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + public @interface OpOp { + } + protected Map> ops = new HashMap<>(); private Map> unmodifiableOps = Collections.unmodifiableMap(ops); diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibrary.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibrary.java index 817fa9f6ea..fec9200b81 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibrary.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibrary.java @@ -131,8 +131,25 @@ public interface PcodeUseropLibrary { * @param inVars the input varnodes as ordered in the source. * @see AnnotatedPcodeUseropLibrary.AnnotatedPcodeUseropDefinition */ - void execute(PcodeExecutor executor, PcodeUseropLibrary library, Varnode outVar, - List inVars); + default void execute(PcodeExecutor executor, PcodeUseropLibrary library, + Varnode outVar, List 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 executor, PcodeUseropLibrary library, PcodeOp op, + Varnode outVar, List inVars); /** * Invoke/execute the raw userop. @@ -147,7 +164,7 @@ public interface PcodeUseropLibrary { * @param op the {@link PcodeOp#CALLOTHER} op */ default void execute(PcodeExecutor executor, PcodeUseropLibrary library, PcodeOp op) { - execute(executor, library, op.getOutput(), + execute(executor, library, op, op.getOutput(), Arrays.asList(op.getInputs()).subList(1, op.getNumInputs())); } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighPcodeUseropDefinition.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighPcodeUseropDefinition.java index 051236009e..86d3688ea7 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighPcodeUseropDefinition.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/SleighPcodeUseropDefinition.java @@ -20,6 +20,7 @@ import java.util.*; import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition; +import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; /** @@ -170,7 +171,7 @@ public class SleighPcodeUseropDefinition implements PcodeUseropDefinition @Override public void execute(PcodeExecutor executor, PcodeUseropLibrary library, - Varnode outArg, List inArgs) { + PcodeOp op, Varnode outArg, List inArgs) { PcodeProgram program = programFor(outArg, inArgs, library); executor.execute(program, library); } diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html index e0b81936f6..07963ade27 100644 --- a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html +++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html @@ -1149,7 +1149,7 @@ emulator:

Anything more than that would require completely custom providers, diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md index 14310ec9d9..85dc9de6fe 100644 --- a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md +++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md @@ -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 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. \ No newline at end of file