Exploration of Native Modules on Android with Frida
In this blog, we will demonstrate the Use of Frida for instrumentation of Native Modules in Android Application while doing android Application Pentesting. also explain the Frida Android Apis. We will show you some examples that highlight what Frida can do.
In our Previous Blog, you are already familiar with Frida, if you didn’t familiar with Frida please go through the previous blog. Getting started with Frida on Android Apps
Prerequisites
Before starting with Exploration_of_Native_Modules_on_Android, it is assumed that the reader has prior knowledge of Frida, and also has a basic understanding of NDK(Native development kit). It would help to better Understanding of API calls and build your own custom tools while exploring/enumerating Application native modules.
Introduction
Mobile security testing of Android applications includes code reviews to understand how the application logic and flow works and identify potential security vulnerabilities. If the app was developed in Java, decompiling the app means reversing the compilation process to extract the Java source code from the binary compiled code. but If native Libraries are used in the application then It is not possible to decompile and retrieve the source code of a compiled C/C++ native library.
In such a case Frida is a very useful tool. It allows the penetration tester to inject their code (JavaScript) inside a program.
let’s Have A Basic Overview About Some Basic Terms of Native Library Implementation in Android.
What is Android NDK?
The NDK (Native Development Kit) is a tool that allows you to program in C/C++ for Android devices. It’s intended to integrate with the SDK (it’s described as a “companion tool”) and used only for performance-critical portions of a project.
The source code is compiled directly into machine code for the CPU (and not into an intermediate language, as with Java) then developerscano get the best performance.
So, whenever you want to make some high-performance applications for great speed or want to use some preexisting code written in some native language then you can use C or C++
What if you want to use both Java/Kotlin and native language in your application? This is the most used case. Is it possible to use Java code from C++ and vice-versa? How will the Java code communicate with the native code? The communication is done by JNI. What is this JNI?
What is JNI ?
- JNI or Java Native Interface is the interface between your Java/Kotlin and the C/C++ code.
- To be able to call native libraries from Java code, a framework named Java Native Interface (JNI) is used. Using this interface, C/C++ functions are mapped as methods and
primitive data types are converted between Java and C/C++. - For this to work, special syntax is needed for JNI to recognize which method in which class a native function should correspond to.
- To mark a function as native in Java, a special keyword called native is used to define a method.
- Java or Kotlin Code uses JNI to communicate with the C or C++ code.
Now we have basic understanding about the NDk,JNI , and their compilation process in android. Lets Proceed to the Enumeration and Hooking Part of Native Libraries .

Finding native libraries
- First of all, we must understand if the app actually uses any native functions, therefore we need to locate these native libraries.
- A native library is a .so file, where “so” stands for Shared Object, which is essentially a compiled C file. On Android, we can have two types of native library: system (for example libc.so) and custom application ones (for example myApp.so). Custom libs for the apps in their respective app directories (_/data/data/package_name/lib/).
- Or You can check the Application specific library by extracting the application in their repective directories.
Having identified native libraries are being used, the next step consists of inspecting the Java class code to understand where and when these native libraries are actually loaded by the app. This task can be done by searching for the Java method: System.loadLibrary().
Finding Native Functions
we need to identify the actual C functions implemented in these native libraries. These are invoked directly within the app and are the link between C and Java. This link is defined by the JNI (Java Native Interface), which requires these functions to be declared within a Java class file by using the keyword “native” and then wrapped inside a normal Java method.
- Declaration of native function in Java class file
- Actual Native functions In Native Library
So far we have learned to find native libraries and native functions in android application . Lets Enumerate /Inspect native functions dynamically Using Frida
Dynamic Exploration Using Frida
Using frida-trace
frida-trace is a tool for dynamically Monitoring/tracing Method calls. It is useful for debugging method calls on every event in mobile application.
Code-Snippets:
- Tracing all Functions Calls In native Library
frida-trace -U -I "libnative-lib*" com.example.nativrproject
this command shows usage to trace any function for a given library, specified by the “the -I”
- Tracing JNI functions in Native Library
frida-trace -U -i “Java_*” [package_name]
This command shows usage to trace all JNI function.
TO know more about frida-trace :
Usages
frida-trace --help
Using Frida Apis
Process.enumerateModules:
This will return an array of Module objects that have been loaded at runtime by the spawned app process
Basically this Frida API enumerate all Native library that is loaded at runtime of application
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
Code-Snippet:
var process = Process.enumerateModules()
console.log(process.length)
var i =0;
for(i=0;i<process.length;i++){
console.log(JSON.stringify(process[i]))
}
- OR you can simply check by Process.enumerateModules()
Spawn the application :-> frida -U com.example.nativrproject
Entered into the shell :> Process.enumerateModules()
Process.enumerateExports:
This 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
This API Will return all exported functions,variable of given module.
Code-Snippet
- Enumerate all functions & Variable of the Given Module
var process = Process.enumerateModules()
console.log(process.length)
var i =0;
for(i=0;i<process.length;i++){
if(process[i].path.indexOf('libnative-lib')!=-1)
{
console.log(JSON.stringify(process[i])+"n")
var exports = process[i].enumerateExports()
for(var j =0;j<exports.length;j++){
console.log(JSON.stringify(exports[j]))
}}}
- Enumerate all The JNI Functions In Given module
var process = Process.enumerateModules()
//console.log(process.length)
var i =0;
for(i=0;i<process.length;i++){
if(process[i].path.indexOf('libnative-lib')!=-1)
{
//console.log(JSON.stringify(process[i])+"n")
var exports = process[i].enumerateExports()
for(var j =0;j<exports.length;j++){
//console.log(JSON.stringify(exports[j]))
if(exports[j].name.indexOf('Java_')!=-1)
{
console.log(JSON.stringify(exports[j])+"n")
} } } }
_The script can only enumerate the modules loaded at its execution.
Using the memory base address and the size of library, monitor the memory to extract useful values
We can only enumerate modules from above Frida Apis . but cant’t hook in specific method in android application.so For solving these issues frida provides a powerful API Interceptor.
By Using Interceptor we can intercept calls of a Specific target function while the app is running.
interceptor.attach
Interceptor.attach(target, callbacks[, data])
: intercept calls to function at target
. This is a NativePointer
specifying the address of the function you would like to intercept calls to.
target is required input of this API is a pointer indicating the current memory address of the target function. Therefore, either we provide the actual address , or we use Module.getExportbyName().
Frida takes care of this detail for you if you get the address from a Frida API (for example Module.getExportByName()
).
Module.getExportByName() : which will return a pointer to the function given as input. if a specified function is not found then this will return an error.
The callbacks
argument is an object containing one or more of:
- callback function given one argument
args
that can be used to read or write arguments as an array ofNativePointer
objects. onLeave(retval)
: callback function given one argumentretval
that is aNativePointer
-derived object containing the raw return value.retval.replace(ptr("0x1234"))
to replace with a pointer
Code-Snippets
Interceptor.attach(Module.findExportByName('libnative-lib.so','Java_com_example_nativrproject_MainActivity_stringFromJNI'),{
onEnter: function(args)
{
console.log("enterd" )
},
onLeave: function (retval) {
//changing return value !!!!
console.log("leaving")
}
})
In above code Snippet:
- Module.findExportByName(‘libnative-lib.so’,’Java_com_example_nativrproject_MainActivity_stringFromJNI)’ – Return Pointor to function Java_com_example_nativrproject_MainActivity_stringFromJNI
- The callbacks for onEnter and onLeave that will be executed when and if the function is ever used by the running app.
Brief Demonstration of Native Function Hooking!!!
Lets take an example :
- android native code
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jint JNICALL
Java_com_example_nativrproject_MainActivity_add( JNIEnv *env, jclass abcd, jint x, jint y) {
//return an integer
return x *y;
}
- JAVA Code
Application with above code print 3*4 =12 in logs. Lets hook the application jni function and check whats paramteres are passing . we are here only to hook and enumerate Native functions not Java functions.
Interceptor.attach(Module.findExportByName('libnative-lib.so','Java_com_example_nativrproject_MainActivity_add'),{
onEnter(args){
console.log('Context information:');
// Save arguments for processing in onLeave.
console.log("first parameter" +args[2].toInt32());
console.log("second parameter"+ args[3].toInt32());
},
onLeave(result)
{
console.log('----------')
// Show argument 1 (buf), saved during onEnter.
console.log("result" + result);
//const dstAddr = Java.vm.getEnv().newStringUtf("1234");
const numBytes = result.toInt32();
console.log('Result : ' + numBytes);
}
});
Code-snippets for Native Code Manipulation
Interceptor.attach(Module.findExportByName('libnative-lib.so','Java_com_example_nativrproject_MainActivity_stringFromJNI'),{
onEnter: function(args)
{
console.log("enterd" )
},
onLeave: function (retval) {
//changing return value !!!!
const dstAddr = Java.vm.getEnv().newStringUtf("1234");
console.log(dstAddr)
retval.replace(dstAddr);
}
})
References
- https://blog.mindorks.com/getting-started-with-android-ndk-android-tutorial
- https://medium.com/@banmarkovic/process-of-compiling-android-app-with-java-kotlin-code-27edcfcce616
- https://frida.re/
- https://notsosecure.com/instrumenting-native-android-functions-using-frida/
- https://github.com/iddoeldor/frida-snippets
- https://medium.com/swlh/exploring-native-functions-with-frida-on-android-part-1-bf93f0bfa1d3
About Payatu
Payatu is a Research Focused, CERT-In impaneled Cybersecurity Consulting company specializing in security assessments of IoT product ecosystem, Web application & Network with a proven track record of securing applications and infrastructure for customers across 20+ countries.
Get in touch with us. Click on the get started button below.