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