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