Home > Mobile Pentesting > Android > Android Dynamic Analysis

Android Dynamic Analysis
Android Mobile Pentesting

Dynamic Analysis


It’s about testing and evaluating a program while software is running.

SSL pinning


Security methodology to ensure that app’s traffic isn’t being intercepted (prevent from Man In The Middle)
Traffic is verified using certificate
Even if we can import certificate into phone the apps may not trust this certificate

  • Proxies
    • Burpsuite (common)
    • Proxyman (used on MACOS only)

even when you configure the proxy and add the certificate, some apps may not work.
because apps won’t be able to authenticate the server (which is burp in this case)

There are many ways to bypass SSL like Frida, objection.

Frida


In the smart for there’s JVM (Java Virtual Machine) and all the app code is stored there.
any class code is loaded from JVM when it’s used.
Frida is hooking into this JVM and manipulates the run time data, so the code can be changed dynamically from there.

Frida consists of FRIDA SERVER on the smart phone and FRIDA CLIENT on our laptop.
We can communicate with these instances using JavaScript or python.

The Frida server hooks into the JVM using JS and the server now can modify the code in the memory, we get access to those classes, methods, etc…

Installation of FRIDA


  • Frida client on our laptop
    • pip3 install frida-tools
  • Frida server on the mobile or emulator
    • install the server from here
    • decompress frida server file and move it to the emulator using adp push <path to frida server> /data/local/tmp
    • /data/local/tmp is the target path and we choosed it because it’s where we can ran executables
    • chmod +x frida server in the emulator (adb shell)
    • run frida binary and now the server is running (need root privileges)

you have frida server running we can test the connection from the client using frida-ps which will list the processes running on the smart phone and send the output to the client.

hooking


It’s the process of overriding method as example and this is why frida so powerful
first we will start with a basic example of hooking an activity and overriding onResume() method to print anything.

onResume() is method called when we move the app to background and return it to foreground again

we will use this script

Java.perform(function() {
  const Activity = Java.use('android.app.Activity');
  Activity.onResume.implementation = function () {
    send('onResume() got called! Let\'s call the original implementation');
    this.onResume();
  };
});

and we will break it down later, but uptil now It manipulates onResume() method by making it send a specific message
So this message will be sent when we go to background and start the app again in the foreground.
hooking

Calling a method

in case of static methods

  • Get a reference to the class
  • call the static method like this class-reference.method

in case of Class methods

  • Get a reference to the class
  • create a reference to the object from this class using var playerInstance = playerClassReference.$new()
  • call the method like this object-reference.method

    Note that this process creates object each time !! we want to control specific object (we will know that later)

working with existing objects

Now we want to manipulate existing object rather than creating a new one.
This operation is done by scanning the memory for object of the class we want and once we catch the object we can manipulate it.

Frida can do this task in easy way using this Java.choose(className, callbacks)

  • classname: the whole name including the package
  • callbacks: consists of functions like:
    • onMatch(instance): contains code if we found an instance
    • onComplete(): contains code if the scan is finished
Java.perform(function() {

    // We are scanning the application memory for existing instances of the Player - Class.
    Java.choose('com.apphacking.fridafunc.Player', {
        // onMatch - Callback => If an instance has been found,
        onMatch: function(instance) {
            // code if an instance has been found
            send("An instance of the player class has been found = " + instance)
            instance.lives.value = 9005;  // to update a variable we use .value
            instance.increaseLive();
            send("The instance lives = " + instance.lives.value);

        },
        // onComplete - Callback => Finished scanning the app memory regarding to the instance.
        onComplete: function() {
            send("Frida has finished scanning the application memory for player instances")
        }

    })
})

Instance as parameter
consider we have a boss class which has object of item class as a parameter passed to the constructor of the boss.
If we want to create an instance of the boss we need an instance of the item first, so our steps will be:

  • Getting the classs reference of the item
  • Creating a new Item Instance
  • Getting the class reference of the boss
  • Creating a new boss instance

and this as the example of the script

Java.perform(function() {

    // Getting the class reference of the item class.
    var itemClassReference = Java.use('com.apphacking.fridainstance.Item');
    // Creating a new item instance
    var itemInstance = itemClassReference.$new(100000);

    // Getting the class reference of the boss class.
    var bossClassReference = Java.use('com.apphacking.fridainstance.Boss');

    // Creating a new boss instance with the newly created item-instance as parameter.
    var bossInstance = bossClassReference.$new(itemInstance);
    bossInstance.hitpoints = 900000;

})

We created a new instance of the item class and we used it, but what if we want to use an existing instance ??
very simple, instead of creating a new object of item we can search in the memory for an existing one and once we find the instance we use it to create the instance of boss object.

Java.perform(function(){

    // Scanning the app memory for an exisiting item instance
    Java.choose('com.apphacking.fridainstance.Item', {
        // If the item instance has been found
        onMatch: function(itemInstance) {
            // Getting the boss class reference
            var bossClassReference =  Java.use('com.apphacking.fridainstance.Boss');
            // Creating a new bossInstance with the item instance (found) as parameter
            var bossInstance = bossClassReference.$new(itemInstance);
        },
        onComplete: function() {
            // Done scanning the app memory
            send("Scanning done");

        }
    });
})

hooking a constructor

The constructor is the function which is called when the object is created and hooking it is a little bit different

in case of having one constructor

classReference.$init.implementation = function(){
}

in case of having more than one constructor we will user overloading

classReference.$init.overload('int').implementation = function(){
}

UI threads

in some situations you will need to create instance of object whose constructor interacts with the ui.
in this case if we just created the instance we will get error.

The solution is schedule this operation on the main thread (UI thread) and this is done like this

Java.scheduleOnMainThread(function(){
    var alienInstance = classRef.$new(param);
})

now the creation of the instance is done on the UI thread and the error is prevented.

Hooking the Native Development Kit (NDK)

We hooked java functions running in JVM, but what if we have native code writted in CPP as example.
To hook a native fucnion we use interceptor.attach(target,callbacks)

  • target: pointer of the function we want to hook
  • callbacks: can be
    • onEnter: easy access to params
    • onLeave: easy access to return values

CPP code is included within Java code like this

static {
    System.loadlibrary("native.lib");
}

native.lib which is CPP code is now loaded within the current activity context
We need to build a bridge from Java world to CPP to call the functions within the CPP code
this can be done like this

public native String encryptString(String pass,int round);

This is the prototype of this function in java world now.
and now we can call this function like this

<snip>
...
encryptString(pwd,round);
...
<snip>

when we look at the cpp code we will see it’s different in argumens as example

Java_com_apphacking_ndkfrida_MainActivity_decryptString(
    JNIEnv* env,jobject,jstring password,int rotation
){
    ...
    return env->NewStringUTF(encryptedvar.c_str());
}

We have these parameters:
- JNIEnv* env: pointers to all the NDKfunctions
- jobject: pointer to the current object
- jstring password: is corresponding to String pass in the java and we are concerned with the data type not the name
- int rotation: is corresponding to int round
and this in return value we make use of the env pointer and NewStringUTF() to make a compatible string in java world.

Note: if we have the prototype of the function in java world as we saw above, we can hook and modify it rather than hooking the native function itself.

In other cases we will be forced to interact with the native functions, so let’s see what can we do
To attach to the function we need a pointer to this function, with frida we can do this:

  • listing the NDK methods
  • now you have the pointer of each function
  • you also have access to parameters and return value through onEnter and onLeave functions
    hookingNDK methods