/* * Java Payloads. * * Copyright (c) 2010, Michael 'mihi' Schierl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package javapayload.builder; import java.util.Arrays; import com.sun.jdi.ArrayReference; import com.sun.jdi.ByteValue; import com.sun.jdi.ClassObjectReference; import com.sun.jdi.ClassType; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; /** * Injects classes into a debugged process via JDI and optionally creates an instance of them using the default constructor. */ public class JDWPClassInjector { private final ThreadReference thread; private final VirtualMachine virtualMachine; private final ObjectReference myClassLoader; private final ClassType _Class; // java.lang.Class private final ClassType _Array; // java.lang.reflect.Array private final ClassType _URLClassLoader; // java.net.URLClassLoader private final ClassType _byte; // byte /** * Create a new class injector. * * @param tr * A suspended thread in the target thread that allows code execution and has no step requests pending. */ public JDWPClassInjector(ThreadReference thread) throws Exception { this.thread = thread; this.virtualMachine = thread.virtualMachine(); // java.lang.Class is guaranteed to be loaded in any JVM, // so pick it from the classes cache this._Class = (ClassType) virtualMachine.classesByName("java.lang.Class").get(0); // load more types via reflection this._Array = loadClass("java.lang.reflect.Array"); this._URLClassLoader = loadClass("java.net.URLClassLoader"); final ClassType _Byte = loadClass("java.lang.Byte"); this._byte = (ClassType) ((ClassObjectReference) _Byte.getValue(_Byte.fieldByName("TYPE"))).reflectedType(); // get an empty URL[] and use it to create an URLClassLoader final ArrayReference urls = buildArray(loadClass("java.net.URL"), 0); myClassLoader = newInstance(_URLClassLoader, "([Ljava/net/URL;)V", new Value[] { urls }); } /** * Create a new array via reflection * * @param baseType * base type of the array * @param length * length of the array */ private ArrayReference buildArray(ClassType baseType, int length) throws Exception { return (ArrayReference) invokeStaticMethod(_Array, "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;", new Value[] { baseType.classObject(), virtualMachine.mirrorOf(length) }); } /** * Inject a class * * @param clazz * the "class file". * @param argsForInstantiate * arguments to instantiate the class with, or null to not instantiate it * @return the {@link ClassType} instance */ public ClassType inject(byte[] clazz, String argsForInstantiate) throws Exception { // build a byte array and fill it final ArrayReference array = buildArray(_byte, clazz.length); ByteValue[] byteValues = new ByteValue[clazz.length]; for (int i = 0; i < clazz.length; i++) { byteValues[i] = virtualMachine.mirrorOf(clazz[i]); } array.setValues(Arrays.asList(byteValues)); // load the class with the class loader final ObjectReference loadedClazz = (ObjectReference) invokeMethod(myClassLoader, _URLClassLoader, "defineClass", "([BII)Ljava/lang/Class;", new Value[] { array, virtualMachine.mirrorOf(0), virtualMachine.mirrorOf(clazz.length) }); final ClassType _clazz = (ClassType) ((ClassObjectReference) loadedClazz).reflectedType(); if (argsForInstantiate != null) { // prepare the class invokeMethod(myClassLoader, _URLClassLoader, "resolveClass", "(Ljava/lang/Class;)V", new Value[] { loadedClazz }); invokeMethod(loadedClazz, _Class, "getMethods", "()[Ljava/lang/reflect/Method;", new Value[0]); // invoke its constructor final ObjectReference loadedInstance = newInstance(_clazz, "()V", new Value[0]); // invoke its go method invokeMethod(loadedInstance, _clazz, "go", "(Ljava/lang/String;)V", new Value[] { virtualMachine.mirrorOf(argsForInstantiate) }); } return _clazz; } /** * Invoke a method via JDI. * * @param object * The object/class to invoke the method on * @param _class * The class the method belongs to * @param name * The name of the method * @param signature * The signature of the method * @param parameters * Parameters to use * @return The return value */ private Value invokeMethod(ObjectReference object, ClassType _class, String name, String signature, Value[] parameters) throws Exception { final Method method = _class.concreteMethodByName(name, signature); if (method == null) { throw new IllegalArgumentException("Method not found: " + name + " " + signature); } return object.invokeMethod(thread, method, Arrays.asList(parameters), 0); } /** * Invoke a static method via JDI. * * @param _class * The class the method belongs to * @param name * The name of the method * @param signature * The signature of the method * @param parameters * Parameters to use * @return The return value */ private Value invokeStaticMethod(ClassType _class, String name, String signature, Value[] parameters) throws Exception { return _class.invokeMethod(thread, _class.concreteMethodByName(name, signature), Arrays.asList(parameters), 0); } /** * Load a class via JDI * * @param className * The class to load * @return The loaded class */ private ClassType loadClass(String className) throws Exception { return (ClassType) ((ClassObjectReference) _Class.invokeMethod(thread, _Class.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;"), Arrays.asList(new Value[] { virtualMachine.mirrorOf(className) }), 0)).reflectedType(); } /** * Create a remote instance of a class via JDI. * * @param _class * Class * @param signature * Signature of the constructor * @param parameters * Constructor parameters * @return * @return the new instance */ private ObjectReference newInstance(ClassType _class, String signature, Value[] parameters) throws Exception { return _class.newInstance(thread, _class.concreteMethodByName("", signature), Arrays.asList(parameters), 0); } }