mirror of
https://github.com/advanced-threat-research/GhidraScripts
synced 2026-06-08 13:03:27 +00:00
Added Sharem.java related to the DEFCON 31 talk
This commit is contained in:
+467
@@ -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>CODE</code> or <code>DATA</code>, 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 <code>String</code> or <code>API Pointer</code>
|
||||
*/
|
||||
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>CODE</code> or <code>DATA</code>,
|
||||
* 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
|
||||
* <code>String</code> or <code>API Pointer</code>
|
||||
* @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>CODE</code> or <code>DATA</code>, 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 <code>String</code> or <code>API Pointer</code>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user