Java Access Control

When working with Java, either at the level of the language or the VM, one often runs into "access level modifiers", which allow developers to mark methods, fields, and classes with the keywords "private", "protected", or "public" (or none, which is its own unique access level: "package-private").

Of course, when attempting to modify the code written by other developers, these access controls often get in our way: we might need to set a protected field, call a private method, or even extend a package-private class. Really, we just want full access to change whatever we might want.

Java Reflection

Thankfully, Java's reflection API actually allows us to bypass many of these security mechanisms without having to do anything "fancy": you can use APIs like Class.getDeclaredMethod to find a private method, and then setAccessible to remove the member's access restrictions.

In fact, this feature of Java's reflection API is sufficiently powerful to allow developers to do some highly counter-intuitive operations, such as modifying the value of String, an otherwise immutable class, as someone discovered while asking a question on StackOverflow ;P.

import java.lang.reflect.*;

public class MutableStrings {
   static void mutate(String s) throws Exception {
      Field value = String.class.getDeclaredField("value");
      value.setAccessible(true);
      value.set(s, s.toUpperCase().toCharArray());
   }
   public static void main(String args[]) throws Exception {
      final String s = "Hello world!";
      System.out.println(s); // "Hello world!"
      mutate(s);
      System.out.println(s); // "HELLO WORLD!"
   }
}

(Above code contributed to StackOverflow by polygenelubricants.)

"Blessed" ClassLoaders

However, the resulting code is highly verbose, horribly inefficient, and generally a pain to deal with. While it is possible to provide some higher-level helper methods, what we would really like to do is just access the things we want to use from Java in a "natural" way and then have the virtual machine's verifier just accept that we are doing something "epic" so that it gets out of our way.

As Substrate is already modifying the virtual machine, it becomes possible to implement this: access checks can simply be removed. Of course, removing all access checks would be very silly; in some situations (such as when interacting with a security manager) you might even create exploit vectors.

Instead, Substrate provides an API developers can use to selectively deactivate security checks for only code that they wish to exist outside of the normal security model: MSJavaBlessClassLoader. This API takes a Java ClassLoader—one with a hopefully-restricted load path—and informs the VM that all classes loaded by that ClassLoader (and only such ClassLoaders) should have complete access to all members.

Compile-Time Checks

Unfortunately, just because you now have access at runtime does not mean that your compiler will allow you to generate this code: the compiler is designed to detect these situations and print error messages; normally, the build is stopped, and no class files will result (so, no cheating).

Obviously, we could just implement our own compiler to generate class files however we wanted, but this would be a lot of work and would make it more difficult to embed into our current workflow. We would thereby much rather figure out ways to just trick the compiler into doing our bidding.

Public Stubs

The first tactic we have available to us is to generate our own code for every class file we want to modify, but where all of the things we wish to access have been changed to public. As we only need them at compile time, this is actually simpler than it might sound: we really only need a skeleton.

With this method, every time we start using a class, we make a new .java file for it where it is marked public. Then, for each member of that class we attempt to use, we declare it ourselves and likewise mark everything public. If we have a method, we can either leave it empty or make it abstract.

This method, unfortunately, cannot be used in all situations: at the Java bytecode level, calling a private method uses a different opcode (invokespecial) than calling a public one (invokevirtual). For more information, you can read Invoking Methods from the Java Virtual Machine Specification.

package android.webkit;
import android.content.Context;

public abstract class BrowserFrame {
    public Context mContext;
    public boolean mIsMainFrame;
    public WebViewCore mWebViewCore;
}

Aspect Weaving javac

A more sophisticated approach is to modify javac. Of course, as a Substrate extension developer, that would be a horribly ironic way of accomplishing our goals: it would be drastically preferred if we could somehow hack the existing Java compiler you already have to do what we need.

Thankfully, there exists a tool that makes doing this trivial: AspectJ, which can be thought of as similar to Substrate for desktop implementations of Java. (AspectJ is one of the many epic projects to have come out of Xerox PARC, first released in 2001.)

The overall technique is from a background called "aspect-oriented programming", and involves some specific terminology: developers write "aspects" that include "advice" that gets "woven" into "joinpoints" described by a "pointcut" to implement a "cross-cutting concern".

I have thereby gone ahead and implemented just such an aspect: one which hooks the various accessibility checks in the compiler and makes them all return "true" in all cases. The resulting code will fail to verify, but that is easily worked around using MSJavaBlessClassLoader.

Using the "Blessed" Compiler

Included as part of the Substrate SDK for Java is both a copy of AspectJ (aspectjweaver.jar) and the very simple aspect described in the previous section (substrate-bless.jar). One only needs to run javac using AspectJ specifying that aspect.

In order to do this, one first needs to know a few things about javac: it is written in Java, it has the class name com.sun.tools.javac.Main, and it is included with the JDK in tools.jar.

-javaagent:aspectjweaverjar

This tells the Java VM that we have an agent to load for purposes of "instrumentation". For more information, see java.lang.instrument.

-XX:-UseSplitVerifier

As of Java 7, the verifier changed in a way that many bytecode engineering platforms (such as AspectJ) have not yet taken into account. This argument reverts to the old implementation.

-cp: substrate-bless.jar:tools.jar

In order to load our aspect, it has to be on the system classpath. (Of course, so does the code we are modifying.) Inside of substrate-bless.jar there is a file META-INF/aop.xml that defines how AspectJ will instrument the compiler.

com.sun.tools.javac.Main

As mentioned earlier, this is the name of the class which implements javac inside of tools.jar.

Any arguments that are normally passed to javac should then come after the class name.

$ cat test.java
class A { private static int f; }
class B { private static int m() {
    return A.f; } }

$ javac test.java
test.java:3: error: f has private access in A
    return A.f; } }
            ^
1 error
$ locate /tools.jar
/usr/lib/jvm/java-7-openjdk-amd64/lib/tools.jar

$ java -javaagent:aspectjweaver.jar -XX:-UseSplitVerifier \
    -cp substrate-bless.jar:/usr/lib/jvm/java-7-openjdk-amd64/lib/tools.jar \
    com.sun.tools.javac.Main test.java

$ javap -p -c B
Compiled from "test.java"
class B {
  B();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  private static int m();
    Code:
       0: getstatic     #2                  // Field A.f:I
       3: ireturn
}