diff --git a/Sharem.java b/Sharem.java
new file mode 100644
index 0000000..669762d
--- /dev/null
+++ b/Sharem.java
@@ -0,0 +1,467 @@
+//Runs SHAREM and gets the resulting output, which is then annotated within Ghidra. This fixes dissassembly mistakes, adds context and arguments to function calls, and creates data points.
+//@author Trellix (by Max ' Libra' Kersten)
+//@category shellcode analysis
+//@keybinding
+//@menupath
+//@toolbar
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import com.google.gson.Gson;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.data.ByteDataType;
+import ghidra.program.model.data.DWordDataType;
+import ghidra.program.model.data.PointerDataType;
+import ghidra.program.model.data.QWordDataType;
+import ghidra.program.model.data.StringDataType;
+import ghidra.program.model.data.WordDataType;
+import ghidra.program.model.listing.Data;
+
+public class Sharem extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+ // The directory in which the command is executed, to be edited prior to using the script
+ File workingDirectory = new File("C:\\path\\to\\sharem");
+ // The command to execute within said working directory, to be edited prior to using the script
+ String command = "C:\\path\\to\\python.exe main.py [architecture as in -r32 or -r64] C:\\path\\to\\shellcode.bin";
+
+ try {
+ /*
+ * Executes the given command from within the given working directory. This
+ * function only returns once the command has been executed.
+ */
+ execute(workingDirectory, command);
+ } catch (Exception ex) {
+ // Print the error message
+ printerr(ex.getMessage());
+ // Return early
+ return;
+ }
+
+ // Instantiate a new Gson object for later use
+ Gson gson = new Gson();
+
+ // Declare the JSON file variable, based on the working directory
+ File jsonFile = new File(
+ workingDirectory.getAbsolutePath() + "\\sharem\\sharem\\sharem\\logs\\default\\jsondefaultdisasm.json");
+
+ // Read the file and store the result in a string
+ String json = Files.readString(jsonFile.toPath());
+
+ // Convert the raw JSON into a Java object
+ SharemObject result = gson.fromJson(json, SharemObject.class);
+
+ // Iterate over the objects
+ for (SharemSubObject object : result.getObjects()) {
+ // If a given object's comment is not null, empty, nor white space
+ if (object.getComment().isBlank() == false) {
+ // Get the offset in hexadecimal format
+ long offset = Long.parseLong(object.getAddress().substring(2), 16);
+ // Set a comment at the given offset, with the given comment
+ setPreComment(toAddr(offset), object.getComment());
+ // Create a string to print debug information
+ String message = "Commented \"" + object.getComment() + "\" at " + object.getAddress();
+ // Print the message
+ println(message);
+ }
+
+ // If the type is CODE
+ if (object.getBytes().equalsIgnoreCase("CODE")) {
+ // Get the value of the bytes
+ byte[] sharemValues = getBytesFromSharemObject(object);
+ if (sharemValues == null) {
+ continue;
+ }
+ // Get the bytes from Ghidra's listing
+ byte[] ghidraValues = getBytes(toAddr(object.getAddress()), object.getSize());
+ // If the values aren't equal
+ if (Arrays.compare(sharemValues, ghidraValues) > 0) {
+ // Clear the listing
+ clearListing(toAddr(object.getAddress()));
+ // Set the bytes as provided by SHAREM
+ setBytes(toAddr(object.getAddress()), sharemValues);
+ // Disassemble the bytes
+ disassemble(toAddr(object.getAddress()));
+ }
+ } else if (object.getBytes().equalsIgnoreCase("DATA")) { // If the type is DATA
+ if (object.getDataType().equalsIgnoreCase("String")) { // if the type is a string
+ // Get the data at the given address
+ Data data = getDataAt(toAddr(object.getAddress()));
+ // If the data is not null
+ if (data != null) {
+ // Compare the length of the data and the size mentioned in the SHAREM object
+ if (data.getLength() != object.getSize()) {
+ // Get the end address
+ Address end = toAddr(object.getAddress()).add(toAddr(object.getSize()).getOffset() - 1);
+ // Get the start address
+ Address start = toAddr(object.getAddress());
+ // Clear the listing
+ clearListing(start, end);
+ // Create a string at the given address
+ createAsciiString(toAddr(object.getAddress()));
+ }
+ } else {
+ // Get the end address
+ Address end = toAddr(object.getAddress()).add(toAddr(object.getSize()).getOffset() - 1);
+ // Get the start address
+ Address start = toAddr(object.getAddress());
+ // Clear the listing
+ clearListing(start, end);
+ // If no data is present, simply create a string at the address
+ createData(toAddr(object.getAddress()), StringDataType.dataType);
+ }
+ } else if (object.getDataType().equalsIgnoreCase("API Pointer")) { // If the type is a pointer
+ // Get the data at the address
+ Data data = getDataAt(toAddr(object.getAddress()));
+ // If the data is not present
+ if (data == null) {
+ // Create the pointer
+ createData(toAddr(object.getAddress()), PointerDataType.dataType);
+ }
+ // Set a comment with the instruction to provide context
+ setPreComment(toAddr(object.getAddress()), object.getInstruction());
+ } else if (object.getDataType().equalsIgnoreCase("DATA")) {
+ // Get the data at the address
+ Data data = getDataAt(toAddr(object.getAddress()));
+
+ /*
+ * If the data is not null, it exists, and can thus be removed, since it will be
+ * overwritten
+ */
+ if (data != null) {
+ // Get the end address
+ Address end = toAddr(object.getAddress()).add(toAddr(object.getSize()).getOffset() - 1);
+ // Get the start address
+ Address start = toAddr(object.getAddress());
+ // Clear the listing
+ clearListing(start, end);
+ }
+
+ // Handle different sizes with regards to data creation
+ switch (object.getSize()) {
+ case 1:
+ createData(toAddr(object.getAddress()), ByteDataType.dataType);
+ break;
+ case 2:
+ createData(toAddr(object.getAddress()), WordDataType.dataType);
+ break;
+ case 4:
+ createData(toAddr(object.getAddress()), DWordDataType.dataType);
+ break;
+ case 8:
+ createData(toAddr(object.getAddress()), QWordDataType.dataType);
+ break;
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * A helper function to get the bytes from the given disassembly object's hex
+ * value field which contains the instruction in hexadecimal format
+ *
+ * @param object the disassembly object
+ * @return the raw bytes of the given instruction, if any
+ */
+ private byte[] getBytesFromSharemObject(SharemSubObject object) {
+ byte[] bytes = new byte[object.getSize()];
+ String[] values = object.getHex().split(" ");
+
+ for (int i = 0; i < values.length; i++) {
+ try {
+ bytes[i] = (byte) Integer.parseInt(values[i], 16);
+ } catch (Exception ex) {
+ /*
+ * Ignore entries where the value ends with dots, as those aren't hex values.
+ * Returning null ensures the caller to omit these bytes
+ */
+ return null;
+ }
+
+ }
+ return bytes;
+ }
+
+ /**
+ * Determines if the current operating system is Windows.
+ *
+ * @return true if the operating system is Windows, false if it is not.
+ */
+ public static boolean isWindows() {
+ /*
+ * If the os.name property of the Java VM contains "windows", the system is
+ * Windows based
+ */
+ if (System.getProperty("os.name").toLowerCase().contains("windows")) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A helper function to launch a new process via the system's shell
+ *
+ * @param workingDirectory the working directory of the process
+ * @param command the command to execute
+ * @throws Exception if anything goes wrong
+ */
+ public void execute(File workingDirectory, String command) throws Exception {
+ try {
+ String[] processName = new String[1];
+ // Check if the OS is windows
+ if (isWindows()) {
+ processName[0] = "cmd";
+ } else { // Use the shell if the OS is not Windows
+ processName[0] = "sh";
+ }
+ // Start a new shell
+ Process p = Runtime.getRuntime().exec(processName, null, workingDirectory);
+ // Get the standard input
+ PrintWriter stdin = new PrintWriter(p.getOutputStream());
+ // Start the command via the shell
+ stdin.println(command);
+ // Close the stream
+ stdin.close();
+ // Wait until the process terminates
+ p.waitFor();
+ } catch (Exception ex) {
+ /*
+ * Throw an exception if anything goes wrong, which is used to notify the
+ * analyst
+ */
+ throw new Exception("Error while launching SHAREM! Error:\n\n" + ex.getMessage());
+ }
+ }
+
+ class SharemSubObject {
+
+ /**
+ * The starting address within the binary where the reference to is made
+ */
+ private String address;
+
+ /**
+ * The instruction, as extracted by SHAREM
+ */
+ private String instruction;
+
+ /**
+ * The hex value of the instruction
+ */
+ private String hex;
+
+ /**
+ * The size of the object (i.e. the length of a string, or the size of an
+ * instruction)
+ */
+ private String size;
+
+ /**
+ * Is either CODE or DATA, indicating what the type of
+ * the object is
+ */
+ private String bytes;
+
+ /**
+ * Provides further information about the data type, if this object references
+ * DATA. The optional values are String or API Pointer
+ */
+ private String dataType;
+
+ /**
+ * Defines how data is accessed
+ */
+ private String dataAccessed;
+
+ /**
+ * Gets the string representation of the data (at the given address with the
+ * given length). This can be garbage when not dealing with strings (i.e.
+ * instructions)
+ */
+ private String string;
+
+ /**
+ * Gets a SHAREM made comment for this object
+ */
+ private String comment;
+
+ /**
+ * A label which contains an address, if present. Redundant field.
+ */
+ private String label;
+
+ /**
+ * Creates an instance of a single instruction/piece of data that is emitted by
+ * SHAREM.
+ *
+ * @param address the starting address within the binary where the
+ * reference to is made
+ * @param instruction the instruction, as extracted by SHAREM
+ * @param hex the hex value of the instruction
+ * @param size the size of the object (i.e. the length of a string, or
+ * the size of an instruction)
+ * @param bytes is either CODE or DATA,
+ * indicating what the type of the object is
+ * @param dataType provides further information about the data type, if this
+ * object references DATA. The optional values are
+ * String or API Pointer
+ * @param dataAccessed defines how data is accessed
+ * @param string gets the string representation of the data (at the given
+ * address with the given length). This can be garbage when
+ * not dealing with strings (i.e. instructions)
+ * @param comment gets a SHAREM made comment for this object
+ * @param label a label which contains an address, if present. Redundant
+ * field.
+ */
+ public SharemSubObject(String address, String instruction, String hex, String size, String bytes,
+ String dataType, String dataAccessed, String string, String comment, String label) {
+ this.address = address;
+ this.instruction = instruction;
+ this.hex = hex;
+ this.size = size;
+ this.bytes = bytes;
+ this.dataType = dataType;
+ this.dataAccessed = dataAccessed;
+ this.string = string;
+ this.comment = comment;
+ this.label = label;
+ }
+
+ /**
+ * The starting address within the binary where the reference to is made
+ *
+ * @return the address as a string
+ */
+ public String getAddress() {
+ return address;
+ }
+
+ /**
+ * The instruction, as extracted by SHAREM
+ *
+ * @return the instruction as a string
+ */
+ public String getInstruction() {
+ return instruction;
+ }
+
+ /**
+ * The hex value of the instruction
+ *
+ * @return the hex value as a string (split by spaces, not denoted by "0x")
+ */
+ public String getHex() {
+ return hex;
+ }
+
+ /**
+ * The size of the object (i.e. the length of a string, or the size of an
+ * instruction)
+ *
+ * @return the size as a string
+ */
+ public int getSize() {
+ return Integer.parseInt(size);
+ }
+
+ /**
+ * Is either CODE or DATA, indicating what the type of
+ * the object is
+ *
+ * @return the type, as a string
+ */
+ public String getBytes() {
+ return bytes;
+ }
+
+ /**
+ * Provides further information about the data type, if this object references
+ * DATA. The optional values are String or API Pointer
+ *
+ * @return the more granular type, as a string
+ */
+ public String getDataType() {
+ return dataType;
+ }
+
+ /**
+ * Defines how data is accessed
+ *
+ * @return the way the data is accessed, as a string
+ */
+ public String getDataAccessed() {
+ return dataAccessed;
+ }
+
+ /**
+ * Gets the string representation of the data (at the given address with the
+ * given length). This can be garbage when not dealing with strings (i.e.
+ * instructions)
+ *
+ * @return the value of the object
+ */
+ public String getString() {
+ return string;
+ }
+
+ /**
+ * Gets a SHAREM made comment for this object
+ *
+ * @return the comment
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * A label which contains an address, if present. Redundant field.
+ *
+ * @return the label as a string
+ */
+ public String getLabel() {
+ return label;
+ }
+ }
+
+ /**
+ * The JSON output from SHAREM is an array of items. For ease-of-access and
+ * ease-of-handling, a Java class is used as a wrapper around the array
+ *
+ * @author Max 'Libra' Kersten for Trellix
+ *
+ */
+ class SharemObject {
+
+ /**
+ * The array of entries from SHAREM
+ */
+ private SharemSubObject[] disassembly;
+
+ /**
+ * Creates an instance of this wrapper object
+ *
+ * @param disassembly the array of objects which contain the disassembly of the
+ * analysed shellcode
+ */
+ public SharemObject(SharemSubObject[] disassembly) {
+ this.disassembly = disassembly;
+ }
+
+ /**
+ * Gets the SHAREM objects
+ *
+ * @return the SHAREM objects
+ */
+ public SharemSubObject[] getObjects() {
+ return disassembly;
+ }
+ }
+}