Exploring Native Functions with Frida on Android — part 2
Dynamic exploration using frida-trace and CLI
Dynamic Inspection
Having covered what can be done with a static approach¹ on native libraries as an information gathering technique, we can move into a more dynamic approach, running the app and leveraging runtime tools.
Ideally, we would like to achieve the following:
- Understand which native functions are being used and ideally at which stage of the app execution (where and when).
- Perform enumeration of native functions for a given library.
- Hook into a native function when it is called to change its behaviour, for example by changing the arguments or the return value.
Frida-trace
To answer the first point, we could initially use frida-trace². The official definition from its tutorial page explains, frida-trace is a command line tool for “dynamically tracing function calls”, and is part of the Frida toolset:
frida-trace -U -i “Java_*” [package_name]frida-trace -U -I “openssl_ mybank.so” co.uk.myBank
The first command shows how to use frida-trace to trace all the JNI functions (identified with “Java_”) for a specific app package.
However, the second command shows usage to trace any function for a given library, specified by the “-I”, which is the flag for including a specific library, also known as modules. Using this second example we again consider our app myBank, with openssl_ mybank.so native library, a forked version of OpenSSL.
The image shows the output of the second example. The first point to note is that frida-trace will not spawn the app, therefore we need to run the app manually before executing the command. As soon as frida-trace is started, it will automatically enumerate the functions for that given module and also inside the current working directory, creates a “handler”. This is a JavaScript file associated with a specific native function that we can edit and amend with our own code; for example to print out more information such as the arguments.
Once the enumeration is done, Frida will trace these functions in an ordered and chronological way (note the millisecond count), as soon as they are called, also showing nested calls and different threads, symbolised by different colours.
This provides a nice image and an overview of what is happening when running the app. However, frida-trace might not be enough or work as expected in certain circumstances.
Having said we first need to start the app manually and then use frida-trace, what if the app implements a security mechanism such as root detection which will kill the app process as soon as it starts if root is detected? In this case we would not be able to utilise frida-trace unless we first bypass the root-detection protection. In such circumstances Frida can provide the same results using the CLI version.
Frida CLI
By using the CLI mode, it is possible to run Frida³ in a way that automatically spawns the app process and loads a JavaScript file which allows full access to the entire framework API, for example bypassing root detection and SSL pinning mechanisms by hooking the respective Java methods. Therefore, using the CLI, it is possible to overcome any potential issues that might be a road block for frida-trace.
Throughout the remainder of this topic we are going to illustrate and discuss several examples covering different scenarios. It is important to understand the logic behind these code samples rather than the actual JavaScript syntax. In all these examples, Frida was always launched with the following command:
frida -Uf [package_name] -l myScript.js --no-pause
Exploring system cryptography and SSL libraries
In this scenario we are interested in native libraries included and used by the Android System. The aim is to establish whether any SSL or cryptographical libraries are used.
We are going to accomplish this by using a simple JavaScript file, myScript.js, containing a custom function, exploreSystemSSL_CryptoModules(). First, we need to understand some of the theory behind these components, Modules and Exports.
Process.enumerateModules() will return an array of Module objects that have been loaded at runtime by the spawned app process.
A Frida Module object has the following properties (similar to a Java class variable):
- name: representing the name of the module as a string
- base: representing the base, initial address (in a hexadecimal representation) of that module currently loaded in memory
- size: representing how big the module is in bytes
- path: representing the full filesystem path as a string
The array of Modules is then parsed to return only those modules that contain the string “libssl” and “crypto” in their name; this can be done using the JavaScript function indexOf() which returns either the index of the match or -1 if no match has been found.
If a match is found, enumerateExports() can be called for that given module to list the Exports.
Process.enumerateExports() returns an array of objects representing Exports, these are then represented as text with JSON.stringify()
An Export object has the following properties:
- type: representing what the export is, it can be either a function or a variable
- name: representing the name of this function/variable
- address: representing the current memory address of this specific function/variable
Before moving to the next scenario, a few considerations are needed with the previous code example provided and the exploreSystemSSL_CryptoModules() function:
- The function shown is not optimised, therefore it is very expensive to run, will delay the app execution and may crash the app if the device used is not computationally powerful enough. It is meant for understanding the logic behind embedded library modules and the potential of the API.
- The function is limited to only enumerate the modules loaded at app execution, therefore if a library is loaded during the runtime lifecycle it will not be shown.
- By having the memory base address and the size of a given library, it is possible to monitor this range of memory (base+size) by using another Frida API, Memory.
As applications get more complex and provide more functionality, using Frida is an effective method of locating the more in-depth ‘behind the scenes’ parts of Android apps whether for a development or a security perspective.
In the following article we will explain concepts pertaining to runtime execution and use of the Memory method to eliminate consideration 2 and 3 above.