4

I'm working on command line tool for Android (think of am), trying to utilize the power of ByteBuddy to stub the static method getApplicationContext defined in android.security.KeyStore

However - the method seem to be invisible to ByteBuddy getDeclaredMethods when subclassing the android.security.KeyStore and hence it is unable to intercept it.

When using getMethods from the reflection API i'm able to list the method.

            Class AndroidKeyStore = Class.forName("android.security.KeyStore");
            Method[] keyStoreMethods =  new ByteBuddy()
                      .with(TypeValidation.DISABLED)
                      .subclass(AndroidKeyStore, ConstructorStrategy.Default.IMITATE_SUPER_CLASS)
                      .name("KeyStoreMasker")
                      .method(ElementMatchers.named("getApplicationContext"))
                      .intercept(SuperMethodCall.INSTANCE)
                      .make()
                      .load(getClass().getClassLoader(),
                            new AndroidClassLoadingStrategy
                            .Injecting(new File("/data/app/cmdutil")))
                      .getLoaded()
                      .getDeclaredMethods();
            for(i = 0; i < keyStoreMethods .length; i++) {
                System.out.println("method = " + keyStoreMethods[i].toString());
            }

When running the above, I was expecting to have a single method - getApplicationContext in the subclass. However the subclass doesn't contain any methods.

Replacing the call to getDeclaredMethods by getMethods I'm able to list all public method of the superclass.

By replacing the intercepted method to a non-static one (for example "state"), i'm able to list the method using ByteBuddy's getDeclaredMethods function:

Number of declared methods in keyStoreMethods: 2

method = public android.security.KeyStore$State AndroidKeyStoreMasker.state()

method = public android.security.KeyStore$State AndroidKeyStoreMasker.state(int)

So my final conclusion is that ByteBuddy (or my usage case with ByteBuddy) has some issue with static method visibility.

Reference to android.security.KeyStore.java: https://android.googlesource.com/platform/frameworks/base/+/master/keystore/java/android/security/KeyStore.java

Any help would be much appreaciated.

Community
  • 1
  • 1

1 Answers1

2

When creating a subclass, Byte Buddy is only able to intercept methods directly declared by a subclass or virtual methods of super classes. This is how the JVM works, static methods are dispatched directly on the receiver.

Byte Buddy is capable of redefining and retransforming existing classes, too, but this requires a Java agent which is not available on Android. Therefore, I am afraid that you need to find a non-static hook point to accomplish what you are trying. Alternatively, have a look at the MemberSubstitution where you can redirect such calls from your code. This also requires a retransformation but since it happens in your code, you can use Byte Buddy's build plugin.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • Thanks for responding. As I don't have any option for none static hook point I went for your second offering - using bytebuddy's build plugin for Maven. But - since the method from the called class is not defined in my code and referenced by reflection, I don't see how to make use of your suggestion. The `android.security.KeyStore` class is not available on build time. So - what did I miss ? – Almog Shachar May 07 '20 at 09:02
  • You'd need to substitute the calls in your code invoking the methods in question. This can be done in build time. – Rafael Winterhalter May 07 '20 at 13:31