Android hooking of abstract class method calls using Frida

qre0ct picture qre0ct · Jul 2, 2017 · Viewed 7.5k times · Source

I am trying to learn Frida and have experimented with a little so far. Things have mostly worked as thankfully I could find enough examples and tutorials to help me through. However, at this point in time, I am stuck with a very specific task at hand.

So let's say below is the Frida hook I am referring to:

Java.perform(function () {

var Activity = Java.use("myPack.myClass");

Activity.methodM1.overload('[B', 'java.lang.String').implementation = function (a, str) {

    var retval = this.methodM1(a, str);

    console.log("[*] return value4: "+retval);

    return retval;
};
});

Now as per my understanding so far, with Java.use above, I am saying that whenever an Object of myPackage.myClass is made and if that object calls the method, methodM1, get the control to my javascript function instead and do whatever is mentioned in there.

Which does the job as expected. However, the point (of interest for the sake of this discussion) was: IF the object gets made then this would happen.

Similarly, instead of Java.use, if we were talking about Java.choose() also, the situation would have been the same. So even in that case, we are saying that IF the object gets made, then call my callback.

Now, what would happen if I am trying to hook into a method of an abstract class So let's say I am trying to hook into the method

'static getInstance(String)' of java.security.KeyPairGenerator (which is an abstract class).

This class being abstract, it's object never really gets made at all. And the method being static, it is directly called using the class name itself. So in this case, neither Java.use() nor Java.choose() will be able to help (if my understanding above is correct).

So how do I hook into the getInstance() at all in such a situation?

Here's something that I have already tried:

Java.perform(
  function()
  {
    Java.enumerateLoadedClasses(
    { 
      onMatch: function(className)
      {
        if(className == "java.security.KeyPairGenerator")
        {
          var item = Java.use(className); 

          console.log("the PrivateKey class was just loaded");
          item.getInstance.overload('java.lang.String').implementation = function(str)
          {
            console.log("[*] This got called ");
            var ret = item.getInstance(str);
            console.log("[*] return value4: "+retval);
            return retval;
          }
        }
      },
      onComplete:function(){}
    });
  }
);

But this does not work. Again, my assumption here is that whenever the Object gets made and the getInstance() gets called, hook it. But here, KeyPairGenerator being an abstract class, it never really gets instantiated in the first place itself. I have also tried :

className.getInstance()

instead of

item.getInstance()

and that does not work either.

Answer

D4l3k picture D4l3k · Jul 4, 2017

Since Frida 10.1.2, early instrumentation works pretty well and can be used in your case to reach your target.

I tried your code on my device (Huwaei P8 lite \w Android 6.0). I am using latest Frida (10.1.4). As a result, using Java.enumerateLoadedClass (in your case) makes the application hang, and after a couple of seconds, frida crashes.

Error: abort was called
at u (frida/node_modules/frida-java/lib/android.js:512)
at p (java.js:2054)
at frida/node_modules/frida-java/index.js:105
at [anon] (repl1.js:28)
at frida/node_modules/frida-java/lib/vm.js:39
at y (frida/node_modules/frida-java/index.js:325)
at frida/node_modules/frida-java/index.js:305
at call (native)
at getPackageInfoNoCheck (Input:1)
[...]

The working solution is to rely on the early instrumentation capability of Frida:

/*
Working code. 
No need of Java.enumerateLoadedClasses
The following application https://github.com/obaro/SimpleKeystoreApp has been installed  on the target device for testing.

Call the current javascript script like so:
frida -U -f com.sample.foo.simplekeystoreapp -l myscript.js --no-pause
*/

function monitorKPG2()
{
    console.log("Starting early instrumentation test...");

    Java.perform(function () {
        var target = Java.use("java.security.KeyPairGenerator");

        console.log("Target = " + target);

        target.getInstance.overload("java.lang.String", "java.lang.String").implementation = function(alg, prov) {
            console.log("getInstance " + alg);

            this.getInstance(alg, prov);
        };
    }); 
}


console.log("Call me may be");
monitorKPG2();