Frida is a dynamic code instrumentation toolkit. Frida allows you to rapidly develop tools to dynamically analyze and manipulate software. This is done by injecting Google’s V8 engine into the target process, allowing JavaScript to be executed inside the running process. From that point on you are able to access memory, hook functions and call native functions inside the injected process. On Android this also gives you access to the Dalvik VM if it present in the injected process, allowing you to hook and call Java functions. In this article we will give you a brief introduction on using Frida to analyze and manipulate an android application.
Setup
This article assumes that you are already familiar with Android and the Android sdk. We will start by installing Frida. For the installation you will need Python, pip, adb, and either Windows, Mac OS X, or Linux. We will be using Linux for our installation. Frida can be installed by issuing the following command.
$ sudo pip install frida.
Next you will need the grab the “frida-server” binary from the frida website (https://build.frida.re/frida/android/arm/bin/frida-server). Push the acquired binary to your android device and run it.
$ adb push frida-server /data/local/tmp/
$ adb shell "chmod 755 /data/local/tmp/frida-server"
$ adb shell "/data/local/tmp/frida-server"
At this point you are all setup to start using frida. You can test if everything is properly setup by running:
$ frida-ps -U
This should give you a list of processes running on you on your android device.
Android hooking example
Now that we have everything setup it’s time to start hooking some android application. In this example we will be hooking the twitter application to have it show us all URIs being requested as we are using the application. We will be doing this by hooking into functions of the square.okhttp.OkHttpClient Java class.
Frida uses a client/server model. In this case the server is frida-server, and the client is the code you write to interact with it. The following is a basic example using python to interact with the frida-server.
filename: android-hook.py
import frida
import sys
# get script name from the command line
scriptname = sys.argv[1]
# open script file
fd = open(scriptname, "r")
# get process to be instrumented from command line
procname = sys.argv[2]
# define callback function to receive and output messages
# received from server
def on_message(message, data):
print(message)
print(data)
# get connect to frida server through usb and attach to process
session = frida.get_usb_device().attach(procname)
# create script using script file opened above
script = session.create_script(fd.read())
# close file opened above
fd.close()
# setup callback using function defined above
script.on('message', on_message)
# load script into the process
script.load()
# read from stdin to keep script running
sys.stdin.read()
The above script takes two arguments when run from the command line. The first argument is the file with JavaScript that you want to inject and the second argument is the process that you want to instrument. Now that we have our client setup to receive messages from Frida we can now move on to the JavaScript that is to be injected inside the process.
To give us an idea of what we have to work with we will start by loading all the Java classes included inside the running process. This can be done by using the Java.enumerateLoadedClasses function.
filename: loadedClasses.js
"use strict";
/* Check if a Java/Dalvik/ART VM is available */
if (Java.available) {
Java.perform(function() {
/* enumerate loaded classes */
Java.enumerateLoadedClasses({
/* when a class is found send it to the client */
onMatch: function(className) {
send(className);
},
/* when we are done enumerating classes send "done" to the client */
onComplete: function() {
send("done");
}
});
});
/* if a Java/Dalvik/ART VM is not available */
} else {
send("Java not available in this process");
}
Now that we have our JavaScript we are ready to run our client. Make sure the twitter app is running on your device then run the following command.
$ python android-hook.js loadedClasses.js com.twitter.android
This should print to the screen all the Java classes loaded inside the com.twitter.android process. It should look similar to the following.
{u'type': u'send', u'payload': u'Lcom/twitter/android/initialization/h;'}
{u'type': u'send', u'payload': u'Lcom/twitter/library/api/aq;'}
{u'type': u'send', u'payload': u'Lcom/twitter/database/generated/gs;'}
{u'type': u'send', u'payload': u'Lcom/squareup/okhttp/v_1_5_1/OkHttpClient$1;'}
{u'type': u'send', u'payload': u'Landroid/app/Notification$BigTextStyle;'}
{u'type': u'send', u'payload': u'Lcom/twitter/model/livepipeline/d;'}
{u'type': u'send', u'payload': u'Lcom/twitter/android/timeline/af;'}
{u'type': u'send', u'payload': u'Lorg/apache/http/conn/params/ConnManagerPNames;'}
From the provided output we will be hooking the open function inside the com.squareup.okhttp.v_1_5_1.OkHttpClient Java class to get the URLs and their request method being used by the twitter app. To do that we will inject the following JavaScript.
filename: geturlsandmethod.js
"use strict";
/* Check if a Java/Dalvik/ART VM is available */
if (Java.available) {
/* ensure that the current thread is attached to the VM */
Java.perform(function() {
/* Get a JavaScript wrapper for the OkHttpClient class */
var httpclient = Java.use("com.squareup.okhttp.v_1_5_1.OkHttpClient");
/* overload the open method with our own function */
httpclient.open.overload("java.net.URL").implementation = function(url) {
console.log("request url:");
/* send a string repressentation of the java.net.URL parameter */
send(url.toString());
/* execute the original function and grab the return value */
var retval = this.open(url);
console.log("request method:");
/* use the returned HttpURLConnection object to get and send the
* request method
*/
send(retval.getRequestMethod());
/* return the above aquired return value */
return retval;
}
});
} else {
/* if a Java/Dalvik/ART VM is not available */
send("Java not available in this process");
}
We now run our client.
$ python android-hook.py geturlsandmethod.js com.twitter.android
As we interact with the twitter app we get output similar to the following.
request url:
{u'type': u'send', u'payload': u'<https://api.twitter.com/1.1/friendships/show.json?source_id=741321085895999488&target_id=14230524>'}
None
request method:
{u'type': u'send', u'payload': u'GET'}
None
request url:
{u'type': u'send', u'payload': u'<https://api.twitter.com/1.1/users/extended_profile.json?id=14230524&include_birthdate=false>'}
None
request method:
{u'type': u'send', u'payload': u'GET'}
None
request url:
{u'type': u'send', u'payload': u'<https://api.twitter.com/1.1/saved_searches/list.json>'}
None
request method:
{u'type': u'send', u'payload': u'GET'}
None
This is just a quick example of what one can accomplish by using Frida when analyzing Android applications. These examples show Frida being used to monitor a function being called, and retrieving the data passed to and returned by said function. Frida can also be used to manipulate the result of a function. More information about Frida can be found at https://frida.re.
This post has been updated based on feedback from @avicoder.