From efc18fad3b540f62c93093c75b3bbf8899c2d41e Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:24:15 -0500 Subject: [PATCH] GP-5290: from review GP-5290: lose the interface GP-5290: mid-review GP-5290: some cleanup GP-5290: state updates in motion GP-5290: basically working GP-5290: fixes for abstract state; set size GP-5290: first pass --- Ghidra/Debug/Debugger/build.gradle | 1 + .../core/debug/taint/AngrTaintState.java | 234 ++++++++++++++++++ .../data/ExtensionPoint.manifest | 1 + .../DecompilerTaint/DecompilerTaint.html | 12 +- .../decompiler/taint/AbstractTaintState.java | 86 ++++--- .../core/decompiler/taint/TaintLabel.java | 36 ++- .../core/decompiler/taint/TaintOptions.java | 35 ++- .../core/decompiler/taint/TaintPlugin.java | 20 +- .../core/decompiler/taint/TaintProvider.java | 47 ++-- .../core/decompiler/taint/TaintState.java | 41 ++- .../actions/TaintAbstractQueryAction.java | 19 +- .../taint/actions/TaintClearAction.java | 10 +- .../taint/actions/TaintGateAction.java | 7 +- .../taint/actions/TaintLoadAction.java | 6 +- .../taint/actions/TaintQueryAction.java | 7 +- .../taint/actions/TaintQueryCustomAction.java | 4 +- .../actions/TaintQueryDefaultAction.java | 4 +- .../taint/actions/TaintSetSizeAction.java | 97 ++++++++ .../taint/actions/TaintSinkAction.java | 7 +- .../actions/TaintSinkBySymbolAction.java | 7 +- .../taint/actions/TaintSliceTreeAction.java | 7 +- .../taint/actions/TaintSourceAction.java | 7 +- .../actions/TaintSourceBySymbolAction.java | 7 +- ...ntStateCTADL.java => CTADLTaintState.java} | 16 +- 24 files changed, 605 insertions(+), 113 deletions(-) create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/AngrTaintState.java create mode 100644 Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSetSizeAction.java rename Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/{TaintStateCTADL.java => CTADLTaintState.java} (94%) diff --git a/Ghidra/Debug/Debugger/build.gradle b/Ghidra/Debug/Debugger/build.gradle index e2ca5133a6..ef3f92f130 100644 --- a/Ghidra/Debug/Debugger/build.gradle +++ b/Ghidra/Debug/Debugger/build.gradle @@ -29,6 +29,7 @@ dependencies { api project(':Base') api project(':ByteViewer') api project(':Decompiler') + api project(':DecompilerDependent') api project(':FunctionGraph') api project(':ProposedUtils') diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/AngrTaintState.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/AngrTaintState.java new file mode 100644 index 0000000000..748003b4f8 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/taint/AngrTaintState.java @@ -0,0 +1,234 @@ +/* ### + * 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.nio.ByteBuffer; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; + +import ghidra.app.decompiler.ClangFuncNameToken; +import ghidra.app.plugin.core.decompiler.taint.*; +import ghidra.app.script.GhidraScript; +import ghidra.app.services.ConsoleService; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.debug.api.tracemgr.DebuggerCoordinates; +import ghidra.program.model.address.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.CodeUnit; +import ghidra.program.model.listing.Program; +import ghidra.program.model.pcode.HighParam; +import ghidra.trace.model.Trace; +import ghidra.trace.model.memory.*; +import ghidra.trace.model.thread.TraceThread; +import ghidra.util.Msg; +import ghidra.util.NumericUtilities; + +/** + * Container for all the decompiler elements the users "selects" via the menu. + * This data is used to build queries. + */ +public class AngrTaintState extends AbstractTaintState { + + private DebuggerTraceManagerService traceManager; + + public AngrTaintState(TaintPlugin plugin) { + super(plugin); + ENGINE_NAME = "angr"; + usesIndex = false; + } + + private Address start; + + @Override + public void buildQuery(List paramList, Path engine, File indexDBFile, + String indexDirectory) { + paramList.add("python"); + paramList.add(engine.toString()); + } + + @Override + public void buildIndex(List paramList, String engine_path, String facts_path, + String indexDirectory) { + // Unused + } + + @Override + public GhidraScript getExportScript(ConsoleService console, boolean perFunction) { + return null; + } + + @Override + protected void writeHeader(PrintWriter writer) { + Program currentProgram = plugin.getCurrentProgram(); + start = currentProgram.getMaxAddress().getNewAddress(Integer.MAX_VALUE); + writer.println("{"); + writer.println("\t\"binary_file\":\"" + currentProgram.getExecutablePath() + "\","); + writer.println("\t\"base_address\":\"" + + Long.toHexString(currentProgram.getImageBase().getOffset()) + "\","); + } + + /* + * NOTE: This is the only method used now for Sources and Sinks. + */ + @Override + protected void writeRule(PrintWriter writer, TaintLabel mark, boolean isSource) { + Program currentProgram = plugin.getCurrentProgram(); + Address addr = mark.getAddress(); + if (isSource) { + if (start.getOffset() > addr.getOffset()) { + start = addr; + } + } + else { + writer.println("\t\"find_address\":\"" + Long.toHexString(addr.getOffset()) + "\","); + return; + } + + if (isSource) { + if (mark.getToken() instanceof ClangFuncNameToken) { + CodeUnit cu = currentProgram.getListing().getCodeUnitAt(addr); + writer.println("\t\"hooks\":[{\"" + Long.toHexString(addr.getOffset()) + + "\":{\"length\":\"" + cu.getLength() + "\"}}],"); + return; + } + if (mark.getHighVariable() instanceof HighParam hp) { + writer.println( + "\t\"arguments\":{\"" + hp.getSlot() + "\":\"" + mark.getSize() + "\"},"); + return; + } + Address vaddr = mark.getVarnodeAddress(); + if (vaddr != null) { + if (vaddr.isRegisterAddress()) { + Language language = currentProgram.getLanguage(); + Register register = language.getRegister(vaddr, mark.getSize()); + writer.println("\t\"regs_vals\":{\"" + register.getName() + "\":\"" + + mark.getSize() + "\"},"); + } + if (vaddr.isMemoryAddress()) { + writer.println("\t\"vectors\":{\"" + vaddr + "\":\"" + mark.getSize() + "\"},"); + } + } + } + + } + + @Override + public void writeGate(PrintWriter writer, TaintLabel mark) { + Address addr = mark.getAddress(); + writer.println("\t\"avoid_address\":\"" + Long.toHexString(addr.getOffset()) + "\","); + } + + @Override + protected void writeFooter(PrintWriter writer) { + writeState(writer); + writer.println("\t\"blank_state\":\"" + Long.toHexString(start.getOffset()) + "\","); + writer.println("\t\"auto_load_libs\":false"); + writer.println("}"); + } + + private void writeState(PrintWriter writer) { + if (traceManager == null) { + traceManager = plugin.getTool().getService(DebuggerTraceManagerService.class); + if (traceManager == null) { + return; + } + } + DebuggerCoordinates current = traceManager.getCurrent(); + Trace trace = current.getTrace(); + if (trace == null) { + return; + } + + TraceMemoryManager memoryManager = trace.getMemoryManager(); + Collection allRegions = + memoryManager.getRegionsAtSnap(current.getSnap()); + for (TraceMemoryRegion region : allRegions) { + AddressRange range = region.getRange(); + Address min = range.getMinAddress(); + int len = (int) range.getLength(); + byte[] bytes = new byte[len]; + int nread = + memoryManager.getBytes(current.getSnap(), min, ByteBuffer.wrap(bytes)); + if (nread == len) { + writer.println("\t\"mem_store\":{\"" + min + "\":\"" + convert(bytes) + "\"},"); + } + else { + Msg.error(this, "Requested " + len + " but returned " + nread); + } + } + + Program currentProgram = plugin.getCurrentProgram(); + List registers = currentProgram.getLanguage().getRegisters(); + TraceThread thread = current.getThread(); + if (thread == null) { + return; + } + TraceMemorySpace regs = + memoryManager.getMemoryRegisterSpace(thread, current.getFrame(), true); + if (regs == null) { + return; + } + for (Register r : registers) { + TraceMemoryState state = regs.getState(current.getPlatform(), current.getSnap(), r); + if (!state.equals(TraceMemoryState.KNOWN)) { + continue; + } + RegisterValue value = regs.getValue(current.getSnap(), r); + byte[] bytes = value.getUnsignedValue().toByteArray(); + String bytestr = convert(bytes); + if (!bytestr.equals("") && !bytestr.equals("00")) { + writer.println( + "\t\"regs_vals\":{\"" + r.getName() + "\":\"0x" + convert(bytes) + "\"},"); + } + } + } + + private String convert(byte[] bytes) { + return NumericUtilities.convertBytesToString(bytes); + } + + + @Override + protected void readQueryResultsIntoDataFrame(Program program, InputStream is) { + + Program currentProgram = plugin.getCurrentProgram(); + String line = null; + taintAddressSet.clear(); + taintVarnodeMap.clear(); + + try { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); + + while ((line = bufferedReader.readLine()) != null) { + if (line.startsWith("t:")) { + String addrStr = line.substring(line.indexOf(":") + 1); + Address address = currentProgram.getMinAddress().getAddress(addrStr); + taintAddressSet.add(address); + } + else { + System.err.println(line); + } + } + bufferedReader.close(); + } + catch (IOException | AddressFormatException e) { + plugin.consoleMessage("IO Error Reading Query Results from Process: " + e.getMessage()); + } + } + +} diff --git a/Ghidra/Features/DecompilerDependent/data/ExtensionPoint.manifest b/Ghidra/Features/DecompilerDependent/data/ExtensionPoint.manifest index 97ef9712cd..433ba3dd79 100644 --- a/Ghidra/Features/DecompilerDependent/data/ExtensionPoint.manifest +++ b/Ghidra/Features/DecompilerDependent/data/ExtensionPoint.manifest @@ -1 +1,2 @@ DataTypeReferenceFinder +TaintState \ No newline at end of file diff --git a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html index 994eec89de..e5df84a3f7 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html +++ b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html @@ -151,6 +151,14 @@

+
+

+ Match on Fields

+

+ Use all access paths, including field references, for sink/source variables. +

+
+

Output Format

@@ -163,9 +171,9 @@

- Match on Fields

+ Query Engine

- Use all access paths, including field references, for sink/source variables. + The engine to be used for formatting queries (e.g. ctadl, angr).

diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java index 725821c531..f33d9ddc5a 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java @@ -43,22 +43,22 @@ public abstract class AbstractTaintState implements TaintState { public static String ENGINE_NAME = ""; - private Set sources = new HashSet<>(); - private Set sinks = new HashSet<>(); - private Set gates = new HashSet<>(); + protected Set sources = new HashSet<>(); + protected Set sinks = new HashSet<>(); + protected Set gates = new HashSet<>(); // Sets used for highlighting. - private AddressSet taintAddressSet = new AddressSet(); - private Map> taintVarnodeMap = new HashMap<>(); + protected AddressSet taintAddressSet = new AddressSet(); + protected Map> taintVarnodeMap = new HashMap<>(); private AddressSet deltaAddressSet = new AddressSet(); private Map> deltaVarnodeMap = new HashMap<>(); - private SarifSchema210 currentQueryData; + protected SarifSchema210 currentQueryData; protected TaintOptions taintOptions; - private TaintPlugin plugin; - + protected TaintPlugin plugin; + protected boolean usesIndex = true; private boolean cancellation; private TaskType taskType; @@ -74,10 +74,14 @@ public abstract class AbstractTaintState implements TaintState { public abstract void buildIndex(List param_list, String engine_path, String facts_path, String index_path); + protected abstract void writeHeader(PrintWriter writer); + protected abstract void writeRule(PrintWriter writer, TaintLabel mark, boolean isSource); protected abstract void writeGate(PrintWriter writer, TaintLabel mark); + protected abstract void writeFooter(PrintWriter writer); + @Override public boolean wasCancelled() { return this.cancellation; @@ -122,6 +126,17 @@ public abstract class AbstractTaintState implements TaintState { return tlabel; } + @Override + public TaintLabel getLabelForToken(MarkType type, ClangToken token) { + Set marks = getTaintLabels(type); + for (TaintLabel existingLabel : marks) { + if (token.equals(existingLabel.getToken())) { + return existingLabel; + } + } + return null; + } + /** * Predicate indicating the presence of one or more sources in the source set; * this is used to determine state validity. @@ -174,7 +189,7 @@ public abstract class AbstractTaintState implements TaintState { public boolean writeQueryFile(File queryTextFile) throws Exception { PrintWriter writer = new PrintWriter(queryTextFile); - writer.println("#include \"pcode/taintquery.dl\""); + writeHeader(writer); for (TaintLabel mark : sources) { if (mark.isActive()) { @@ -182,8 +197,6 @@ public abstract class AbstractTaintState implements TaintState { } } - writer.println(""); - for (TaintLabel mark : sinks) { if (mark.isActive()) { // CAREFUL note the "false" @@ -192,7 +205,6 @@ public abstract class AbstractTaintState implements TaintState { } if (!gates.isEmpty()) { - writer.println(""); for (TaintLabel mark : gates) { if (mark.isActive()) { writeGate(writer, mark); @@ -200,6 +212,8 @@ public abstract class AbstractTaintState implements TaintState { } } + writeFooter(writer); + writer.flush(); writer.close(); @@ -221,7 +235,7 @@ public abstract class AbstractTaintState implements TaintState { return false; } - List param_list = new ArrayList(); + List paramList = new ArrayList(); File queryFile = null; taintOptions = plugin.getOptions(); @@ -229,37 +243,39 @@ public abstract class AbstractTaintState implements TaintState { // Make sure we can access and execute the engine binary. Path engine = Path.of(taintOptions.getTaintEnginePath()); - File engine_file = engine.toFile(); + File engineFile = engine.toFile(); - if (!engine_file.exists() || !engine_file.canExecute()) { + if (!engineFile.exists()) { plugin.consoleMessage("The " + getName() + " binary (" + - engine_file.getCanonicalPath() + ") cannot be found or executed."); - engine_file = getFilePath(taintOptions.getTaintEnginePath(), + engineFile.getCanonicalPath() + ") cannot be found or executed."); + engineFile = getFilePath(taintOptions.getTaintEnginePath(), "Select the " + getName() + " binary"); - if (engine_file == null) { + if (engineFile == null) { plugin.consoleMessage( "No " + getName() + " engine has been specified; exiting query function."); return false; } } - plugin.consoleMessage("Using " + getName() + " binary: " + engine_file.toString()); + plugin.consoleMessage("Using " + getName() + " binary: " + engineFile.toString()); - Path index_directory = Path.of(taintOptions.getTaintOutputDirectory()); + Path indexDirectory = Path.of(taintOptions.getTaintOutputDirectory()); Path indexDBPath = Path.of(taintOptions.getTaintOutputDirectory(), taintOptions.getTaintIndexDBName(program.getName())); - File indexDBFile = indexDBPath.toFile(); - plugin.consoleMessage("Attempting to use index: " + indexDBFile.toString()); - - if (!indexDBFile.exists()) { - plugin.consoleMessage("The index database for the binary named: " + - program.getName() + " does not exist; create it first."); - return false; + + if (usesIndex) { + plugin.consoleMessage("Attempting to use index: " + indexDBFile.toString()); + + if (!indexDBFile.exists()) { + plugin.consoleMessage("The index database for the binary named: " + + program.getName() + " does not exist; create it first."); + return false; + } + + plugin.consoleMessage("Using index database: " + indexDBFile); } - plugin.consoleMessage("Using index database: " + indexDBFile); - switch (queryType) { case SRCSINK: // Generate a datalog query file based on the selected source, sink, etc. data. @@ -281,7 +297,7 @@ public abstract class AbstractTaintState implements TaintState { plugin.consoleMessage("Unknown query type."); } - buildQuery(param_list, engine, indexDBFile, index_directory.toString()); + buildQuery(paramList, engine, indexDBFile, indexDirectory.toString()); if (queryType.equals(QueryType.SRCSINK) || queryType.equals(QueryType.CUSTOM)) { // The datalog that specifies the query. @@ -295,12 +311,12 @@ public abstract class AbstractTaintState implements TaintState { return false; } } - param_list.add(queryFile.getAbsolutePath()); + paramList.add(queryFile.getAbsolutePath()); } - Msg.info(this, "Query Param List: " + param_list.toString()); + Msg.info(this, "Query Param List: " + paramList.toString()); try { - ProcessBuilder pb = new ProcessBuilder(param_list); + ProcessBuilder pb = new ProcessBuilder(paramList); pb.directory(new File(taintOptions.getTaintOutputDirectory())); pb.redirectError(Redirect.INHERIT); Process p = pb.start(); @@ -327,9 +343,10 @@ public abstract class AbstractTaintState implements TaintState { } /** + * @param prog current program if needed * @param is the input stream (SARIF json) from the process builder that runs the engine */ - private void readQueryResultsIntoDataFrame(Program program, InputStream is) { + protected void readQueryResultsIntoDataFrame(Program prog, InputStream is) { StringBuilder sb = new StringBuilder(); String line = null; @@ -534,6 +551,7 @@ public abstract class AbstractTaintState implements TaintState { return plugin.getOptions(); } + @Override public String getName() { return ENGINE_NAME; } 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 c7c8bd90a3..33edbb23a3 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 @@ -28,6 +28,7 @@ public class TaintLabel { private String fname; private HighFunction hfun; private HighVariable hvar; + private Varnode vnode; private boolean active; private String label; private boolean isGlobal = false; @@ -36,8 +37,10 @@ public class TaintLabel { // TODO: This is not a good identifier since it could change during re work! private Address addr; private ClangLine clangLine; + private int size = 0; public TaintLabel(MarkType mtype, ClangToken token) throws PcodeException { + HighVariable highVar = token.getHighVariable(); if (highVar == null) { hfun = token.getClangFunction().getHighFunction(); @@ -50,25 +53,31 @@ public class TaintLabel { } } - Varnode exactSpot = token.getVarnode(); - if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl - HighVariable high = exactSpot.getHigh(); + this.vnode = token.getVarnode(); + if (vnode != null) { // The user pointed at a particular usage, not just the vardecl + HighVariable high = vnode.getHigh(); if (high instanceof HighLocal) { - highVar = hfun.splitOutMergeGroup(high, exactSpot); + highVar = hfun.splitOutMergeGroup(high, vnode); } } String fn = token instanceof ClangFuncNameToken ftoken ? ftoken.getText() : hfun.getFunction().getName(); PcodeOp pcodeOp = token.getPcodeOp(); - Address target = pcodeOp == null ? null : pcodeOp.getSeqnum().getTarget(); - + Address target = pcodeOp == null ? hfun.getFunction().getEntryPoint() : pcodeOp.getSeqnum().getTarget(); + if (vnode == null && pcodeOp != null) { + vnode = pcodeOp.getOutput(); + } + this.mtype = mtype; this.token = token; this.fname = fn; this.hvar = highVar; this.active = true; this.addr = target; + if (hvar != null) { + size = hvar.getSize(); + } this.clangLine = token.getLineParent(); // Initial label is one of SOURCE, SINK, or GATE @@ -145,6 +154,14 @@ public class TaintLabel { public void activate() { active = true; } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } public void toggle() { active = !active; @@ -213,5 +230,12 @@ public class TaintLabel { } return true; } + + public Address getVarnodeAddress() { + if (vnode != null) { + return vnode.getAddress(); + } + return null; + } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java index 1f32ddf8b6..d249163c0b 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java @@ -45,6 +45,7 @@ public class TaintOptions { /* The default name of the index database file. */ public final static String OP_KEY_TAINT_DB = "Taint.Query.Index"; + public final static String OP_KEY_TAINT_QUERY_ENGINE = "Taint.Query Engine"; public final static String OP_KEY_TAINT_QUERY_DIRECTION = "Taint.Force Direction"; public final static String OP_KEY_TAINT_QUERY_OUTPUT_FORM = "Taint.Output Format"; /* Color used in the decompiler to highlight taint. */ @@ -68,13 +69,14 @@ public class TaintOptions { new GColor("color.bg.listing.highlighter.default"); private final static Highlighter TAINT_HIGHLIGHT_STYLE_DEFAULT = Highlighter.DEFAULT; - private String taintEngine; + private String taintEnginePath; private String taintFactsDir; private String taintOutputDir; private String taintQuery; private String taintDB; + private String taintQueryEngine; private TaintDirection taintQueryDirection; private TaintFormat taintQueryOutputForm; @@ -106,7 +108,7 @@ public class TaintOptions { public TaintOptions(TaintProvider provider) { taintProvider = provider; - taintEngine = DEFAULT_TAINT_ENGINE_PATH; + taintEnginePath = DEFAULT_TAINT_ENGINE_PATH; taintFactsDir = DEFAULT_TAINT_FACTS_DIR; taintOutputDir = DEFAULT_TAINT_OUTPUT_DIR; taintQuery = DEFAULT_TAINT_QUERY; @@ -129,7 +131,11 @@ public class TaintOptions { opt.registerOption(OP_KEY_TAINT_QUERY_OUTPUT_FORM, TaintFormat.ALL, new HelpLocation(HelpTopics.DECOMPILER, "Taint Output Type"), - "The type of Source-Sink query output (e.g., sarif, summary, text"); + "The type of Source-Sink query output (e.g., sarif, summary, text)"); + + opt.registerOption(OP_KEY_TAINT_QUERY_ENGINE, "", + new HelpLocation(HelpTopics.DECOMPILER, "Taint Query Engine"), + "The query engine (e.g., angr, ctadl)"); opt.registerOption(OP_KEY_TAINT_ENGINE_PATH, DEFAULT_TAINT_ENGINE_PATH, new HelpLocation(HelpTopics.DECOMPILER, "Taint Engine Directory"), @@ -176,7 +182,17 @@ public class TaintOptions { */ public void grabFromToolAndProgram(Plugin ownerPlugin, ToolOptions opt, Program program) { - taintEngine = opt.getString(OP_KEY_TAINT_ENGINE_PATH, ""); + String engine = opt.getString(OP_KEY_TAINT_QUERY_ENGINE, ""); + if (!engine.equals(taintQueryEngine)) { + TaintPlugin plugin = (TaintPlugin) ownerPlugin; + TaintState state = TaintState.newInstance(plugin, engine); + if (state != null) { + plugin.setTaintState(state); + taintQueryEngine = engine; + } + } + + taintEnginePath = opt.getString(OP_KEY_TAINT_ENGINE_PATH, ""); taintFactsDir = opt.getString(OP_KEY_TAINT_FACTS_DIR, ""); taintQuery = opt.getString(OP_KEY_TAINT_QUERY, ""); // taintQueryResultsFile = opt.getString(OP_KEY_TAINT_QUERY_RESULTS, ""); @@ -196,8 +212,12 @@ public class TaintOptions { return taintQueryOutputForm; } + public String getTaintEngineType() { + return taintQueryEngine; + } + public String getTaintEnginePath() { - return taintEngine; + return taintEnginePath; } public String getTaintFactsDirectory() { @@ -244,6 +264,11 @@ public class TaintOptions { return taintUseAllAccess; } + public void setTaintQueryEngine(String engine) { + this.taintQueryEngine = engine; + taintProvider.setOption(OP_KEY_TAINT_QUERY_ENGINE, engine); + } + public void setTaintOutputForm(TaintFormat form) { this.taintQueryOutputForm = form; taintProvider.setOption(OP_KEY_TAINT_QUERY_OUTPUT_FORM, form.getOptionString()); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java index 00e7ba157e..4dc83df83d 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java @@ -111,8 +111,11 @@ public class TaintPlugin extends ProgramPlugin implements TaintService { } public static enum TaintFormat { - ALL("all", "sarif+all"), GRAPHS("graphs", "sarif+graphs"), - INSTS("insts", "sarif+instructions"), PATHS("paths", "sarif"); + ALL("all", "sarif+all"), + GRAPHS("graphs", "sarif+graphs"), + INSTS("insts", "sarif+instructions"), + PATHS("paths", "sarif"), + NONE("none", "none"); private String label; private String optionString; @@ -175,7 +178,6 @@ public class TaintPlugin extends ProgramPlugin implements TaintService { public TaintPlugin(PluginTool tool) { super(tool); - state = TaintState.newInstance(this); taintProvider = new TaintProvider(this); taintDecompMarginProvider = new TaintDecompilerMarginProvider(this); createActions(); @@ -353,9 +355,11 @@ public class TaintPlugin extends ProgramPlugin implements TaintService { GhidraState ghidraState = new GhidraState(tool, null, currentProgram, currentLocation, currentHighlight, currentHighlight); GhidraScript exportScript = state.getExportScript(consoleService, false); - RunPCodeExportScriptTask export_task = - new RunPCodeExportScriptTask(tool, exportScript, ghidraState, consoleService); - tool.execute(export_task); + if (exportScript != null) { + RunPCodeExportScriptTask export_task = + new RunPCodeExportScriptTask(tool, exportScript, ghidraState, consoleService); + tool.execute(export_task); + } } @Override @@ -433,6 +437,10 @@ public class TaintPlugin extends ProgramPlugin implements TaintService { return state; } + public void setTaintState(TaintState state) { + this.state = state; + } + public TaintProvider getProvider() { return taintProvider; } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java index 34b605fdbe..c5bbccdbae 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java @@ -61,8 +61,6 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh private DecompilerProvider decompilerProvider; private Navigatable navigatable; - private TaintState state; - private DecompilerHighlighter highlighter; private TaintCTokenHighlighterPalette highlightPalette; @@ -84,7 +82,6 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh super(plugin.getTool(), "TaintProvider", plugin.getName(), DecompilerActionContext.class); this.plugin = plugin; this.taintOptions = new TaintOptions(this); - this.state = plugin.getTaintState(); this.cachedHighlightsByToken = new HashMap<>(); this.cachedHighlightByAddress = new HashMap<>(); this.highlightPalette = new TaintCTokenHighlighterPalette(256); @@ -108,34 +105,37 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh // These actions are only available in the drop-down window - TaintSourceAction taintSourceAction = new TaintSourceAction(plugin, state); + TaintSourceAction taintSourceAction = new TaintSourceAction(plugin); setGroupInfo(taintSourceAction, variableGroup, subGroupPosition++); TaintSourceBySymbolAction taintSourceBySymbolAction = - new TaintSourceBySymbolAction(plugin, state); + new TaintSourceBySymbolAction(plugin); setGroupInfo(taintSourceBySymbolAction, variableGroup, subGroupPosition++); - TaintSinkAction taintSinkAction = new TaintSinkAction(plugin, state); + TaintSinkAction taintSinkAction = new TaintSinkAction(plugin); setGroupInfo(taintSinkAction, variableGroup, subGroupPosition++); TaintSinkBySymbolAction taintSinkBySymbolAction = - new TaintSinkBySymbolAction(plugin, state); + new TaintSinkBySymbolAction(plugin); setGroupInfo(taintSinkBySymbolAction, variableGroup, subGroupPosition++); - TaintGateAction taintGateAction = new TaintGateAction(plugin, state); + TaintGateAction taintGateAction = new TaintGateAction(plugin); setGroupInfo(taintGateAction, variableGroup, subGroupPosition++); - TaintClearAction taintClearAction = new TaintClearAction(plugin, state); + TaintClearAction taintClearAction = new TaintClearAction(plugin); setGroupInfo(taintClearAction, variableGroup, subGroupPosition++); - // These actions have an icon and a drop-down menu option in the decompiler window. - TaintQueryAction taintQueryAction = new TaintQueryAction(plugin, state); - TaintQueryDefaultAction taintQueryDefaultAction = - new TaintQueryDefaultAction(plugin, state); - TaintQueryCustomAction taintQueryCustomAction = new TaintQueryCustomAction(plugin, state); - TaintLoadAction taintLoadAction = new TaintLoadAction(plugin, state); + TaintSetSizeAction taintSizeAction = new TaintSetSizeAction(plugin); + setGroupInfo(taintSizeAction, variableGroup, subGroupPosition++); - TaintSliceTreeAction taintSliceTreeAction = new TaintSliceTreeAction(plugin, state); + // These actions have an icon and a drop-down menu option in the decompiler window. + TaintQueryAction taintQueryAction = new TaintQueryAction(plugin); + TaintQueryDefaultAction taintQueryDefaultAction = + new TaintQueryDefaultAction(plugin); + TaintQueryCustomAction taintQueryCustomAction = new TaintQueryCustomAction(plugin); + TaintLoadAction taintLoadAction = new TaintLoadAction(plugin); + + TaintSliceTreeAction taintSliceTreeAction = new TaintSliceTreeAction(plugin); DockingAction taintLabelTableAction = new DockingAction("TaintShowLabels", TaintPlugin.HELP_LOCATION) { @@ -153,7 +153,8 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh @Override public boolean isEnabledForContext(ActionContext context) { - return state.hasMarks(); + TaintState state = plugin.getTaintState(); + return state == null ? false : state.hasMarks(); } }; @@ -175,6 +176,7 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh provider.addLocalAction(taintQueryCustomAction); provider.addLocalAction(taintLoadAction); provider.addLocalAction(taintClearAction); + provider.addLocalAction(taintSizeAction); } /** @@ -253,6 +255,10 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh * @param taskType subtract previous result */ public void setTaint(TaskType taskType) { + TaintState state = plugin.getTaintState(); + if (state == null) { + return; + } if (navigatable == null) { navigatable = tool.getService(CodeViewerService.class).getNavigatable(); } @@ -284,6 +290,11 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh } public boolean matchOn(ClangToken token) { + + TaintState state = plugin.getTaintState(); + if (state == null) { + return false; + } if (token instanceof ClangBreak || token instanceof ClangTypeToken || @@ -372,7 +383,7 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh Msg.info(this, "TaintProvider: clearTaint() - state clearTaint() and highligher apply highlights."); matchCount = 0; - state.clearTaint(); + plugin.getTaintState().clearTaint(); highlighter.clearHighlights(); cachedHighlightByAddress.clear(); cachedHighlightsByToken.clear(); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java index 3c1b256f76..dc1c3332bb 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java @@ -16,12 +16,12 @@ package ghidra.app.plugin.core.decompiler.taint; import java.io.File; +import java.lang.reflect.Constructor; import java.util.*; import com.contrastsecurity.sarif.SarifSchema210; import ghidra.app.decompiler.*; -import ghidra.app.plugin.core.decompiler.taint.ctadl.TaintStateCTADL; import ghidra.app.script.GhidraScript; import ghidra.app.services.ConsoleService; import ghidra.framework.plugintool.PluginTool; @@ -31,12 +31,18 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.*; import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.SymbolTable; +import ghidra.util.Msg; +import ghidra.util.classfinder.ClassSearcher; +import ghidra.util.classfinder.ExtensionPoint; /** * The interface for the methods that collect desired taint information from the decompiler window and store them * for construction of queries and indexing. + *

+ * NOTE: ALL TaintState CLASSES MUST END IN "TaintState". If not, + * the ClassSearcher will not find them. */ -public interface TaintState { +public interface TaintState extends ExtensionPoint { public enum MarkType { SOURCE, SINK, GATE @@ -50,10 +56,31 @@ public interface TaintState { SET_TAINT, SET_DELTA, APPLY_DELTA } - public static TaintState newInstance(TaintPlugin plugin) { - return new TaintStateCTADL(plugin); + public static TaintState newInstance(TaintPlugin plugin, String type) { + List> list = ClassSearcher.getClasses(TaintState.class) + .stream() + .toList(); + Class[] constructorArgumentTypes = {TaintPlugin.class}; + Object[] args = new Object[1]; + args[0] = plugin; + for (Class clazz : list) { + if (clazz.getName().toLowerCase().contains(type)) { + try { + Constructor constructor = clazz.getConstructor(constructorArgumentTypes); + Object obj = constructor.newInstance(plugin); + return TaintState.class.cast(obj); + } + catch (Exception e) { + throw new RuntimeException("Unable to instantiate TaintState"); + } + } + } + Msg.error(plugin, "No match for engine = "+type); + return null; } + public String getName(); + /** * Perform a Source-Sink query on the index database. * @@ -108,6 +135,10 @@ public interface TaintState { public GhidraScript getExportScript(ConsoleService console, boolean perFunction); + public void setTaskType(TaskType taskType); + + public TaintLabel getLabelForToken(MarkType type, ClangToken token); + public static String hvarName(ClangToken token) { HighVariable hv = token.getHighVariable(); HighFunction hf = @@ -159,6 +190,4 @@ public interface TaintState { return false; } - public void setTaskType(TaskType taskType); - } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java index ef6d284040..52ee73f957 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java @@ -20,6 +20,7 @@ import javax.swing.Icon; import docking.action.MenuData; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.*; +import ghidra.app.plugin.core.decompiler.taint.TaintPlugin.TaintFormat; import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; import ghidra.app.plugin.core.decompiler.taint.sarif.SarifTaintGraphRunHandler; import ghidra.framework.plugintool.PluginTool; @@ -37,7 +38,6 @@ import sarif.SarifService; public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAction { protected TaintPlugin plugin; - protected TaintState state; protected String desc; protected String title; @@ -45,14 +45,13 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc protected Icon executeTaintQueryIcon; protected QueryType queryType; - public TaintAbstractQueryAction(TaintPlugin plugin, TaintState state, String desc, String title, String cmd) { + public TaintAbstractQueryAction(TaintPlugin plugin, String desc, String title, String cmd) { super(cmd); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "Taint"+desc)); setMenuBarData(new MenuData(new String[] { "Source-Sink", getName() })); this.plugin = plugin; - this.state = state; this.desc = desc; this.title = title; } @@ -62,6 +61,9 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc */ @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } return true; } @@ -73,6 +75,7 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc Task defaultQueryTask = new Task(title, true, true, true, true) { @Override public void run(TaskMonitor monitor) { + TaintState state = plugin.getTaintState(); state.setCancellation(false); monitor.initialize(program.getFunctionManager().getFunctionCount()); state.queryIndex(program, tool, queryType); @@ -87,10 +90,14 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc // We still get a progress bar and option to cancel. tool.execute(defaultQueryTask); + TaintState state = plugin.getTaintState(); if (!state.wasCancelled()) { - SarifService sarifService = plugin.getSarifService(); - sarifService.getController().setDefaultGraphHander(SarifTaintGraphRunHandler.class); - sarifService.showSarif(desc, state.getData()); + TaintFormat format = state.getOptions().getTaintOutputForm(); + if (!format.equals(TaintFormat.NONE)) { + SarifService sarifService = plugin.getSarifService(); + sarifService.getController().setDefaultGraphHander(SarifTaintGraphRunHandler.class); + sarifService.showSarif(desc, state.getData()); + } plugin.consoleMessage("executing query..."); TaintProvider provider = plugin.getProvider(); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintClearAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintClearAction.java index 1342792aaa..ce17896d1d 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintClearAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintClearAction.java @@ -23,7 +23,6 @@ import docking.action.MenuData; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; -import ghidra.app.util.HelpTopics; import ghidra.util.HelpLocation; /** @@ -34,25 +33,26 @@ import ghidra.util.HelpLocation; public class TaintClearAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; - private TaintState state; - public TaintClearAction(TaintPlugin plugin, TaintState state) { + public TaintClearAction(TaintPlugin plugin) { super("Clear Markers"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintClear")); setPopupMenuData(new MenuData(new String[] { "Taint", "Clear" }, "Decompile")); setKeyBindingData(new KeyBindingData(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); this.plugin = plugin; - this.state = state; } @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } return true; } @Override protected void decompilerActionPerformed(DecompilerActionContext context) { - state.clearMarkers(); + plugin.getTaintState().clearMarkers(); plugin.clearIcons(); plugin.clearTaint(); plugin.consoleMessage("taint cleared"); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintGateAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintGateAction.java index 7e413ac8d9..c55497d576 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintGateAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintGateAction.java @@ -25,7 +25,6 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; -import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import ghidra.util.UndefinedFunction; @@ -43,7 +42,7 @@ public class TaintGateAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; private MarkType mtype; - public TaintGateAction(TaintPlugin plugin, TaintState state) { + public TaintGateAction(TaintPlugin plugin) { super("Mark Gate"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintGate")); setPopupMenuData(new MenuData(new String[] { "Taint", "Gate" }, "Decompile")); @@ -58,6 +57,10 @@ public class TaintGateAction extends TaintAbstractDecompilerAction { @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + Function function = context.getFunction(); if (function == null || function instanceof UndefinedFunction) { return false; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintLoadAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintLoadAction.java index 10c5d4a641..09f6d85d29 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintLoadAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintLoadAction.java @@ -27,7 +27,6 @@ import generic.theme.GIcon; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; -import ghidra.app.util.HelpTopics; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; @@ -47,12 +46,11 @@ import ghidra.util.HelpLocation; public class TaintLoadAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; - private TaintState state; private static String loadSarifFileIconString = "icon.fsbrowser.file.extension.obj"; private static Icon loadSarifFileIcon = new GIcon(loadSarifFileIconString); - public TaintLoadAction(TaintPlugin plugin, TaintState state) { + public TaintLoadAction(TaintPlugin plugin) { super("Load SARIF file"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintLoadSarif")); @@ -60,7 +58,6 @@ public class TaintLoadAction extends TaintAbstractDecompilerAction { setToolBarData(new ToolBarData(loadSarifFileIcon)); this.plugin = plugin; - this.state = state; } @Override @@ -78,6 +75,7 @@ public class TaintLoadAction extends TaintAbstractDecompilerAction { // Pop a file dialog to select the SARIF file. // need to pop-up a file chooser dialog. + TaintState state = plugin.getTaintState(); GhidraFileChooser file_chooser = new GhidraFileChooser(tool.getToolFrame()); file_chooser.setCurrentDirectory(new File(state.getOptions().getTaintEnginePath())); file_chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java index 2ada4ef297..9ae43d9801 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java @@ -27,8 +27,8 @@ import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; public class TaintQueryAction extends TaintAbstractQueryAction { - public TaintQueryAction(TaintPlugin plugin, TaintState state) { - super(plugin, state, "Query", "Source-Sink Taint Query", "Run taint query"); + public TaintQueryAction(TaintPlugin plugin) { + super(plugin, "Query", "Source-Sink Taint Query", "Run taint query"); executeTaintQueryIconString = "icon.graph.default.display.program.graph"; executeTaintQueryIcon = new GIcon(executeTaintQueryIconString); queryType = QueryType.SRCSINK; @@ -39,7 +39,8 @@ public class TaintQueryAction extends TaintAbstractQueryAction { @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { - return state.isValid(); + TaintState state = plugin.getTaintState(); + return state == null ? false : state.isValid(); } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java index eac8d0b915..ba4c2a31e6 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java @@ -25,8 +25,8 @@ import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; public class TaintQueryCustomAction extends TaintAbstractQueryAction { - public TaintQueryCustomAction(TaintPlugin plugin, TaintState state) { - super(plugin, state, "CustomQuery", "Custom Taint Query", "Run custom taint query"); + public TaintQueryCustomAction(TaintPlugin plugin) { + super(plugin, "CustomQuery", "Custom Taint Query", "Run custom taint query"); queryType = QueryType.CUSTOM; setKeyBindingData(new KeyBindingData(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK)); } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java index 27fa19078c..2760b841e9 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java @@ -27,8 +27,8 @@ import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; public class TaintQueryDefaultAction extends TaintAbstractQueryAction { - public TaintQueryDefaultAction(TaintPlugin plugin, TaintState state) { - super(plugin, state, "DefaultQuery", "Default Taint Query", "Run default taint query"); + public TaintQueryDefaultAction(TaintPlugin plugin) { + super(plugin, "DefaultQuery", "Default Taint Query", "Run default taint query"); executeTaintQueryIconString = "icon.version.tracking.markup.status.conflict"; executeTaintQueryIcon = new GIcon(executeTaintQueryIconString); queryType = QueryType.DEFAULT; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSetSizeAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSetSizeAction.java new file mode 100644 index 0000000000..c33f3b7c51 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSetSizeAction.java @@ -0,0 +1,97 @@ +/* ### + * 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.decompiler.taint.actions; + +import docking.action.MenuData; +import docking.widgets.dialogs.InputDialog; +import ghidra.app.decompiler.*; +import ghidra.app.plugin.core.decompile.DecompilerActionContext; +import ghidra.app.plugin.core.decompiler.taint.TaintLabel; +import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; +import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; +import ghidra.program.model.listing.Function; +import ghidra.util.UndefinedFunction; + +/** + * Triggered by right-click on a token in the decompiler window. + *

+ * Action triggered from a specific token in the decompiler window to mark a variable as a + * source or sink and generate the requisite query. Legal tokens to select include: + *

  • + * An input parameter, + *
  • + * A stack variable, + *
  • + * A variable associated with a register, or + *
  • + * A "dynamic" variable. + *
+ */ +public class TaintSetSizeAction extends TaintAbstractDecompilerAction { + + private TaintPlugin plugin; + + public TaintSetSizeAction(TaintPlugin plugin) { + super("Set length"); + // Taint Menu -> Source sub item. + setPopupMenuData(new MenuData(new String[] { "Taint", "Set length" }, "Decompile")); + this.plugin = plugin; + } + + @Override + protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + + Function function = context.getFunction(); + if (function == null || function instanceof UndefinedFunction) { + return false; + } + + ClangToken tokenAtCursor = context.getTokenAtCursor(); + if (tokenAtCursor == null) { + return false; + } + if (tokenAtCursor instanceof ClangFieldToken) { + return true; + } + if (tokenAtCursor.Parent() instanceof ClangReturnType) { + return false; + } + if (tokenAtCursor instanceof ClangFuncNameToken) { + return true; + } + if (!tokenAtCursor.isVariableRef()) { + return false; + } + return true; + } + + @Override + protected void decompilerActionPerformed(DecompilerActionContext context) { + TaintLabel label = plugin.getTaintState().getLabelForToken(MarkType.SOURCE, context.getTokenAtCursor()); + if (label != null) { + InputDialog dialog = new InputDialog("Update length", "Length", Integer.toHexString(label.getSize())); + plugin.getTool().showDialog(dialog); + if (dialog.isCanceled()) { + return; + } + String val = dialog.getValue(); + label.setSize(Integer.parseInt(val, 16)); + } + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkAction.java index 9bf5cf30fd..d4836f4d16 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkAction.java @@ -25,7 +25,6 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; -import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import ghidra.util.UndefinedFunction; @@ -40,7 +39,7 @@ public class TaintSinkAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; private MarkType mtype; - public TaintSinkAction(TaintPlugin plugin, TaintState state) { + public TaintSinkAction(TaintPlugin plugin) { super("Mark Sink"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintSink")); setPopupMenuData(new MenuData(new String[] { "Taint", "Sink" }, "Decompile")); @@ -59,6 +58,10 @@ public class TaintSinkAction extends TaintAbstractDecompilerAction { */ @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + Function function = context.getFunction(); if (function == null || function instanceof UndefinedFunction) { return false; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkBySymbolAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkBySymbolAction.java index fcd539987a..d743631b8a 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkBySymbolAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSinkBySymbolAction.java @@ -21,7 +21,6 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; -import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import ghidra.util.UndefinedFunction; @@ -36,7 +35,7 @@ public class TaintSinkBySymbolAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; private MarkType mtype; - public TaintSinkBySymbolAction(TaintPlugin plugin, TaintState state) { + public TaintSinkBySymbolAction(TaintPlugin plugin) { super("Mark Sink (Symbol)"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintSinkSymbol")); setPopupMenuData(new MenuData(new String[] { "Taint", "Sink (Symbol)" }, "Decompile")); @@ -54,6 +53,10 @@ public class TaintSinkBySymbolAction extends TaintAbstractDecompilerAction { */ @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + Function function = context.getFunction(); if (function == null || function instanceof UndefinedFunction) { return false; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSliceTreeAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSliceTreeAction.java index c7c632472e..b54aba5e53 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSliceTreeAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSliceTreeAction.java @@ -20,7 +20,6 @@ import ghidra.app.decompiler.ClangToken; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; -import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Program; import ghidra.program.model.pcode.HighVariable; import ghidra.util.HelpLocation; @@ -45,7 +44,7 @@ public class TaintSliceTreeAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; - public TaintSliceTreeAction(TaintPlugin plugin, TaintState state) { + public TaintSliceTreeAction(TaintPlugin plugin) { super("Show Slice Tree"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintSliceTree")); setPopupMenuData(new MenuData(new String[] { "Taint", "Slice Tree" }, "Decompile")); @@ -56,6 +55,10 @@ public class TaintSliceTreeAction extends TaintAbstractDecompilerAction { @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + return true; } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceAction.java index 55dd7a3471..792a61edb5 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceAction.java @@ -24,7 +24,6 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; -import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import ghidra.util.UndefinedFunction; @@ -49,7 +48,7 @@ public class TaintSourceAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; private MarkType mtype; - public TaintSourceAction(TaintPlugin plugin, TaintState state) { + public TaintSourceAction(TaintPlugin plugin) { super("Mark Source"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintSource")); // Taint Menu -> Source sub item. @@ -66,6 +65,10 @@ public class TaintSourceAction extends TaintAbstractDecompilerAction { @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + Function function = context.getFunction(); if (function == null || function instanceof UndefinedFunction) { return false; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceBySymbolAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceBySymbolAction.java index 8072edb417..29a1c353ce 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceBySymbolAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintSourceBySymbolAction.java @@ -21,7 +21,6 @@ import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; -import ghidra.app.util.HelpTopics; import ghidra.program.model.listing.Function; import ghidra.util.HelpLocation; import ghidra.util.UndefinedFunction; @@ -46,7 +45,7 @@ public class TaintSourceBySymbolAction extends TaintAbstractDecompilerAction { private TaintPlugin plugin; private MarkType mtype; - public TaintSourceBySymbolAction(TaintPlugin plugin, TaintState state) { + public TaintSourceBySymbolAction(TaintPlugin plugin) { super("Mark Source (Symbol)"); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "TaintSourceSymbol")); // Taint Menu -> Source sub item. @@ -61,6 +60,10 @@ public class TaintSourceBySymbolAction extends TaintAbstractDecompilerAction { @Override protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { + if (plugin.getTaintState() == null) { + return false; + } + Function function = context.getFunction(); if (function == null || function instanceof UndefinedFunction) { return false; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/CTADLTaintState.java similarity index 94% rename from Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java rename to Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/CTADLTaintState.java index 7781c41dc8..fe65000da6 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/CTADLTaintState.java @@ -35,9 +35,9 @@ import ghidra.program.model.pcode.HighVariable; * Container for all the decompiler elements the users "selects" via the menu. * This data is used to build queries. */ -public class TaintStateCTADL extends AbstractTaintState { +public class CTADLTaintState extends AbstractTaintState { - public TaintStateCTADL(TaintPlugin plugin) { + public CTADLTaintState(TaintPlugin plugin) { super(plugin); ENGINE_NAME = "ctadl"; } @@ -106,6 +106,12 @@ public class TaintStateCTADL extends AbstractTaintState { return perFunction ? "ExportPCodeForSingleFunction.java" : "ExportPCodeForCTADL.java"; } + + @Override + protected void writeHeader(PrintWriter writer) { + writer.println("#include \"pcode/taintquery.dl\""); + } + /* * NOTE: This is the only method used now for Sources and Sinks. */ @@ -178,6 +184,7 @@ public class TaintStateCTADL extends AbstractTaintState { Boolean allAccess = taintOptions.getTaintUseAllAccess(); String method = "TaintSanitizeAll"; Address addr = mark.getAddress(); + // TODO: verify setting entryPoint as addr doesn't break things if (mark.getFunctionName() == null) { return; @@ -199,4 +206,9 @@ public class TaintStateCTADL extends AbstractTaintState { writer.println("\tVertex(vn, p)."); } + @Override + protected void writeFooter(PrintWriter writer) { + // Nothing to do here + } + }