Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a class dynamically that extends another dynamic class using reflection?

I'm fetching a class dynamically like this:

Class<?> clazz = Class.forName("com.android.systemui.quicksettings.QuickSettingsTile");

I'd like to create another class called DummyTile thats extends my previous class. It would have looked like this:

public class DummyTile extends QuickSettingsTile {

    public DummyTile(Context context, QuickSettingsController qsc) {
        super(context, qsc);
    }

    public void updateTile() {
        System.out.println("Hello");
    }

    @Override
    public void updateResources() {
        updateTile();
        super.updateResources();
    }

}

...but I'm not sure how to do this using reflection. This is the class that I'm trying to extend. I'm also not sure as to how I would override a method and initialise the object using the constructor. I've worked with very simple things using reflection but never dealt with extending another class dynamically.

If someone could point me in the right direction with some snippets, I'm confident that I'll be able to handle it from there.

like image 439
Mridang Agarwalla Avatar asked Oct 03 '22 16:10

Mridang Agarwalla


2 Answers

You can’t do that with reflection. You can create interface implementations dynamically using java.lang.reflect.Proxy but that’s it.

If you want more you have to use third-party libraries. But these libraries usually work on a lower level, e.g. byte code and require some experience. And they cannot be used in restricted environments.

like image 135
Holger Avatar answered Oct 11 '22 20:10

Holger


You can't really extend a class using reflection, but you can encapsulate it.
This is of course a major security risk and you should think hard about whether you want to allow this.

See: https://web.archive.org/web/20120201052157/http://initbinder.com/articles/hack_any_java_class_using_reflection_attack.html

Something like this should do the trick.

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.IllegalAccessException;
import java.lang.reflect.InvocationTargetException;
import java.lang.NoSuchMethodException;

public class Tester {

  private static String CLASS_NAME = "VictimClass";
  private static Class victimClass = null;
  private static Object victimClassObj = null;

  public static void main(String[] args) {
    victimClass = loadClass(victimClass, CLASS_NAME);
    printClassStructure();
    attack();
  }

  private static Class loadClass(Class clazz, String className) {
    Thread thread = Thread.currentThread();
    ClassLoader classLoader =
    thread.getContextClassLoader();

    try {
      clazz = Class.forName(className, true, classLoader);
    }
    catch (ClassNotFoundException e) {
      System.err.println("Error: could not find class: "
      + CLASS_NAME);
    }

    return clazz;
  }

  private static void printClassStructure() {

    Constructor[] constructors =
    victimClass.getDeclaredConstructors();
    for (Constructor c : constructors) {
      int modifier = c.getModifiers();
      System.out.println("Declared constructor name: "
          + c.getName() + "ntis accessible: "
          + c.isAccessible() + "ntis private: "
          + Modifier.isPrivate(modifier) + "n");
    }

  Method[] methods = victimClass.getDeclaredMethods();
    for (Method m : methods) {
      int modifier = m.getModifiers();
      System.out.println("Declared method name: " + m.getName()
          + "ntis accessible: "
          + m.isAccessible()
          + "ntis private: "
          + Modifier.isPrivate(modifier)
          + "ntis static: "
          + Modifier.isStatic(modifier) + "n");
    }

    Field[] fields = victimClass.getDeclaredFields();
    for (Field f : fields) {
      int modifier = f.getModifiers();
      System.out.println("Declared field name: " + f.getName()
          + "ntis accessible: "
          + f.isAccessible()
          + "ntis private: "
          + Modifier.isPrivate(modifier)
          + "ntis static: "
          + Modifier.isStatic(modifier)
          + "ntis final: "
          + Modifier.isFinal(modifier) + "n");
    }
  }

  private static void attack() {

    Field[] fields = victimClass.getDeclaredFields();
    Method[] methods = victimClass.getDeclaredMethods();
    Constructor[] constructors = victimClass.getDeclaredConstructors();

    //make constructor accessible
    constructors[0].setAccessible(true);

    System.err.println("Initiating reflection attack:");
    try {
      //create new object by invoking private constructor
      victimClassObj = constructors[0].newInstance(new Object[] {});

      //make static method accessible and get its value
      //please note: when invoking static method,
      //object represented by this Method is null
      methods[2].setAccessible(true);
      Object o = methods[2].invoke(null, new Object[] {});
      System.out.println("Got user ID from private static accessor: "
           + o.toString());

      //make method accessible and get its value
      methods[0].setAccessible(true);
      o = methods[0].invoke(victimClassObj, new Object[] {});
      System.out.println("Got original password from private accessor: "
           + o.toString());

      //make method accessible and set to it new value
      methods[1].setAccessible(true);
      System.out.println("Injecting new password using private mutator");
      methods[1].invoke(victimClassObj, new Object[] {"injected_password"});

      //get method’s its new value
      o = methods[0].invoke(victimClassObj, new Object[] {});
      System.out.println("Got injected password from private accessor: "
          + o.toString());

      //make field accessible and get its value
      fields[2].setAccessible(true);
      o = fields[2].get(victimClassObj);
      System.out.println("Got private field: " + o);

      //make field accessible and set to it new value
      System.out.println("Injecting value to a private field:");
      fields[2].set(victimClassObj, "new_default_value");

      //get field’s its new value
      o = fields[2].get(victimClassObj);
      System.out.println("Got updated private field: " + o);

      //make field accessible and get its value
      fields[1].setAccessible(true);
      o = fields[1].get(victimClassObj);
      System.out.println("Got private static field: " + o);

      //make field accessible and set to it new value
      System.out.println("Injecting value to a private static final field:");
      fields[1].set(null, new Integer(2));

      //get field’s its new value
      o = fields[1].get(victimClassObj);
      System.out.println("Got updated private static final field: " + o);

    }
    catch (InstantiationException e) {
      System.err.println("Error: could not instantiate: " + e);
    }

    catch (IllegalAccessException e) {
      System.err.println("Error: could not access: " + e);
    }

    catch (InvocationTargetException e) {
      System.err.println("Error: could not invoke the target: " + e);
    }
  }
}
like image 1
Johan Avatar answered Oct 11 '22 19:10

Johan