-
Notifications
You must be signed in to change notification settings - Fork 7
/
Sharem.java
467 lines (421 loc) · 14.6 KB
/
Sharem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
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;
}
}
}