GP-3832 importer/exporter for SARIF

This commit is contained in:
d-millar
2023-11-30 16:17:06 -05:00
committed by ghidra1
parent c225fac124
commit 31ca84453a
174 changed files with 15711 additions and 334 deletions
@@ -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;
}
}
}
@@ -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 {
@@ -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);
}
}
}
}
}
}
@@ -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();
}
}
}
@@ -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);
}
@@ -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());
}
}
@@ -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);
}
}
@@ -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;
@@ -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();
@@ -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();
}
}
@@ -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;
}
@@ -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();
@@ -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";
}
@@ -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?
}
@@ -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";
}
}
@@ -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;
}
@@ -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);
}
@@ -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();
}
@@ -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);
}
}
@@ -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;
}
@@ -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>
@@ -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);
}
@@ -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));
+1
View File
@@ -0,0 +1 @@
MODULE FILE LICENSE: lib/java-sarif-2.1.jar MIT
+36
View File
@@ -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>&nbsp;</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