mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 08:05:50 +08:00
GP-3832 importer/exporter for SARIF
This commit is contained in:
+71
@@ -0,0 +1,71 @@
|
||||
/* ###
|
||||
* 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.datamgr.actions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import docking.widgets.tree.GTree;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.ISF.IsfDataTypeWriter;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DataTypeWriterTask extends Task {
|
||||
|
||||
private final DataTypeManager programDataTypeMgr;
|
||||
private final List<DataType> dataTypeList;
|
||||
private final File file;
|
||||
private final GTree gTree;
|
||||
|
||||
public DataTypeWriterTask(GTree gTree, DataTypeManager programDataTypeMgr, List<DataType> dataTypeList, File file) {
|
||||
super("Export Data Types", true, false, true);
|
||||
this.gTree = gTree;
|
||||
this.programDataTypeMgr = programDataTypeMgr;
|
||||
this.dataTypeList = dataTypeList;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
try {
|
||||
//monitor.setMessage("Export to " + file.getName() + "...");
|
||||
FileWriter baseWriter = file == null ? null : new FileWriter(file);
|
||||
IsfDataTypeWriter dataTypeWriter = new IsfDataTypeWriter(programDataTypeMgr, dataTypeList, baseWriter);
|
||||
|
||||
try {
|
||||
JsonObject object = dataTypeWriter.getRootObject(monitor);
|
||||
if (file != null) {
|
||||
dataTypeWriter.write(object);
|
||||
}
|
||||
} finally {
|
||||
dataTypeWriter.close();
|
||||
}
|
||||
} catch (CancelledException e) {
|
||||
// user cancelled; ignore
|
||||
} catch (IOException e) {
|
||||
Msg.showError(getClass(), gTree, "Export Data Types Failed", "Error exporting Data Types: " + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -188,14 +188,14 @@ public class ExportToIsfAction extends DockingAction {
|
||||
fileChooser.dispose();
|
||||
}
|
||||
|
||||
private class DataTypeWriterTask extends Task {
|
||||
public class DataTypeWriterTask extends Task {
|
||||
|
||||
private final DataTypeManager programDataTypeMgr;
|
||||
private final List<DataType> dataTypeList;
|
||||
private final File file;
|
||||
private final GTree gTree;
|
||||
|
||||
DataTypeWriterTask(GTree gTree, DataTypeManager programDataTypeMgr,
|
||||
public DataTypeWriterTask(GTree gTree, DataTypeManager programDataTypeMgr,
|
||||
List<DataType> dataTypeList, File file) {
|
||||
super("Export Data Types", true, false, true);
|
||||
this.gTree = gTree;
|
||||
@@ -209,12 +209,9 @@ public class ExportToIsfAction extends DockingAction {
|
||||
try {
|
||||
monitor.setMessage("Export to " + file.getName() + "...");
|
||||
IsfDataTypeWriter dataTypeWriter =
|
||||
new IsfDataTypeWriter(programDataTypeMgr, new FileWriter(file));
|
||||
new IsfDataTypeWriter(programDataTypeMgr, dataTypeList, new FileWriter(file));
|
||||
|
||||
try {
|
||||
for (DataType dataType : dataTypeList) {
|
||||
dataTypeWriter.requestType(dataType);
|
||||
}
|
||||
JsonObject object = dataTypeWriter.getRootObject(monitor);
|
||||
dataTypeWriter.write(object);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ public class IsfClientHandler {
|
||||
private String lookType(String ns, String key) throws IOException {
|
||||
IsfDataTypeWriter isfWriter = createDataTypeWriter(server.getDataTypeManager(ns));
|
||||
isfWriter.setSkipSymbols(true);
|
||||
isfWriter.requestType(key);
|
||||
//isfWriter.requestType(key);
|
||||
return writeFrom(isfWriter);
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public class IsfClientHandler {
|
||||
|
||||
private IsfDataTypeWriter createDataTypeWriter(DataTypeManager dtm) throws IOException {
|
||||
StringWriter out = new StringWriter();
|
||||
return new IsfDataTypeWriter(dtm, out);
|
||||
return new IsfDataTypeWriter(dtm, null, out);
|
||||
}
|
||||
|
||||
private String writeFrom(IsfDataTypeWriter dataTypeWriter) throws IOException {
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/* ###
|
||||
* 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.program.model.data.ISF;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.docking.settings.SettingsDefinition;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
|
||||
|
||||
public abstract class AbstractIsfObject implements IsfObject {
|
||||
|
||||
@Exclude
|
||||
public String name;
|
||||
@Exclude
|
||||
public String location;
|
||||
@Exclude
|
||||
public List<IsfSetting> settings;
|
||||
|
||||
public AbstractIsfObject(DataType dt) {
|
||||
if (dt != null) {
|
||||
name = dt.getName();
|
||||
location = dt.getCategoryPath().getPath();
|
||||
Settings defaultSettings = dt.getDefaultSettings();
|
||||
processSettings(dt, defaultSettings);
|
||||
}
|
||||
}
|
||||
|
||||
protected void processSettings(DataType dt, Settings defaultSettings) {
|
||||
SettingsDefinition[] settingsDefinitions = dt.getSettingsDefinitions();
|
||||
for (SettingsDefinition def : settingsDefinitions) {
|
||||
if (def.hasValue(defaultSettings)) {
|
||||
settings = new ArrayList<>();
|
||||
String[] names = defaultSettings.getNames();
|
||||
for (String n : names) {
|
||||
Object value = defaultSettings.getValue(n);
|
||||
if (value != null) {
|
||||
IsfSetting setting = new IsfSetting(n, value);
|
||||
settings.add(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/* ###
|
||||
* 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.program.model.data.ISF;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class AbstractIsfWriter implements Closeable {
|
||||
|
||||
protected JsonWriter writer;
|
||||
protected Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
protected JsonObject root = new JsonObject();
|
||||
protected JsonArray objects = new JsonArray();
|
||||
|
||||
public AbstractIsfWriter(Writer baseWriter) throws IOException {
|
||||
if (writer != null) {
|
||||
this.writer = new JsonWriter(baseWriter);
|
||||
writer.setIndent(" ");
|
||||
}
|
||||
this.gson = new GsonBuilder().addSerializationExclusionStrategy(strategy).setPrettyPrinting().create();
|
||||
}
|
||||
|
||||
protected abstract void genRoot(TaskMonitor monitor) throws CancelledException, IOException;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Exclude {
|
||||
// EMPTY
|
||||
}
|
||||
|
||||
// Am setting this as the default, but it's possible we may want more latitude
|
||||
// in the future
|
||||
protected boolean STRICT = true;
|
||||
|
||||
// @Exclude used for properties that might be desirable for a non-STRICT
|
||||
// implementation.
|
||||
ExclusionStrategy strategy = new ExclusionStrategy() {
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes field) {
|
||||
return STRICT && field.getAnnotation(Exclude.class) != null;
|
||||
}
|
||||
};
|
||||
|
||||
public JsonObject getRootObject(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
genRoot(monitor);
|
||||
return root;
|
||||
}
|
||||
|
||||
public JsonArray getResults() {
|
||||
return objects;
|
||||
}
|
||||
|
||||
public JsonElement getTree(Object obj) {
|
||||
return gson.toJsonTree(obj);
|
||||
}
|
||||
|
||||
public Object getObject(JsonElement element, Class<? extends Object> clazz) {
|
||||
return gson.fromJson(element, clazz);
|
||||
}
|
||||
|
||||
public void write(JsonObject object) {
|
||||
gson.toJson(object, writer);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (writer != null) {
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-3
@@ -17,16 +17,15 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.BuiltInDataType;
|
||||
|
||||
public class IsfBuiltIn implements IsfObject {
|
||||
public class IsfBuiltIn extends AbstractIsfObject {
|
||||
|
||||
public Integer size;
|
||||
public Boolean signed;
|
||||
public String kind;
|
||||
public String endian;
|
||||
|
||||
public IsfBuiltIn(BuiltInDataType builtin) {
|
||||
super(builtin);
|
||||
size = IsfUtilities.getLength(builtin);
|
||||
signed = IsfUtilities.getSigned(builtin);
|
||||
kind = IsfUtilities.getBuiltInKind(builtin);
|
||||
endian = IsfUtilities.getEndianness(builtin);
|
||||
}
|
||||
|
||||
+11
-2
@@ -16,9 +16,9 @@
|
||||
package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.program.model.data.ISF.IsfDataTypeWriter.Exclude;
|
||||
import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
|
||||
|
||||
public class IsfComponent implements IsfObject {
|
||||
public class IsfComponent extends AbstractIsfObject {
|
||||
|
||||
public Integer offset;
|
||||
public IsfObject type;
|
||||
@@ -30,16 +30,25 @@ public class IsfComponent implements IsfObject {
|
||||
@Exclude
|
||||
public String field_name;
|
||||
@Exclude
|
||||
public Boolean noFieldName;
|
||||
@Exclude
|
||||
public String comment;
|
||||
|
||||
|
||||
public IsfComponent(DataTypeComponent component, IsfObject typeObj) {
|
||||
super(component.getDataType());
|
||||
offset = component.getOffset();
|
||||
type = typeObj;
|
||||
|
||||
field_name = component.getFieldName();
|
||||
if (field_name == null || field_name.equals("")) {
|
||||
noFieldName = true;
|
||||
}
|
||||
ordinal = component.getOrdinal();
|
||||
length = component.getLength();
|
||||
comment = component.getComment();
|
||||
|
||||
processSettings(component.getDataType(), component.getDefaultSettings());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-23
@@ -15,52 +15,50 @@
|
||||
*/
|
||||
package ghidra.program.model.data.ISF;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.ISF.IsfDataTypeWriter.Exclude;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class IsfComposite implements IsfObject {
|
||||
public class IsfComposite extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public Integer size;
|
||||
public JsonObject fields;
|
||||
|
||||
@Exclude
|
||||
public int alignment;
|
||||
|
||||
|
||||
public IsfComposite(Composite composite, IsfDataTypeWriter writer, TaskMonitor monitor) {
|
||||
super(composite);
|
||||
size = composite.getLength();
|
||||
kind = composite instanceof Structure ? "struct" : "union";
|
||||
alignment = composite.getAlignment();
|
||||
|
||||
DataTypeComponent[] components = composite.getComponents();
|
||||
Map<String, DataTypeComponent> comps = new HashMap<>();
|
||||
for (DataTypeComponent component : components) {
|
||||
String key = component.getFieldName();
|
||||
if (key == null) {
|
||||
key = component.getDefaultFieldName();
|
||||
}
|
||||
comps.put(key, component);
|
||||
if (components.length == 0) {
|
||||
// NB: composite.getLength always returns > 0
|
||||
size = 0;
|
||||
}
|
||||
ArrayList<String> keylist = new ArrayList<>(comps.keySet());
|
||||
Collections.sort(keylist);
|
||||
|
||||
fields = new JsonObject();
|
||||
for (String key : keylist) {
|
||||
for (DataTypeComponent component : components) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
DataTypeComponent component = comps.get(key);
|
||||
IsfObject type = writer.getObjectTypeDeclaration(component);
|
||||
IsfComponent cobj = new IsfComponent(component, type);
|
||||
IsfComponent cobj = getComponent(component, type);
|
||||
String key = component.getFieldName();
|
||||
if (key == null) {
|
||||
key = DataTypeComponent.DEFAULT_FIELD_NAME_PREFIX + component.getOrdinal();
|
||||
if (component.getParent() instanceof Structure) {
|
||||
key += "_0x" + Integer.toHexString(component.getOffset());
|
||||
}
|
||||
}
|
||||
fields.add(key, writer.getTree(cobj));
|
||||
}
|
||||
}
|
||||
|
||||
protected IsfComponent getComponent(DataTypeComponent component, IsfObject type) {
|
||||
return new IsfComponent(component, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,13 +17,14 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.Array;
|
||||
|
||||
public class IsfDataTypeArray implements IsfObject {
|
||||
public class IsfDataTypeArray extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public Integer count;
|
||||
public IsfObject subtype;
|
||||
|
||||
public IsfDataTypeArray(Array arr, IsfObject typeObj) {
|
||||
super(arr);
|
||||
kind = IsfUtilities.getKind(arr);
|
||||
count = arr.getNumElements();
|
||||
subtype = typeObj;
|
||||
|
||||
+3
-2
@@ -16,9 +16,9 @@
|
||||
package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.BitFieldDataType;
|
||||
import ghidra.program.model.data.ISF.IsfDataTypeWriter.Exclude;
|
||||
import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
|
||||
|
||||
public class IsfDataTypeBitField implements IsfObject {
|
||||
public class IsfDataTypeBitField extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public Integer bit_length;
|
||||
@@ -31,6 +31,7 @@ public class IsfDataTypeBitField implements IsfObject {
|
||||
private int storage_size;
|
||||
|
||||
public IsfDataTypeBitField(BitFieldDataType bf, int componentOffset, IsfObject typeObj) {
|
||||
super(bf);
|
||||
kind = IsfUtilities.getKind(bf);
|
||||
bit_length = bf.getBitSize();
|
||||
bit_offset = bf.getBitOffset();
|
||||
|
||||
+4
-3
@@ -17,14 +17,15 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
public class IsfDataTypeDefault implements IsfObject {
|
||||
public class IsfDataTypeDefault extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public String name;
|
||||
int size;
|
||||
|
||||
public IsfDataTypeDefault(DataType dt) {
|
||||
super(dt);
|
||||
kind = IsfUtilities.getKind(dt);
|
||||
name = dt.getName();
|
||||
size = dt.getLength();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,12 +17,13 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
public class IsfDataTypeTypeDef implements IsfObject {
|
||||
public class IsfDataTypeTypeDef extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public IsfObject subtype;
|
||||
|
||||
public IsfDataTypeTypeDef(DataType dt, IsfObject typeObj) {
|
||||
super(dt);
|
||||
kind = IsfUtilities.getKind(dt);
|
||||
subtype = typeObj;
|
||||
}
|
||||
|
||||
+183
-215
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -17,13 +17,14 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.Dynamic;
|
||||
|
||||
public class IsfDynamicComponent implements IsfObject {
|
||||
public class IsfDynamicComponent extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public Integer count;
|
||||
public IsfObject subtype;
|
||||
|
||||
public IsfDynamicComponent(Dynamic dynamicType, IsfObject type, int elementCnt) {
|
||||
super(dynamicType);
|
||||
kind = "array";
|
||||
subtype = type;
|
||||
count = elementCnt;
|
||||
|
||||
@@ -19,13 +19,14 @@ import com.google.gson.JsonObject;
|
||||
|
||||
import ghidra.program.model.data.Enum;
|
||||
|
||||
public class IsfEnum implements IsfObject {
|
||||
public class IsfEnum extends AbstractIsfObject {
|
||||
|
||||
public Integer size;
|
||||
public String base;
|
||||
public JsonObject constants = new JsonObject();
|
||||
|
||||
public IsfEnum(Enum enumm) {
|
||||
super(enumm);
|
||||
size = enumm.getLength();
|
||||
base = "int";
|
||||
String[] names = enumm.getNames();
|
||||
|
||||
+5
-2
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package ghidra.program.model.data.ISF;
|
||||
|
||||
public class IsfFunction implements IsfObject {
|
||||
import ghidra.program.model.data.FunctionDefinition;
|
||||
|
||||
public class IsfFunction extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
|
||||
public IsfFunction() {
|
||||
public IsfFunction(FunctionDefinition def) {
|
||||
super(def);
|
||||
kind = "function";
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -18,14 +18,15 @@ package ghidra.program.model.data.ISF;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.FunctionDefinition;
|
||||
|
||||
public class IsfFunctionPointer implements IsfObject {
|
||||
public class IsfFunctionPointer extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public IsfObject subtype;
|
||||
|
||||
public IsfFunctionPointer(FunctionDefinition def, DataType dt) {
|
||||
super(def);
|
||||
kind = "pointer";
|
||||
subtype = new IsfFunction();
|
||||
subtype = new IsfFunction(def);
|
||||
//TODO?
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -17,6 +17,6 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
public interface IsfObject {
|
||||
|
||||
// EMPTY by design
|
||||
|
||||
// EMPTY
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* ###
|
||||
* 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.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.Pointer;
|
||||
|
||||
public class IsfPointer implements IsfObject {
|
||||
|
||||
public Integer size;
|
||||
public String kind;
|
||||
public String endian;
|
||||
|
||||
public IsfPointer(Pointer ptr) {
|
||||
size = ptr.hasLanguageDependantLength() ? -1 : IsfUtilities.getLength(ptr);
|
||||
kind = "pointer";
|
||||
endian = IsfUtilities.getEndianness(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* ###
|
||||
* 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.program.model.data.ISF;
|
||||
|
||||
public class IsfSetting implements IsfObject {
|
||||
|
||||
public String name;
|
||||
public String kind;
|
||||
public String value;
|
||||
|
||||
public IsfSetting(String name, Object value) {
|
||||
this.name = name;
|
||||
this.value = value.toString();
|
||||
this.kind = value instanceof String ? "string" : "long";
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -17,15 +17,16 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
public class IsfTypedObject implements IsfObject {
|
||||
public class IsfTypedObject extends AbstractIsfObject {
|
||||
|
||||
public String kind;
|
||||
public Integer size;
|
||||
public IsfObject type;
|
||||
|
||||
public IsfTypedObject(DataType dt, IsfObject typeObj) {
|
||||
super(dt);
|
||||
kind = IsfUtilities.getKind(dt);
|
||||
size = dt.getLength();
|
||||
size = dt.hasLanguageDependantLength() ? -1 : dt.getLength();
|
||||
type = typeObj;
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -18,18 +18,17 @@ package ghidra.program.model.data.ISF;
|
||||
import ghidra.program.model.data.BuiltInDataType;
|
||||
import ghidra.program.model.data.TypeDef;
|
||||
|
||||
public class IsfTypedefBase implements IsfObject {
|
||||
public class IsfTypedefBase extends AbstractIsfObject {
|
||||
|
||||
public Integer size;
|
||||
public String kind;
|
||||
public Boolean signed;
|
||||
public String endian;
|
||||
|
||||
public IsfTypedefBase(TypeDef typeDef) {
|
||||
super(typeDef);
|
||||
BuiltInDataType builtin = (BuiltInDataType) typeDef.getBaseDataType();
|
||||
size = typeDef.getLength();
|
||||
kind = IsfUtilities.getBuiltInKind(builtin);
|
||||
signed = IsfUtilities.getSigned(typeDef);
|
||||
endian = IsfUtilities.getEndianness(typeDef);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -17,11 +17,12 @@ package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.TypeDef;
|
||||
|
||||
public class IsfTypedefIntegral implements IsfObject {
|
||||
public class IsfTypedefIntegral extends AbstractIsfObject {
|
||||
|
||||
public Integer size;
|
||||
|
||||
public IsfTypedefIntegral(TypeDef td) {
|
||||
super(td);
|
||||
size = td.getLength();
|
||||
}
|
||||
|
||||
|
||||
+16
-7
@@ -15,21 +15,30 @@
|
||||
*/
|
||||
package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.Pointer;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.data.TypeDef;
|
||||
|
||||
public class IsfTypedefPointer implements IsfObject {
|
||||
public class IsfTypedefPointer extends AbstractIsfObject {
|
||||
|
||||
public Integer size;
|
||||
public Boolean signed;
|
||||
public String kind;
|
||||
public String endian;
|
||||
public IsfObject type;
|
||||
|
||||
public IsfTypedefPointer() {
|
||||
PointerDataType ptr = new PointerDataType();
|
||||
size = ptr.getLength();
|
||||
signed = false; //IsfUtilities.getSigned(ptr);
|
||||
kind = IsfUtilities.getBuiltInKind(ptr);
|
||||
public IsfTypedefPointer(TypeDef typeDef) {
|
||||
super(typeDef);
|
||||
Pointer ptr;
|
||||
if (typeDef != null) {
|
||||
ptr = (Pointer) typeDef.getBaseDataType();
|
||||
}
|
||||
else {
|
||||
ptr = new PointerDataType();
|
||||
}
|
||||
size = ptr.hasLanguageDependantLength() ? -1 : ptr.getLength();
|
||||
kind = "typedef";
|
||||
endian = IsfUtilities.getEndianness(ptr);
|
||||
type = new IsfPointer(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -15,19 +15,19 @@
|
||||
*/
|
||||
package ghidra.program.model.data.ISF;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.TypeDef;
|
||||
|
||||
public class IsfTypedefUser implements IsfObject {
|
||||
public class IsfTypedefUser extends AbstractIsfObject {
|
||||
|
||||
public Integer size;
|
||||
public String kind;
|
||||
public IsfObject type;
|
||||
|
||||
public IsfTypedefUser(TypeDef typeDef, IsfObject typeObj) {
|
||||
DataType baseType = typeDef.getBaseDataType();
|
||||
super(typeDef);
|
||||
size = typeDef.getLength();
|
||||
kind = IsfUtilities.getKind(baseType);
|
||||
kind = "typedef";
|
||||
//kind = IsfUtilities.getKind(baseType);
|
||||
type = typeObj;
|
||||
}
|
||||
|
||||
|
||||
+44
-47
@@ -20,46 +20,6 @@ import ghidra.program.model.data.Enum;
|
||||
|
||||
public class IsfUtilities {
|
||||
|
||||
// list of Ghidra built-in type names which correspond to C primitive types
|
||||
private static String[] INTEGRAL_TYPES = { "char", "short", "int", "long", "long long",
|
||||
"__int64", "float", "double", "long double", "void" };
|
||||
|
||||
private static String[] INTEGRAL_MODIFIERS =
|
||||
{ "signed", "unsigned", "const", "static", "volatile", "mutable", };
|
||||
|
||||
public static boolean isIntegral(String typedefName, String basetypeName) {
|
||||
for (String type : INTEGRAL_TYPES) {
|
||||
if (typedefName.equals(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean endsWithIntegralType = false;
|
||||
for (String type : INTEGRAL_TYPES) {
|
||||
if (typedefName.endsWith(" " + type)) {
|
||||
endsWithIntegralType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean containsIntegralModifier = false;
|
||||
for (String modifier : INTEGRAL_MODIFIERS) {
|
||||
if (typedefName.indexOf(modifier + " ") >= 0 ||
|
||||
typedefName.indexOf(" " + modifier) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (endsWithIntegralType && containsIntegralModifier) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typedefName.endsWith(" " + basetypeName)) {
|
||||
return containsIntegralModifier;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static DataType getBaseDataType(DataType dt) {
|
||||
while (dt != null) {
|
||||
if (dt instanceof Array) {
|
||||
@@ -117,7 +77,7 @@ public class IsfUtilities {
|
||||
return "enum";
|
||||
}
|
||||
if (dt instanceof TypeDef) {
|
||||
return "base"; //"typedef";
|
||||
return "typedef";
|
||||
}
|
||||
if (dt instanceof FunctionDefinition) {
|
||||
return "function";
|
||||
@@ -133,7 +93,7 @@ public class IsfUtilities {
|
||||
|
||||
public static String getBuiltInKind(BuiltInDataType dt) {
|
||||
if (dt instanceof AbstractIntegerDataType) {
|
||||
return dt.getLength() == 1 ? "char" : "int";
|
||||
return dt.getName();
|
||||
}
|
||||
if (dt instanceof AbstractFloatDataType) {
|
||||
return "float";
|
||||
@@ -145,7 +105,7 @@ public class IsfUtilities {
|
||||
return "char"; // "string";
|
||||
}
|
||||
if (dt instanceof PointerDataType) {
|
||||
return "void"; //"pointer";
|
||||
return "pointer";
|
||||
}
|
||||
if (dt instanceof VoidDataType) {
|
||||
return "void";
|
||||
@@ -185,11 +145,48 @@ public class IsfUtilities {
|
||||
return dt.getLength();
|
||||
}
|
||||
|
||||
public static Boolean getSigned(DataType dt) {
|
||||
return dt.getDataOrganization().isSignedChar();
|
||||
}
|
||||
|
||||
public static String getEndianness(DataType dt) {
|
||||
return dt.getDataOrganization().isBigEndian() ? "big" : "little";
|
||||
}
|
||||
|
||||
// // list of Ghidra built-in type names which correspond to C primitive types
|
||||
// private static String[] INTEGRAL_TYPES = { "char", "short", "int", "long", "long long",
|
||||
// "__int64", "float", "double", "long double", "void" };
|
||||
//
|
||||
// private static String[] INTEGRAL_MODIFIERS =
|
||||
// { "signed", "unsigned", "const", "static", "volatile", "mutable", };
|
||||
//
|
||||
// public static boolean isIntegral(String typedefName, String basetypeName) {
|
||||
// for (String type : INTEGRAL_TYPES) {
|
||||
// if (typedefName.equals(type)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// boolean endsWithIntegralType = false;
|
||||
// for (String type : INTEGRAL_TYPES) {
|
||||
// if (typedefName.endsWith(" " + type)) {
|
||||
// endsWithIntegralType = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// boolean containsIntegralModifier = false;
|
||||
// for (String modifier : INTEGRAL_MODIFIERS) {
|
||||
// if (typedefName.indexOf(modifier + " ") >= 0 ||
|
||||
// typedefName.indexOf(" " + modifier) >= 0) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (endsWithIntegralType && containsIntegralModifier) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// if (typedefName.endsWith(" " + basetypeName)) {
|
||||
// return containsIntegralModifier;
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -389,7 +389,6 @@ src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Misc/Appendix.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Misc/Tips.htm||NONE||||END|
|
||||
src/main/help/help/topics/Misc/Welcome_to_Ghidra_Help.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Navigation/Navigation.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Navigation/images/GoToDialog.png||GHIDRA||||END|
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
<LI><A href="#binary">Raw Bytes</A></LI>
|
||||
|
||||
<LI><A href="#xml">XML Export Format</A></LI>
|
||||
|
||||
<LI><A href="#sarif">SARIF Export Format</A></LI>
|
||||
</UL>
|
||||
|
||||
<H2>Export Action</H2>
|
||||
@@ -414,6 +416,18 @@
|
||||
XML Options are identical the <A href=
|
||||
"help/topics/ImporterPlugin/importer.htm#xml_options">XML Importer Options</A>.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="sarif"/><A name="Options_SARIF"/>SARIF</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The SARIF Exporter creates SARIF files that conform to Ghidra's Program DTD. You can
|
||||
re-import files in this format using the <A href=
|
||||
"help/topics/ImporterPlugin/importer.htm">SARIF Importer</A>.</P>
|
||||
|
||||
<P><A name="sarif_options"></A> <I><IMG alt="" border="0" src="help/shared/note.png">The
|
||||
SARIF Options are identical the <A href=
|
||||
"help/topics/ImporterPlugin/importer.htm#sarif_options">SARIF Importer Options</A>.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P class="relatedtopic">Related Topics:</P>
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<LI>Raw Binary</LI>
|
||||
<LI>Relocatable Object Module Format (OMF)</LI>
|
||||
<LI>XML Input Format</LI>
|
||||
<LI>SARIF Input Format</LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
@@ -680,6 +681,16 @@
|
||||
Names</A>.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>SARIF Options <A name="sarif_options"><A name="Options_SARIF_Input_Format"/></A></H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The SARIF format is used to load from a SARIF formatted file. The options are simply
|
||||
switches for which types of program information to import and are identical to the options
|
||||
specified above for XML.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Library_Paths"></A>Library Search Path</H2>
|
||||
|
||||
+9
-2
@@ -64,8 +64,12 @@ import ghidra.util.task.*;
|
||||
public class ExporterDialog extends DialogComponentProvider implements AddressFactoryService {
|
||||
|
||||
private static final String XML_WARNING =
|
||||
" Warning: XML is lossy and intended only for transfering data to external tools. " +
|
||||
"GZF is the recommended format for saving and sharing program data.";
|
||||
" Warning: XML is lossy and intended only for transfering data to external tools. " +
|
||||
"GZF is the recommended format for saving and sharing program data.";
|
||||
|
||||
private static final String SARIF_WARNING =
|
||||
" Warning: SARIF is lossy and intended only for transfering data to external tools. " +
|
||||
"GZF is the recommended format for saving and sharing program data.";
|
||||
|
||||
private static String lastUsedExporterName = GzfExporter.NAME; // default to GZF first time
|
||||
|
||||
@@ -394,6 +398,9 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
if (getSelectedExporter().getName().contains("XML")) {
|
||||
setStatusText(XML_WARNING);
|
||||
}
|
||||
if (getSelectedExporter().getName().contains("SARIF")) {
|
||||
setStatusText(SARIF_WARNING);
|
||||
}
|
||||
setOkEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -470,7 +470,7 @@ public class CodeManagerTest extends AbstractGenericTest {
|
||||
Instruction inst = listing.getInstructionAt(addr(0x1100));
|
||||
inst.setProperty("Numbers", 12);
|
||||
|
||||
PropertyMap map = listing.getPropertyMap("Numbers");
|
||||
PropertyMap<?> map = listing.getPropertyMap("Numbers");
|
||||
assertNotNull(map);
|
||||
|
||||
inst.setProperty("FavoriteColor", new SaveableColor(Palette.RED));
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
MODULE FILE LICENSE: lib/java-sarif-2.1.jar MIT
|
||||
@@ -0,0 +1,36 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
eclipse.project.name = 'Features Sarif'
|
||||
|
||||
dependencies {
|
||||
api project(':Base')
|
||||
api project(':Debugger-isf')
|
||||
//api "javax.activation:activation-1.1.1"
|
||||
api "com.contrastsecurity.sarif:java-sarif-2.1"
|
||||
}
|
||||
|
||||
test {
|
||||
// temporary to prevent test from running when building
|
||||
// specify a pattern that doesn't match any test files.
|
||||
include "dontruntests"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
##VERSION: 2.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/topics/Sarif/SARIF.htm||GHIDRA||||END|
|
||||
src/main/java/sarif/DEVNOTES.txt||GHIDRA||||END|
|
||||
@@ -0,0 +1,2 @@
|
||||
RunHandler
|
||||
ResultHandler
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version='1.0' encoding='ISO-8859-1' ?>
|
||||
<!--
|
||||
|
||||
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
|
||||
upon the JavaHelp table of contents document format. The Ghidra help system uses a
|
||||
TOC_Source.xml file to allow a module with help to define how its contents appear in the
|
||||
Ghidra help viewer's table of contents. The main document (in the Base module)
|
||||
defines a basic structure for the
|
||||
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
|
||||
their files directly into this structure (and optionally define a substructure).
|
||||
|
||||
|
||||
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
|
||||
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
|
||||
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
|
||||
appropriate id attribute value. Using these two tags allows any module to define a place
|
||||
in the table of contents system (<tocdef>), which also provides a place for
|
||||
other TOC_Source.xml files to insert content (<tocref>).
|
||||
|
||||
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
|
||||
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
|
||||
<module name>_TOC.xml files, which are table of contents files written in the format
|
||||
desired by the JavaHelp system. Additionally, the generated files will be merged together
|
||||
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
|
||||
help GUI, there will be one table of contents that has been created from the definitions in
|
||||
all of the modules' TOC_Source.xml files.
|
||||
|
||||
|
||||
Tags and Attributes
|
||||
|
||||
<tocdef>
|
||||
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
|
||||
-text - the display text of the node, as seen in the help GUI
|
||||
-target** - the file to display when the node is clicked in the GUI
|
||||
-sortgroup - this is a string that defines where a given node should appear under a given
|
||||
parent. The string values will be sorted by the JavaHelp system using
|
||||
a javax.text.RulesBasedCollator. If this attribute is not specified, then
|
||||
the text of attribute will be used.
|
||||
|
||||
<tocref>
|
||||
-id - The id of the <tocdef> that this reference points to
|
||||
|
||||
**The URL for the target is relative and should start with 'help/topics'. This text is
|
||||
used by the Ghidra help system to provide a universal starting point for all links so that
|
||||
they can be resolved at runtime, across modules.
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<tocroot>
|
||||
<tocref id="Program Annotation">
|
||||
<tocdef id="SARIF" sortgroup="q" text="SARIF" target="help/topics/Sarif/SARIF.htm" >
|
||||
</tocdef>
|
||||
</tocref>
|
||||
</tocroot>
|
||||
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>Static Analysis Results Interchange Format (SARIF)</TITLE>
|
||||
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY lang="EN-US">
|
||||
<H1><A name="Using_SARIF_Files"></A>Using SARIF Files</H1>
|
||||
|
||||
<P>SARIF is an OASIS standard for exchanging the results of static analysis performed against
|
||||
a target executable. While not a perfect match for exchanging Ghidra program analysis results,
|
||||
it has at least some level of community acceptance and support within the reverse engineering
|
||||
communities at large. SARIF can be created in any number of ways, but, within Ghidra, SARIF
|
||||
creation is done by means of any exporter. As with all of our exporters, a dialog appears when
|
||||
you request an export, with an options panel for selecting which features you would like to export.
|
||||
Currently, we support bookmarks, code, comments, data types, defined data, entry points, equates,
|
||||
external libraries, functions, memory maps, properties, references, registers, relocations,
|
||||
symbols, and program trees. </P>
|
||||
|
||||
<P>SARIF files can be re-ingested in two ways. A matching importer uses the same options to
|
||||
create a new program or add features to an existing one. The process is probably lossy,
|
||||
although efforts have been made to re-create exported programs with some fidelity. Known issues
|
||||
are documented in the DEVNOTES file. The second way to ingest SARIF files is to configure a tool
|
||||
with the SarifPlugin. The plugin provides a single "Read File" toolbar action. After selecting
|
||||
a file, the SARIF results are displayed in table format, with associated ingest actions applicable
|
||||
to a table selection. The features provide by the SARIF importer and exporter all share the action
|
||||
"Add To Program".</P>
|
||||
|
||||
<H1><A name="Custom_SARIF_Handlers"></A>Writing Custom SARIF Handlers</H1>
|
||||
|
||||
<P>While the main SarifProgramResultHandler class handles the majority of program-derived
|
||||
information used on import/export, there are many occasions where you might want to apply the
|
||||
results of some external analysis to a Ghidra program. The "Features Sarif" project contains
|
||||
templates (in the form of abstract classes) for custom handlers: SarifResultHandler.java and
|
||||
SarifRunHandler.java. Both handler types extend ExtensionPoint and are discovered automatically
|
||||
when the a file is read by the SarifPlugin.</P>
|
||||
|
||||
<P>Per-result handlers are used to populate a table displayed to the user and to associate
|
||||
actions with a column in the table. The "getKey" method returns the name for a column, and the
|
||||
"getActionName" a name for the action to be taken. When the user selects that action (in this case,
|
||||
"Commit"), the table provider executes the program task specified by "getTask". In this example,
|
||||
the task grabs the selected rows from the table and, for each row, applies the return type in the
|
||||
column "return_type" to the address in the column "address".</P>
|
||||
|
||||
<P>A more complicated use case might involve the "parse" method, which is called for every result.
|
||||
The default "handle" method takes anything returned by the "parse" method, creates key-value pairs
|
||||
(stored as a map, but...) using "getKey" and the value returned, and adds them to a list of results
|
||||
stored in the data frame. These can be retrieved programmatically later or processed immediately.
|
||||
For example, the SarifPropertyResultHandler looks for results with "AdditionalProperties" labelled
|
||||
either "viewer/table/xxx" or "listing/[comment, highlight, bookmark]". Items in the second category
|
||||
are applied immediately to the current program. Items in the first category are added to the table
|
||||
under column "xxx".</P>
|
||||
|
||||
<P> A third and significantly more complicated example is the ProgramResultHandler, which overrides
|
||||
the "handle" method. This handler converts the set of "AdditionalProperties" into a key-value map.
|
||||
The map is added to a list of results for the data frame, but also converted to a map of lists based
|
||||
on the result's "RuleId". The frame ultimately accrues a map of list of maps, keyed on "RuleId".
|
||||
Its "Add to Program" task takes each per-rule list and applies them to the program using the values
|
||||
stored in the per-result maps.</P>
|
||||
|
||||
<P>A final example, the SarifGraphRunHandler, is a per-run handler and is generally called only once
|
||||
(unless the SARIF files contains multiple runs). This example retrieves a SARIF Graph from the run,
|
||||
converts it to a Ghidra graph object, and displays it. Controls for how and when SARIF graphs are
|
||||
displayed are stored under Edit->Tool Options->Graph->Sarif.</P>
|
||||
|
||||
|
||||
<P class="relatedtopic">Related Topics:</P>
|
||||
|
||||
<UL>
|
||||
<LI><A href="help/topics/ImporterPlugin/importer.htm">Importers</A></LI>
|
||||
<LI><A href="help/topics/ExporterPlugin/importer.htm">Exporters</A></LI>
|
||||
</UL>
|
||||
|
||||
<P> </P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -0,0 +1,13 @@
|
||||
Random notes:
|
||||
|
||||
1. Symbols require a pre- and post-function pass, pre- to guarantee the correct naming of globals
|
||||
including functions and libraries and post- to guarantee namespaces have already been created
|
||||
for locals
|
||||
|
||||
Code differences from export/re-import:
|
||||
|
||||
1. There may be missing parameter datatypes for parameters in a FunctionDefinition used inside a structure or union.
|
||||
|
||||
2. Datatypes like "RTTIBaseDescriptor *32 _((image-base-relative)) *32 _((image-base-relative))" are not handled correctly.
|
||||
|
||||
3. Modified/multiple ProgramTrees will not be imported correctly. Appears to be no way to fix this with the current API.
|
||||
@@ -0,0 +1,289 @@
|
||||
/* ###
|
||||
* 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 sarif;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.contrastsecurity.sarif.Location;
|
||||
import com.contrastsecurity.sarif.LogicalLocation;
|
||||
import com.contrastsecurity.sarif.Result;
|
||||
import com.contrastsecurity.sarif.SarifSchema210;
|
||||
|
||||
import docking.widgets.table.ObjectSelectedListener;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.app.services.GraphDisplayBroker;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.BookmarkManager;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.service.graph.AttributedGraph;
|
||||
import ghidra.service.graph.EmptyGraphType;
|
||||
import ghidra.service.graph.GraphDisplay;
|
||||
import ghidra.service.graph.GraphDisplayOptions;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.GraphException;
|
||||
import resources.ResourceManager;
|
||||
import sarif.handlers.SarifResultHandler;
|
||||
import sarif.handlers.SarifRunHandler;
|
||||
import sarif.managers.ProgramSarifMgr;
|
||||
import sarif.model.SarifDataFrame;
|
||||
import sarif.view.ImageArtifactDisplay;
|
||||
import sarif.view.SarifResultsTableProvider;
|
||||
|
||||
/**
|
||||
* Controller for handling interactions between the SARIF log file and Ghidra
|
||||
*/
|
||||
public class SarifController implements ObjectSelectedListener<Map<String, Object>> {
|
||||
|
||||
private SarifPlugin plugin;
|
||||
private Program program;
|
||||
|
||||
private ColorizingService coloringService;
|
||||
private BookmarkManager bookmarkManager;
|
||||
private ProgramSarifMgr programManager;
|
||||
|
||||
private Set<SarifResultsTableProvider> providers = new HashSet<>();
|
||||
public Set<ImageArtifactDisplay> artifacts = new HashSet<>();
|
||||
public Set<GraphDisplay> graphs = new HashSet<>();
|
||||
|
||||
public Set<SarifResultHandler> getSarifResultHandlers() {
|
||||
Set<SarifResultHandler> set = new HashSet<>();
|
||||
set.addAll(ClassSearcher.getInstances(SarifResultHandler.class));
|
||||
return set;
|
||||
}
|
||||
|
||||
public Set<SarifRunHandler> getSarifRunHandlers() {
|
||||
Set<SarifRunHandler> set = new HashSet<>();
|
||||
set.addAll(ClassSearcher.getInstances(SarifRunHandler.class));
|
||||
return set;
|
||||
}
|
||||
|
||||
public SarifController(Program program, SarifPlugin plugin) {
|
||||
this.program = program;
|
||||
this.plugin = plugin;
|
||||
this.coloringService = plugin.getTool().getService(ColorizingService.class);
|
||||
this.programManager = new ProgramSarifMgr(program);
|
||||
}
|
||||
|
||||
public SarifController(ProgramSarifMgr manager) {
|
||||
this.program = null;
|
||||
this.plugin = null;
|
||||
this.coloringService = null; // plugin.getTool().getService(ColorizingService.class);
|
||||
this.programManager = manager;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
Set<SarifResultsTableProvider> copyProviders = new HashSet<>();
|
||||
copyProviders.addAll(providers);
|
||||
for (SarifResultsTableProvider p : copyProviders) {
|
||||
p.dispose();
|
||||
}
|
||||
Set<ImageArtifactDisplay> copyArtifacts = new HashSet<>();
|
||||
copyArtifacts.addAll(artifacts);
|
||||
for (ImageArtifactDisplay a : copyArtifacts) {
|
||||
a.dispose();
|
||||
}
|
||||
for (GraphDisplay g : graphs) {
|
||||
g.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void showTable(boolean makeVisible) {
|
||||
for (SarifResultsTableProvider p : providers) {
|
||||
p.setVisible(makeVisible);
|
||||
}
|
||||
}
|
||||
|
||||
public void showTable(String logName, SarifSchema210 sarif) {
|
||||
SarifDataFrame df = new SarifDataFrame(sarif, this, false);
|
||||
SarifResultsTableProvider provider = new SarifResultsTableProvider(logName, this.plugin, this, df);
|
||||
provider.filterTable.addSelectionListener(this);
|
||||
provider.addToTool();
|
||||
provider.setVisible(true);
|
||||
provider.setTitle(logName);
|
||||
if (!providers.contains(provider)) {
|
||||
providers.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public void showImage(String key, BufferedImage img) {
|
||||
if (plugin.displayArtifacts()) {
|
||||
ImageArtifactDisplay display = new ImageArtifactDisplay(plugin.getTool(), key, "Sarif Parse", img);
|
||||
display.setVisible(true);
|
||||
artifacts.add(display);
|
||||
}
|
||||
}
|
||||
|
||||
public void showGraph(AttributedGraph graph) {
|
||||
try {
|
||||
GraphDisplayBroker service = this.plugin.getTool().getService(GraphDisplayBroker.class);
|
||||
boolean append = plugin.appendToGraph();
|
||||
GraphDisplay display = service.getDefaultGraphDisplay(append, null);
|
||||
GraphDisplayOptions graphOptions = new GraphDisplayOptions(new EmptyGraphType());
|
||||
graphOptions.setMaxNodeCount(plugin.getGraphSize());
|
||||
|
||||
if (plugin.displayGraphs()) {
|
||||
display.setGraph(graph, graphOptions, graph.getDescription(), append, null);
|
||||
graphs.add(display);
|
||||
}
|
||||
} catch (GraphException | CancelledException e) {
|
||||
Msg.error(this, "showGraph failed " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a results has "listing/<something>" in a SARIF result, this handles
|
||||
* defining our custom API for those
|
||||
*
|
||||
* @param log
|
||||
* @param result
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void handleListingAction(Result result, String key, Object value) {
|
||||
List<Address> addrs = getListingAddresses(result);
|
||||
for (Address addr : addrs) {
|
||||
switch (key) {
|
||||
case "comment":
|
||||
/* @formatter:off
|
||||
* docs/GhidraAPI_javadoc/api/constant-values.html#ghidra.program.model.listing.CodeUnit
|
||||
* EOL_COMMENT 0
|
||||
* PRE_COMMENT 1
|
||||
* POST_COMMENT 2
|
||||
* PLATE_COMMENT 3
|
||||
* REPEATABLE_COMMENT 4
|
||||
* @formatter:on
|
||||
*/
|
||||
String comment = (String) value;
|
||||
getProgram().getListing().setComment(addr, CodeUnit.PLATE_COMMENT, comment);
|
||||
break;
|
||||
case "highlight":
|
||||
Color color = Color.decode((String) value);
|
||||
coloringService.setBackgroundColor(addr, addr, color);
|
||||
break;
|
||||
case "bookmark":
|
||||
String bookmark = (String) value;
|
||||
getProgram().getBookmarkManager().setBookmark(addr, "Sarif", result.getRuleId(), bookmark);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void colorBackground(AddressSetView set, Color color) {
|
||||
coloringService.setBackgroundColor(set, color);
|
||||
}
|
||||
|
||||
public void colorBackground(Address addr, Color color) {
|
||||
coloringService.setBackgroundColor(addr, addr, color);
|
||||
}
|
||||
|
||||
public Address longToAddress(Object lval) {
|
||||
if (lval instanceof Long) {
|
||||
return getProgram().getAddressFactory().getDefaultAddressSpace().getAddress((Long) lval);
|
||||
}
|
||||
return getProgram().getAddressFactory().getDefaultAddressSpace().getAddress((Integer) lval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get listing addresses associated with a result
|
||||
*
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
public List<Address> getListingAddresses(Result result) {
|
||||
List<Address> addrs = new ArrayList<>();
|
||||
if (result.getLocations() != null && result.getLocations().size() > 0) {
|
||||
List<Location> locations = result.getLocations();
|
||||
for (Location loc : locations) {
|
||||
Address addr = locationToAddress(loc);
|
||||
if (addr != null) {
|
||||
addrs.add(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return addrs;
|
||||
}
|
||||
|
||||
public Address locationToAddress(Location loc) {
|
||||
if (loc.getPhysicalLocation() != null) {
|
||||
return longToAddress(loc.getPhysicalLocation().getAddress().getAbsoluteAddress());
|
||||
}
|
||||
if (loc.getLogicalLocations() != null) {
|
||||
Set<LogicalLocation> logicalLocations = loc.getLogicalLocations();
|
||||
for (LogicalLocation logLoc : logicalLocations) {
|
||||
switch (logLoc.getKind()) {
|
||||
case "function":
|
||||
String fname = logLoc.getName();
|
||||
for (Function func : getProgram().getFunctionManager().getFunctions(true)) {
|
||||
if (fname.equals(func.getName())) {
|
||||
return func.getEntryPoint();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Msg.error(this, "Unknown logical location to handle: " + logLoc.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void objectSelected(Map<String, Object> row) {
|
||||
if (row != null) {
|
||||
if (row.containsKey("CodeFlows")) {
|
||||
for (List<Address> flow : (List<List<Address>>) row.get("CodeFlows")) {
|
||||
this.plugin.makeSelection(flow);
|
||||
}
|
||||
}
|
||||
if (row.containsKey("Graphs")) {
|
||||
for (AttributedGraph graph : (List<AttributedGraph>) row.get("Graphs")) {
|
||||
this.showGraph(graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeProvider(SarifResultsTableProvider provider) {
|
||||
providers.remove(provider);
|
||||
}
|
||||
|
||||
public ProgramSarifMgr getProgramSarifMgr() {
|
||||
return programManager;
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
public void setProgram(Program program) {
|
||||
this.program = program;
|
||||
this.bookmarkManager = program.getBookmarkManager();
|
||||
bookmarkManager.defineType("Sarif", ResourceManager.loadImage("images/peach_16.png"), Color.pink, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
/* ###
|
||||
* 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 sarif;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.AnalysisWorker;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.OptionException;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.AbstractProgramLoader;
|
||||
import ghidra.app.util.opinion.LoadException;
|
||||
import ghidra.app.util.opinion.LoadSpec;
|
||||
import ghidra.app.util.opinion.Loaded;
|
||||
import ghidra.app.util.opinion.LoaderTier;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.CompilerSpecDescription;
|
||||
import ghidra.program.model.lang.CompilerSpecNotFoundException;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.lang.ExternalLanguageCompilerSpecQuery;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.LanguageCompilerSpecPair;
|
||||
import ghidra.program.model.lang.LanguageDescription;
|
||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import sarif.export.SarifObject;
|
||||
import sarif.managers.ProgramInfo;
|
||||
import sarif.managers.ProgramSarifMgr;
|
||||
|
||||
public class SarifLoader extends AbstractProgramLoader {
|
||||
|
||||
private static final String FILE_EXTENSION = SarifObject.SARIF ? ".sarif" : ".json";
|
||||
public final static String SARIF_SRC_NAME = "SARIF Input Format";
|
||||
|
||||
@Override
|
||||
public LoaderTier getTier() {
|
||||
return LoaderTier.SPECIALIZED_TARGET_LOADER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTierPriority() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLoadIntoProgram() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||
|
||||
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||
|
||||
//
|
||||
// Unusual Code Alert!: the parse() method below uses Processor to
|
||||
// location processors
|
||||
// by name when reading SARIF. The Processor class is not fully
|
||||
// populated until the languages have been loaded.
|
||||
//
|
||||
getLanguageService();
|
||||
|
||||
ParseResult result = parse(provider);
|
||||
|
||||
ProgramInfo info = result.lastInfo;
|
||||
if (info == null) {
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
if (info.languageID != null) {// non-external language
|
||||
// got a language ID, good...
|
||||
try {
|
||||
LanguageDescription languageDescription =
|
||||
getLanguageService().getLanguageDescription(info.languageID);
|
||||
|
||||
boolean preferred = false;
|
||||
if (info.compilerSpecID == null) {
|
||||
// no compiler spec ID, try to pick "default" (embedded
|
||||
// magic string!!! BAD)
|
||||
for (CompilerSpecDescription csd : languageDescription.getCompatibleCompilerSpecDescriptions()) {
|
||||
LanguageCompilerSpecPair pair = new LanguageCompilerSpecPair(
|
||||
languageDescription.getLanguageID(), csd.getCompilerSpecID());
|
||||
loadSpecs.add(new LoadSpec(this, 0, pair, preferred));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// test existence; throw exception on failure
|
||||
languageDescription.getCompilerSpecDescriptionByID(info.compilerSpecID);
|
||||
// good, we know exactly what this is (make it preferred)
|
||||
LanguageCompilerSpecPair pair =
|
||||
new LanguageCompilerSpecPair(info.languageID, info.compilerSpecID);
|
||||
preferred = true;
|
||||
loadSpecs.add(new LoadSpec(this, 0, pair, preferred));
|
||||
}
|
||||
}
|
||||
catch (CompilerSpecNotFoundException | LanguageNotFoundException lnfe) {
|
||||
// ignore
|
||||
// should fall into loadSpecs.isEmpty() case below
|
||||
}
|
||||
|
||||
}
|
||||
else if (info.processorName != null) {// external language
|
||||
// no ID, look by processor/possibly endian
|
||||
Integer size = extractSize(info.addressModel);
|
||||
Endian endian = Endian.toEndian(info.endian);
|
||||
ExternalLanguageCompilerSpecQuery broadQuery =
|
||||
new ExternalLanguageCompilerSpecQuery(info.processorName,
|
||||
info.getNormalizedExternalToolName(), endian, size, info.compilerSpecID);
|
||||
List<LanguageCompilerSpecPair> pairs =
|
||||
getLanguageService().getLanguageCompilerSpecPairs(broadQuery);
|
||||
|
||||
if (!pairs.isEmpty()) {
|
||||
boolean preferred = false;
|
||||
if (pairs.size() == 1) {
|
||||
preferred = true;
|
||||
}
|
||||
for (LanguageCompilerSpecPair pair : pairs) {
|
||||
loadSpecs.add(new LoadSpec(this, 0, pair, preferred));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loadSpecs.isEmpty() && provider.getName().endsWith(FILE_EXTENSION)) {
|
||||
// just put 'em all in (give endianess preference)
|
||||
List<LanguageDescription> languageDescriptions =
|
||||
getLanguageService().getLanguageDescriptions(false);
|
||||
for (LanguageDescription languageDescription : languageDescriptions) {
|
||||
Collection<CompilerSpecDescription> compilerSpecDescriptions =
|
||||
languageDescription.getCompatibleCompilerSpecDescriptions();
|
||||
for (CompilerSpecDescription compilerSpecDescription : compilerSpecDescriptions) {
|
||||
LanguageCompilerSpecPair pair =
|
||||
new LanguageCompilerSpecPair(languageDescription.getLanguageID(),
|
||||
compilerSpecDescription.getCompilerSpecID());
|
||||
loadSpecs.add(new LoadSpec(this, 0, pair, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
private static Pattern ADDRESS_MODEL_PATTERN = Pattern.compile("(\\d+)-bit");
|
||||
|
||||
private Integer extractSize(String addressModel) {
|
||||
if (addressModel != null) {
|
||||
Matcher matcher = ADDRESS_MODEL_PATTERN.matcher(addressModel);
|
||||
if (matcher.find()) {
|
||||
return Integer.parseInt(matcher.group(1));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferredFileName(ByteProvider provider) {
|
||||
String name = provider.getName();
|
||||
if (name.toLowerCase().endsWith(FILE_EXTENSION)) {
|
||||
return name.substring(0, name.length() - FILE_EXTENSION.length());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Loaded<Program>> loadProgram(ByteProvider provider, String programName,
|
||||
Project project, String programFolderPath, LoadSpec loadSpec, List<Option> options,
|
||||
MessageLog log, Object consumer, TaskMonitor monitor)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
|
||||
//throw new RuntimeException("SARIF importer supports only 'Add To Program'");
|
||||
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
|
||||
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
|
||||
CompilerSpec importerCompilerSpec =
|
||||
importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
|
||||
|
||||
ParseResult result = parse(provider);
|
||||
|
||||
Address imageBase = null;
|
||||
if (result.lastInfo.imageBase != null) {
|
||||
imageBase = importerLanguage.getAddressFactory().getAddress(result.lastInfo.imageBase);
|
||||
}
|
||||
Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
|
||||
importerCompilerSpec, consumer);
|
||||
List<Loaded<Program>> loadedList =
|
||||
List.of(new Loaded<>(prog, programName, programFolderPath));
|
||||
boolean success = false;
|
||||
try {
|
||||
success = doImport(result.lastSarifMgr, options, log, prog, monitor, false);
|
||||
if (success) {
|
||||
createDefaultMemoryBlocks(prog, importerLanguage, log);
|
||||
return loadedList;
|
||||
}
|
||||
throw new LoadException("Failed to load");
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
release(loadedList, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
|
||||
List<Option> options, MessageLog log, Program prog, TaskMonitor monitor)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
File file = provider.getFile();
|
||||
doImport(new ProgramSarifMgr(prog, file), options, log, prog, monitor, true);
|
||||
}
|
||||
|
||||
private boolean doImportWork(final ProgramSarifMgr mgr, final List<Option> options,
|
||||
final MessageLog log, Program prog, TaskMonitor monitor,
|
||||
final boolean isAddToProgram) throws LoadException {
|
||||
boolean success = true;
|
||||
try {
|
||||
SarifProgramOptions sarifOptions = mgr.getOptions();
|
||||
sarifOptions.setOptions(options);
|
||||
sarifOptions.setAddToProgram(isAddToProgram);
|
||||
mgr.read(prog, monitor);
|
||||
success = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = "(empty)";
|
||||
if (log != null && !"".equals(log.toString())) {
|
||||
message = log.toString();
|
||||
}
|
||||
Msg.warn(this, "SARIF import exception, log: " + message, e);
|
||||
throw new LoadException(e.getMessage());
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean doImport(final ProgramSarifMgr mgr, final List<Option> options,
|
||||
final MessageLog log, Program prog, TaskMonitor monitor, final boolean isAddToProgram)
|
||||
throws IOException {
|
||||
|
||||
if (!AutoAnalysisManager.hasAutoAnalysisManager(prog)) {
|
||||
int txId = prog.startTransaction("SARIF Import");
|
||||
try {
|
||||
return doImportWork(mgr, options, log, prog, monitor, isAddToProgram);
|
||||
}
|
||||
finally {
|
||||
prog.endTransaction(txId, true);
|
||||
}
|
||||
}
|
||||
|
||||
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(prog);
|
||||
try {
|
||||
return analysisMgr.scheduleWorker(new AnalysisWorker() {
|
||||
|
||||
@Override
|
||||
public String getWorkerName() {
|
||||
return "SARIF Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean analysisWorkerCallback(Program program, Object workerContext,
|
||||
TaskMonitor taskMonitor) throws Exception, CancelledException {
|
||||
return doImportWork(mgr, options, log, program, taskMonitor, isAddToProgram);
|
||||
}
|
||||
}, null, false, monitor);
|
||||
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return false;
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IOException) {
|
||||
throw (IOException) cause;
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParseResult {
|
||||
final ProgramSarifMgr lastSarifMgr;
|
||||
final ProgramInfo lastInfo;
|
||||
|
||||
ParseResult(ProgramSarifMgr lastSarifMgr, ProgramInfo lastInfo) {
|
||||
this.lastSarifMgr = lastSarifMgr;
|
||||
this.lastInfo = lastInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private ParseResult parse(ByteProvider provider) {
|
||||
try {
|
||||
ProgramSarifMgr lastSarifMgr = new ProgramSarifMgr(provider);
|
||||
ProgramInfo lastInfo = lastSarifMgr.getProgramInfo();
|
||||
return new ParseResult(lastSarifMgr, lastInfo);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
// This can happen during the import process when this loader attempts to load
|
||||
// a non-SARIF file (there really should be 2 methods, a speculative version and
|
||||
// a version that expects no exception)
|
||||
Msg.trace(this, "Unable to parse SARIF for " + provider.getName(), e);
|
||||
return new ParseResult(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram) {
|
||||
return new SarifProgramOptions().getOptions(loadIntoProgram);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return SARIF_SRC_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
|
||||
try {
|
||||
new SarifProgramOptions().setOptions(options);
|
||||
}
|
||||
catch (OptionException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/* ###
|
||||
* 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 sarif;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import com.contrastsecurity.sarif.SarifSchema210;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import ghidra.MiscellaneousPluginPackage;
|
||||
import ghidra.app.events.ProgramActivatedPluginEvent;
|
||||
import ghidra.app.events.ProgramClosedPluginEvent;
|
||||
import ghidra.app.events.ProgramOpenedPluginEvent;
|
||||
import ghidra.app.events.ProgramVisibilityChangePluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginEvent;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
import resources.ResourceManager;
|
||||
import sarif.io.SarifGsonIO;
|
||||
import sarif.io.SarifIO;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = MiscellaneousPluginPackage.NAME,
|
||||
category = PluginCategoryNames.ANALYSIS,
|
||||
shortDescription = "Sarif Plugin.",
|
||||
description = "From sarif parsing to DL modelling"
|
||||
)
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
* A {@link ProgramPlugin} for reading in sarif files
|
||||
*/
|
||||
public class SarifPlugin extends ProgramPlugin implements OptionsChangeListener {
|
||||
public static final String NAME = "Sarif";
|
||||
public static final Icon SARIF_ICON = ResourceManager.loadImage("images/peach_16.png");
|
||||
|
||||
private Map<Program, SarifController> sarifControllers;
|
||||
private SarifIO io;
|
||||
|
||||
public SarifPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
this.sarifControllers = new HashMap<Program, SarifController>();
|
||||
this.io = new SarifGsonIO();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
createActions();
|
||||
initializeOptions();
|
||||
}
|
||||
|
||||
public void readFile(File file) {
|
||||
if (file != null) {
|
||||
try {
|
||||
showSarif(file.getName(), io.readSarif(file));
|
||||
} catch (JsonSyntaxException | IOException e) {
|
||||
Msg.showError(this, tool.getActiveWindow(), "File parse error", "Invalid Sarif File");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
Program eventProgram = null;
|
||||
if (event instanceof ProgramActivatedPluginEvent) {
|
||||
ProgramActivatedPluginEvent ev = (ProgramActivatedPluginEvent) event;
|
||||
eventProgram = ev.getActiveProgram();
|
||||
}
|
||||
else if (event instanceof ProgramOpenedPluginEvent) {
|
||||
ProgramOpenedPluginEvent ev = (ProgramOpenedPluginEvent) event;
|
||||
eventProgram = ev.getProgram();
|
||||
}
|
||||
else if (event instanceof ProgramClosedPluginEvent) {
|
||||
ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
|
||||
eventProgram = ev.getProgram();
|
||||
}
|
||||
else if (event instanceof ProgramClosedPluginEvent) {
|
||||
ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
|
||||
eventProgram = ev.getProgram();
|
||||
}
|
||||
else if (event instanceof ProgramVisibilityChangePluginEvent) {
|
||||
ProgramVisibilityChangePluginEvent ev = (ProgramVisibilityChangePluginEvent) event;
|
||||
eventProgram = ev.getProgram();
|
||||
}
|
||||
SarifController controller = sarifControllers.get(eventProgram);
|
||||
if (controller != null) {
|
||||
if (event instanceof ProgramClosedPluginEvent) {
|
||||
controller.dispose();
|
||||
sarifControllers.remove(eventProgram, controller);
|
||||
}
|
||||
else if (event instanceof ProgramVisibilityChangePluginEvent) {
|
||||
ProgramVisibilityChangePluginEvent ev = (ProgramVisibilityChangePluginEvent) event;
|
||||
controller.showTable(ev.isProgramVisible());
|
||||
}
|
||||
else {
|
||||
controller.showTable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ultimately both selections end up calling this to actually show something on
|
||||
* the Ghidra gui
|
||||
*
|
||||
* @param logName
|
||||
* @param sarif
|
||||
*/
|
||||
public void showSarif(String logName, SarifSchema210 sarif) {
|
||||
currentProgram = getCurrentProgram();
|
||||
if (currentProgram != null) {
|
||||
if (!sarifControllers.containsKey(currentProgram)) {
|
||||
SarifController controller = new SarifController(currentProgram, this);
|
||||
sarifControllers.put(currentProgram, controller);
|
||||
}
|
||||
SarifController currentController = sarifControllers.get(currentProgram);
|
||||
if (currentController != null) {
|
||||
currentController.showTable(logName, sarif);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Msg.showError(this, tool.getActiveWindow(), "File parse error", "No current program");
|
||||
}
|
||||
|
||||
public void makeSelection(List<Address> addrs) {
|
||||
AddressSet selection = new AddressSet();
|
||||
for (Address addr : addrs) {
|
||||
selection.add(addr);
|
||||
}
|
||||
this.setSelection(selection);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
//@formatter:off
|
||||
new ActionBuilder("Read", getName())
|
||||
.menuPath("Sarif", "Read File")
|
||||
.menuGroup("sarif", "1")
|
||||
.enabledWhen(ctx -> getCurrentProgram() != null)
|
||||
.onAction(e -> {
|
||||
GhidraFileChooser chooser = new GhidraFileChooser(tool.getActiveWindow());
|
||||
this.readFile(chooser.getSelectedFile());
|
||||
})
|
||||
.buildAndInstall(tool);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
private void initializeOptions() {
|
||||
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
|
||||
options.addOptionsChangeListener(this);
|
||||
|
||||
HelpLocation help = new HelpLocation(getName(), "Options");
|
||||
|
||||
Options sarifOptions = options.getOptions(NAME);
|
||||
registerOptions(sarifOptions, help);
|
||||
loadOptions(sarifOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) throws OptionsVetoException {
|
||||
|
||||
Options sarifOptions = options.getOptions(NAME);
|
||||
loadOptions(sarifOptions);
|
||||
}
|
||||
|
||||
public void registerOptions(Options options, HelpLocation help) {
|
||||
|
||||
options.setOptionsHelpLocation(help);
|
||||
|
||||
options.registerOption("Display Artifacts", displayArtifacts(), help,
|
||||
"Display artifacts by default");
|
||||
|
||||
options.registerOption("Display Graphs", displayGraphs(), help,
|
||||
"Display graphs by default");
|
||||
|
||||
options.registerOption("Max Graph Size", getGraphSize(), help,
|
||||
"Maximum number of nodes per graph");
|
||||
|
||||
options.registerOption("Append Graphs", appendToGraph(), help,
|
||||
"Append to existing graph");
|
||||
|
||||
}
|
||||
|
||||
public void loadOptions(Options options) {
|
||||
|
||||
displayArtifactsByDefault = options.getBoolean("Display Artifacts", displayArtifacts());
|
||||
displayGraphsByDefault = options.getBoolean("Display Graphs", displayGraphs());
|
||||
maxGraphSize = options.getInt("Max Graph Size", getGraphSize());
|
||||
appendToCurrentGraph = options.getBoolean("Append Graphs", appendToGraph());
|
||||
|
||||
}
|
||||
|
||||
private boolean displayGraphsByDefault = false;
|
||||
public boolean displayGraphs() {
|
||||
return displayGraphsByDefault;
|
||||
}
|
||||
|
||||
private boolean displayArtifactsByDefault = false;
|
||||
public boolean displayArtifacts() {
|
||||
return displayArtifactsByDefault;
|
||||
}
|
||||
|
||||
private int maxGraphSize = 1000;
|
||||
public int getGraphSize() {
|
||||
return maxGraphSize;
|
||||
}
|
||||
|
||||
private boolean appendToCurrentGraph = false;
|
||||
public boolean appendToGraph() {
|
||||
return appendToCurrentGraph;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
/* ###
|
||||
* 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 sarif;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import com.contrastsecurity.sarif.Artifact;
|
||||
import com.contrastsecurity.sarif.ArtifactContent;
|
||||
import com.contrastsecurity.sarif.ReportingDescriptor;
|
||||
import com.contrastsecurity.sarif.ReportingDescriptorReference;
|
||||
import com.contrastsecurity.sarif.Run;
|
||||
import com.contrastsecurity.sarif.ToolComponent;
|
||||
|
||||
public class SarifUtils {
|
||||
|
||||
public static ByteArrayInputStream getArtifactContent(Artifact artifact) {
|
||||
ArtifactContent content = artifact.getContents();
|
||||
String b64 = content.getBinary();
|
||||
byte[] decoded = Base64.decode(b64);
|
||||
return new ByteArrayInputStream(decoded);
|
||||
}
|
||||
|
||||
public static ReportingDescriptor getTaxaValue(ReportingDescriptorReference taxa, ToolComponent taxonomy) {
|
||||
List<ReportingDescriptor> view = new ArrayList<>(taxonomy.getTaxa());
|
||||
return view.get(taxa.getIndex().intValue());
|
||||
}
|
||||
|
||||
public static ToolComponent getTaxonomy(ReportingDescriptorReference taxa, Set<ToolComponent> taxonomies) {
|
||||
Object idx = taxa.getToolComponent().getIndex();
|
||||
if (idx == null) {
|
||||
List<ToolComponent> view = new ArrayList<>(taxonomies);
|
||||
idx= taxa.getIndex();
|
||||
return view.get(idx instanceof Long ? ((Long)idx).intValue() : (Integer) idx);
|
||||
}
|
||||
for (ToolComponent taxonomy : taxonomies) {
|
||||
if (taxonomy.getName().equals(taxa.getToolComponent().getName())) {
|
||||
return taxonomy;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<String> getTaxonomyNames(Run sarifRun) {
|
||||
List<String> names = new ArrayList<>();
|
||||
Set<ToolComponent> taxonomies = sarifRun.getTaxonomies();
|
||||
if (taxonomies != null) {
|
||||
for (ToolComponent taxonomy : sarifRun.getTaxonomies()) {
|
||||
names.add(taxonomy.getName());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* ###
|
||||
* 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 sarif.export;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import ghidra.program.model.data.ISF.AbstractIsfWriter;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public abstract class AbstractExtWriter extends AbstractIsfWriter {
|
||||
|
||||
public AbstractExtWriter(Writer baseWriter) throws IOException {
|
||||
super(baseWriter);
|
||||
STRICT = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract void genRoot(TaskMonitor monitor) throws CancelledException, IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/* ###
|
||||
* 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 sarif.export;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.DomainObjectService;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.OptionException;
|
||||
import ghidra.app.util.exporter.Exporter;
|
||||
import ghidra.app.util.exporter.ExporterException;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import sarif.SarifProgramOptions;
|
||||
import sarif.managers.ProgramSarifMgr;
|
||||
|
||||
/**
|
||||
* An implementation of exporter that creates
|
||||
* an SARIF representation of the program.
|
||||
*/
|
||||
public class SarifExporter extends Exporter {
|
||||
private SarifProgramOptions options = new SarifProgramOptions();
|
||||
|
||||
/**
|
||||
* Constructs a new SARIF exporter.
|
||||
*/
|
||||
public SarifExporter() {
|
||||
super("SARIF", "sarif", new HelpLocation("ExporterPlugin", "sarif"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getOptions(DomainObjectService domainObjectService) {
|
||||
if (options == null) {
|
||||
options = new SarifProgramOptions();
|
||||
}
|
||||
return options.getOptions(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(List<Option> options) throws OptionException {
|
||||
this.options.setOptions(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet, TaskMonitor monitor)
|
||||
throws IOException, ExporterException {
|
||||
|
||||
log.clear();
|
||||
|
||||
if (!(domainObj instanceof Program)) {
|
||||
log.appendMsg("Unsupported type: "+domainObj.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
Program program = (Program)domainObj;
|
||||
|
||||
if (addrSet == null) {
|
||||
addrSet = program.getMemory();
|
||||
}
|
||||
|
||||
ProgramSarifMgr mgr = new ProgramSarifMgr(program, file);
|
||||
|
||||
try {
|
||||
log = mgr.write(program, addrSet, monitor, options);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new ExporterException("User cancelled SARIF export.");
|
||||
}
|
||||
|
||||
options = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/* ###
|
||||
* 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 sarif.export;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.ISF.IsfObject;
|
||||
|
||||
public class SarifObject implements IsfObject {
|
||||
|
||||
public static boolean SARIF = true;
|
||||
|
||||
protected JsonObject message;
|
||||
protected String kind;
|
||||
protected String level;
|
||||
protected String ruleId;
|
||||
protected JsonArray locations;
|
||||
protected JsonObject properties;
|
||||
|
||||
protected JsonObject element;
|
||||
|
||||
public SarifObject(String key, String ruleKey, JsonElement element) {
|
||||
if (SARIF) {
|
||||
message = new JsonObject();
|
||||
message.addProperty("text", key);
|
||||
kind = "INFORMATIONAL";
|
||||
level = "NONE";
|
||||
ruleId = ruleKey;
|
||||
properties = new JsonObject();
|
||||
properties.add("additionalProperties", element);
|
||||
} else {
|
||||
this.element = (JsonObject) element;
|
||||
this.element.addProperty("key", key);
|
||||
this.element.addProperty("rule", ruleKey);
|
||||
}
|
||||
}
|
||||
|
||||
public SarifObject(String key, String ruleKey, JsonElement tree, Address min, Address max) {
|
||||
this(key, ruleKey, tree);
|
||||
if (min != null) {
|
||||
writeLocations(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
public SarifObject(String key, String ruleKey, JsonElement tree, AddressSetView body) {
|
||||
this(key, ruleKey, tree);
|
||||
if (body != null) {
|
||||
writeLocations(body);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeLocations(Address min, Address max) {
|
||||
if (SARIF) {
|
||||
locations = new JsonArray();
|
||||
JsonObject element = new JsonObject();
|
||||
locations.add(element);
|
||||
JsonObject ploc = new JsonObject();
|
||||
element.add("physicalLocation", ploc);
|
||||
JsonObject address = new JsonObject();
|
||||
ploc.add("address", address);
|
||||
address.addProperty("absoluteAddress", min.getOffset());
|
||||
address.addProperty("length", max.subtract(min) + 1);
|
||||
Address minAddress = min;
|
||||
if (minAddress.getAddressSpace().getType() != AddressSpace.TYPE_RAM) {
|
||||
JsonObject artifact = new JsonObject();
|
||||
ploc.add("artifactLocation", artifact);
|
||||
artifact.addProperty("uri", minAddress.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
element.addProperty("startAddress", min.toString(true));
|
||||
element.addProperty("stopAddress", max.toString(true));
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeLocations(AddressSetView set) {
|
||||
if (SARIF) {
|
||||
locations = new JsonArray();
|
||||
AddressRangeIterator addressRanges = set.getAddressRanges();
|
||||
while (addressRanges.hasNext()) {
|
||||
JsonObject element = new JsonObject();
|
||||
locations.add(element);
|
||||
AddressRange next = addressRanges.next();
|
||||
JsonObject ploc = new JsonObject();
|
||||
element.add("physicalLocation", ploc);
|
||||
JsonObject address = new JsonObject();
|
||||
ploc.add("address", address);
|
||||
address.addProperty("absoluteAddress", next.getMinAddress().getOffset());
|
||||
address.addProperty("length", next.getLength());
|
||||
Address minAddress = next.getMinAddress();
|
||||
if (minAddress.getAddressSpace().getType() != AddressSpace.TYPE_RAM) {
|
||||
JsonObject artifact = new JsonObject();
|
||||
ploc.add("artifactLocation", artifact);
|
||||
artifact.addProperty("uri", minAddress.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
element.addProperty("startAddress", set.getMinAddress().toString(true));
|
||||
element.addProperty("stopAddress", set.getMaxAddress().toString(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/* ###
|
||||
* 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 sarif.export;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import ghidra.program.model.data.ISF.AbstractIsfWriter;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class SarifWriterTask extends Task {
|
||||
|
||||
protected AbstractIsfWriter writer;
|
||||
protected JsonArray results;
|
||||
|
||||
public SarifWriterTask(String tag, AbstractIsfWriter writer, JsonArray results) {
|
||||
super(tag, true, false, true);
|
||||
this.writer = writer;
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
try {
|
||||
try {
|
||||
writer.getRootObject(monitor);
|
||||
JsonArray res = writer.getResults();
|
||||
for (JsonElement element : res) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
results.add(element);
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} catch (CancelledException e) {
|
||||
// user cancelled; ignore
|
||||
} catch (IOException e) {
|
||||
Msg.error("Export Data Types Failed", "Error exporting Data Types: " + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* ###
|
||||
* 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 sarif.export.bkmk;
|
||||
|
||||
import ghidra.program.model.data.ISF.IsfObject;
|
||||
import ghidra.program.model.listing.Bookmark;
|
||||
|
||||
public class ExtBookmark implements IsfObject {
|
||||
|
||||
String name;
|
||||
String comment;
|
||||
String kind;
|
||||
|
||||
public ExtBookmark(Bookmark b) {
|
||||
String category = b.getCategory();
|
||||
String comment = b.getComment();
|
||||
if (category != null && category.length() != 0) {
|
||||
name = category;
|
||||
}
|
||||
if (comment != null && comment.length() != 0) {
|
||||
this.comment = comment;
|
||||
}
|
||||
kind = b.getType().getTypeString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* ###
|
||||
* 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 sarif.export.bkmk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.listing.Bookmark;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import sarif.export.AbstractExtWriter;
|
||||
import sarif.export.SarifObject;
|
||||
import sarif.managers.BookmarksSarifMgr;
|
||||
|
||||
public class SarifBookmarkWriter extends AbstractExtWriter {
|
||||
|
||||
private List<Bookmark> bookmarks = new ArrayList<>();
|
||||
|
||||
public SarifBookmarkWriter(List<Bookmark> target, Writer baseWriter) throws IOException {
|
||||
super(baseWriter);
|
||||
bookmarks = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
genBookmarks(monitor);
|
||||
root.add("bookmarks", objects);
|
||||
}
|
||||
|
||||
private void genBookmarks(TaskMonitor monitor) throws CancelledException, IOException{
|
||||
monitor.initialize(bookmarks.size());
|
||||
for (Bookmark b : bookmarks) {
|
||||
ExtBookmark isf = new ExtBookmark(b);
|
||||
SarifObject sarif = new SarifObject(BookmarksSarifMgr.SUBKEY, BookmarksSarifMgr.KEY, getTree(isf), b.getAddress(), b.getAddress());
|
||||
objects.add(getTree(sarif));
|
||||
monitor.increment();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* ###
|
||||
* 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 sarif.export.code;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.data.ISF.IsfObject;
|
||||
|
||||
public class ExtCodeBlock implements IsfObject {
|
||||
|
||||
public ExtCodeBlock(AddressRange range) {
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user