Introduction

In this article, we’ll specifically take a look at Frida.re to inject JavaScript into native applications to explore them in order to gain a deeper understanding of their internals. First, we should mention that frida.re is supported on all major platforms, namely Windows, Mac, Linux, iOS as well as Android and provides a number of benefits when exploring application internals.

Frida is a dynamic code instrumentation toolkit, which injects JavaScript into native applications on various platforms. By injecting JavaScript into the process namespace and interacting with it, it gives us a complete access to the process’s memory, which enables us to do various things, like hook API function. Frida can be used to enhance a program with additional functionality without actually changing and recompiling the code; we can just use Frida, and inject the missing functionality into the process’s namespace and use as though it was implemented by the application itself. We should emphasize that Frida is written in C and injects Google’s V8 JavaScript engine into the target process, which we can use to communicate with the application and use it for various things. In order to add additional functionality, the JavaScript running inside the process’s namespace and our program are using a communication channel for exchanging messages.

Basically, Frida is a dynamic code instrumentation toolkit, which injects JavaScript into the native process, which can be scripted to control the process in question. With Frida, we can hijack any API function call to modify its behavior to make it do something it was not intended from API point of view. Basically, in Frida, a Google V8 engine code is injected into the process, while Python API is used to talk to the JS code inside the process.

Getting Frida up and running

At first, we must obtain Frida in order to be able to play with it. We can do so by using Python virtual environment, which can be done by instantiating a new virtual environment by using virtualenv command, activating the virtual environment by sourcing it and installing Frida with pip install.

# virtualenv venv
# source venv/bin/activate
# pip install frida
Once the Frida is installed, the following commands are available.

  • frida
  • frida-discover
  • frida-ls-devices
  • frida-ps
  • frida-repl
  • frida-trace

The most useful command is frida-trace, which enables function tracing. The -I and -X parameters can be used to include and exclude a module, while the -i and -x are used to include and exclude a function. Frida can trace the following:

  • Exported Functions: exported functions usually used by shared libraries can be traced without problems with Frida. We only have to specify the function we would like to trace by passing the -i parameter to frida-trace.
  • Symbol Functions: at a time of this writing, Frida doesn’t yet support hooking functions based on a symbol, but it supports hooking functions base on their address, which we can use to hook an arbitrary function. First, we have to determine where the function is by getting its relative address and hook it with -a option.

A Simple Example

Let’s first write a simple program, which uses an exported function (via a shared library) as well as a symbol function. First, we have to create a library foo, which consists of a header file foo.c as well as actual implementation code foo.c.

The following is a foo.h header file containing the prototype definitions for functions sum, sub and isfibonnaci.

#ifndef FOO_H

#define FOO_H

extern int sum(int x, int y);

extern int sub(int x, int y);

extern int isfibonnaci(int x);

#endif

The following is a foo.c code file containing the actual implementation of functions sum, sub and isfibonnaci.

#include “foo.h”
#include <math.h>

int isfibonnaci(int x) {

double x1 = 5 * pow(x,2) + 4;

double x2 = 5 * pow(x,2) – 4;

long x1sqrt = (long)sqrt(x1);

long x2sqrt = (long)sqrt(x2);

return (x1sqrt*x1sqrt == x1) || (x2sqrt*x2sqrt == x2);

}

int sum(int x, int y) {

return x + y;

}

int sub(int x, int y) {

return x – y;

}

The following is a code file main.c containing the actual program that uses the functions sum and isfibonnaci implemented in a library foo. Note that the while loop continues until x is lower than 1000, after which the while loop will terminate execution and the program will complete. The while loop itself contains a sleep function to cause the program to sleep for one second in each iteration, which gives us enough time to attach to a process with Frida. Note that frida-trace tool can only attach to a running process, so we can’t run our program and then attach to it with frida-trace, because the program will already terminate its execution and there will be nothing to which to attach it.

#include <stdio.h>

#include <stdlib.h>

#include “foo.h”

int main(int argc, char **argv) {

printf(“The function sum() is at %p.\n”, sum);

printf(“The function sub() is at %p.\n”, sub);

printf(“The function isfibonnaci() is at %p.\n”, isfibonnaci);

int x = 0;

int t = 0;

while(x < 1000) {

int r = isfibonnaci(x);

if(r==1) {

printf(“Fibonnaci number found: %d; the next fibnum is supposed to be: %d.\n”, x, sum(x,t));

t = x;

}

x += 1;

sleep(1);

}

return 0;

}

First, we have to compile or assemble the source files, but not link them to get an object file foo.o. We can compile to object file by passing the -c parameter to gcc compiler. Next, we have to create a shared library foo.so from an object file foo.o, by using the -shared parameter. At the end we have to link the main program with the shared library by using the -l parameter passing it the name of the library: note that by using the -lfoo parameter, gcc will actually be looking for libfoo.so library and not the lib.so library (gcc assumes all libraries start with the ‘lib’ prefix and end with ‘.so’ suffix). We also need the -L parameter to tell gcc where to find libfoo.so library, and since the library is in the current directory, we can use the pwd command to specify it.

# gcc -c -fpic -Wall -Werror foo.c

# gcc -lm -shared -o libfoo.so foo.o

# gcc -L$(pwd) -lfoo -o main main.c

Next, we can run the program, which will result in an error as presented below, which happens because the loader cannot find the shared library we’ve created, because we haven’t copied it to a standard location. To specify the path where the loader should look, we have to export the LD_LIBRARY_PATH variable as presented below. Afterward, we can simply run the compiled from with “./main”, which will successfully run the program to display the fibonnaci numbers from range 1-1000.

# export LD_LIBRARY_PATH=”$(pwd):$LD_LIBRARY_PATH”

# ./main

The function sum() is at 0x400640.

The function sub() is at 0x400660.

The function isfibonnaci() is at 0x400650.

Fibonnaci number found: 0; the next fibnum is supposed to be: 0.

Fibonnaci number found: 1; the next fibnum is supposed to be: 1.

Fibonnaci number found: 2; the next fibnum is supposed to be: 3.

Fibonnaci number found: 3; the next fibnum is supposed to be: 5.

Fibonnaci number found: 5; the next fibnum is supposed to be: 8.

Fibonnaci number found: 8; the next fibnum is supposed to be: 13.

Fibonnaci number found: 13; the next fibnum is supposed to be: 21.

Fibonnaci number found: 21; the next fibnum is supposed to be: 34.

Fibonnaci number found: 34; the next fibnum is supposed to be: 55.

Fibonnaci number found: 55; the next fibnum is supposed to be: 89.

Fibonnaci number found: 89; the next fibnum is supposed to be: 144.

Fibonnaci number found: 144; the next fibnum is supposed to be: 233.

Fibonnaci number found: 233; the next fibnum is supposed to be: 377.

Fibonnaci number found: 377; the next fibnum is supposed to be: 610.

Fibonnaci number found: 610; the next fibnum is supposed to be: 987.

Fibonnaci number found: 987; the next fibnum is supposed to be: 1597.

While the program is running, we can use the frida-trace tool to hook the isfibonnaci function, which will print a new line every time the function is called. On the picture below, we can see that a function is called approximately every second, as it should have been. Did you notice that a script isfibonnaci.js was automatically created in the __handlers__/libfoo.so/ directory?

The isfibonnaci.js contains the following code, which contains the onEnter function called before calling the isfibonnaci function and onLeave function called after we’ve already called the isfibonnaci function.

Notice that the onEnter function contains the log() function call, which displays the current function name on stdout. Since we know the function accepts only one integer parameter, we can simply log the first parameter argv[0] as well. After reruning the script, it will also display the parameter passed to the function as presented below.

Here we’ve seen how easy it is to actually log the function as well as parameters being executed by the program application. This is all because of the power of Frida.

The Dropbox example

Let’s take a look of what we can do with the Dropbox application. Let’s first download a headless version of Dropbox via the following command. Note that this was done on a cloud Linux operating system running 64-bit Linux version. The first command downloads and Dropbox and extracts it into a home folder, while the second command runs a Dropbox daemon.

# cd ~ && wget -O – “https://www.dropbox.com/download?plat=lnx.x86_64” | tar xzf –
# ~/.dropbox-dist/dropboxd

After running a daemon, it will display a message about the computer not being linked to any Dropbox account and provide an URL, which we have to visit in order to create an account to link the computer to that Dropbox account. In the interface, we only need to specify name, surname, an email address and a password (note that the email cannot be @mailcatch.com since it is blocked by the Dropbox service due to suspicious activity; we’ll just have to use the real email address registered with Google, Yahoo or any other provider.

After creating the account and logging into the account, the daemon back in the console will display a message about the computer now being linked to a Dropbox account. Afterwards, a new folder ~/Dropbox/ will be created, which will be synchronized with the cloud.

Then we can start following all recv function calls by using the “frida-trace -i ‘recv*’ dropbox” command as presented on the picture below. Eventually, recv* function calls will be exchanged between a server and a client, resulting in new lines being printed at the bottom of console output.

Also note that a number of handlers have been loaded for functions: recvfrom, recv, recvmmsg, recvmsg and recvfrom. While the program is running, we can edit any of the handlers to change the behavior of Frida. Let’s review the recvmsg.js handler, which again contains the onEnter and onLeave functions.

Ethical Hacking Training – Resources (InfoSec)

At this point, it should be fairly easy to add the necessary code to modify the messages being sent over the wire in order to try to do some malicious actions like changing the username of the currently logged in user in order to try to access the data of another user. First, we should login to the application by using existing username in order to obtain a valid session enabling authenticated access to the cloud-based application, after which we should change the username. By doing that, we’ll be authenticating without existing username’s session, while accessing the data of another user. I haven’t tried this, since I’m not exactly sure whether I can do that legally or not, so the rest is up to the reader.

Conclusion

We’ve seen how easy it is to use Frida to inspect the state of the program at runtime, but not only that, we can also intercept function calls, change arguments or alter the whole program execution. The really awesome part of Frida is its scripting possibilities enabling us to write a JavaScript file, which will automatically hook a function inside a program to alter its behavior or return an altered value from an existing function.

It’s certainly beneficial to install Frida and experiment with it, as it may enable discovering a whole range of vulnerabilities that haven’t previously been discovered. I suggest injecting into a wide range of applications in order to enumerate the functions being called by the application, after which you can focus on a few interesting function calls and display their arguments as well as return values. After that, you can try to modify the parameters to see what happens and whether the return value changes in some way.

References

[1] Inject JavaScript to explore native apps on Windows, Mac, Linux, iOS and Android,

http://www.frida.re/.

[2]CVE-2013-1862

http://www.cvedetails.com/cve/CVE-2013-1862/