Exploring Native Modules in Android with Frida

Exploring Native Modules in Android with Frida

Mobile security testing is a crucial aspect of ensuring the integrity and security of Android applications. In order to thoroughly assess an app’s security, it’s important to understand how the app’s code works, including any native modules that may be utilized. Native modules, often implemented in C/C++, provide developers with the ability to optimize performance and utilize existing code libraries. However, reverse engineering native modules can be challenging due to the inability to decompile the compiled C/C++ code.

Fortunately, Frida, a dynamic binary instrumentation tool, provides penetration testers with a powerful solution. By injecting JavaScript code into an app, Frida allows testers to interact with and modify the behavior of native modules in real time. In this comprehensive blog, we will explore the use of Frida for the instrumentation of native modules in Android applications, providing step-by-step instructions and examples to demonstrate the capabilities of Frida.

Prerequisites

Before diving into exploring native modules on Android using Frida, it’s essential to have a basic understanding of Frida and the Android NDK (Native Development Kit). Familiarity with API calls and the compilation process of native libraries will greatly enhance your ability to effectively enumerate and hook native functions.

Understanding Native Libraries and the JNI

In order to effectively explore native modules in Android, it’s important to understand the concepts of Native Libraries and the Java Native Interface (JNI).

Native Libraries, also known as .so files (Shared Objects), are compiled C/C++ files that are utilized within an Android application. These libraries provide developers with the ability to write high-performance code and interact directly with the device’s hardware. Native Libraries can be system libraries or custom application libraries, and they are loaded into the application’s memory at runtime.

The JNI is the interface that allows communication between Java/Kotlin code and native C/C++ code.  By using the JNI, developers can seamlessly integrate native code into their Java/Kotlin applications and leverage the power of native libraries.

Enumerating Native Libraries

Before we can explore and hook native functions, we need to locate the native libraries used by an Android application. Native libraries are typically stored as .so files within the app’s package. These files can be found either in the system directory, such as /system/lib, or within the app’s specific directory, such as /data/data/package_name/lib.

To identify the native libraries used by an app, we can extract the APK file and examine its contents. By unzipping the APK, we can access the lib directory, where the native libraries are stored. Additionally, the System.loadLibrary() method in the Java code indicates the loading of a native library.

android

Once we identify the native libraries, the next step involves inspecting the Java class code to understand where and when these libraries load. We can search for the System.loadLibrary() method to determine the point of library loading within the app’s code.

Finding Native Functions

Having located the native libraries and understood their loading process, we can now focus on identifying the actual C functions implemented within these libraries. These functions act as the bridge between the Java and C code and the app invokes them directly.  The JNI requires these functions to be declared within a Java class file using the native keyword and wrapped inside a normal Java method.

To identify the native functions, we can search for function declarations in the Java code. Native functions are typically declared using the native keyword, and their implementations can be found within the native library files. By searching for patterns such as (private|public) static native, we can identify the native function declarations in the app’s codebase.

android

Dynamic Exploration Using Frida

Now that we have a solid understanding of native libraries and native functions in Android applications, it’s time to explore and interact with them using Frida. Frida provides powerful APIs that allow us to dynamically trace and hook native functions at runtime.

Using Frida-Trac

Frida-Trac is a handy tool for dynamically monitoring and tracing method calls in mobile applications. It allows us to debug method calls and gain insights into the app’s behavior.

To trace all function calls in a specific native library, we can use the following Frida-Trac command:

				
					frida-trace -U -I "libnative-lib*" com.example.app  
				
			

This command traces any function within the specified library, indicated by the -I flag.

android

To trace JNI functions in a native library, we can use the following command:

				
					frida-trace -U -i "Java_*" [package_name]  
				
			
android

This command traces all JNI functions, indicated by the -i flag.

Using Frida APIs

Frida provides a set of powerful APIs that allow us to interact with native modules and functions at runtime. One of the essential Frida APIs is Process.enumerateModules(), which returns an array of Module objects representing the native libraries loaded by the app at runtime.

A Frida Module object has properties such as name, base, size, and path, which provide information about the loaded native libraries.

To enumerate all the loaded native modules, we can use the following Frida API:

				
					var process = Process.enumerateModules();  console.log(process.length);  var i = 0;  for (i = 0; i < process.length; i++) { console.log(JSON.stringify(process[i])); }  
				
			

We can check this using the following command –

				
					Process.enumerateModules() 
				
			
android
Interceptor.Attach

Interceptor.attach(target, callbacks[, data]): Intercept calls to a function at the target address. The required input for this API is a NativePointer that specifies the memory address of the function you want to intercept.

In this context, “target” is a crucial parameter. You have two options for providing the address:

1. Manual Input: You can explicitly provide the actual memory address of the target function.

2. Frida Convenience: Alternatively, you can rely on Frida to handle this task for you. Frida offers utility functions like Module.getExportByName() that can automatically retrieve and return a pointer to the specified function.

However, it’s important to note that if the function you specify is not found, Module.getExportByName() will return an error. Hence, ensuring the use of the correct function name is crucial to avoid errors during the interception process.

These Frida APIs provide us with the ability to dynamically explore and interact with native modules and functions in real time, allowing us to gain deeper insights into an app’s behavior and potentially discover security vulnerabilities.

Exploring and analyzing native modules in Android applications is a crucial step in mobile security testing. By understanding the concepts of native libraries, JNI, and the loading process of these modules, we can effectively identify and analyze the native functions within an app. Frida serves as a powerful tool for dynamic binary instrumentation, enabling us to inject JavaScript code into an app and interact with native modules and functions in real time. With Frida, we can trace function calls, enumerate loaded modules, and even hook and modify the behavior of native functions.

By combining static analysis techniques with dynamic exploration using Frida, security testers can gain a comprehensive understanding of an app’s native code and uncover potential security vulnerabilities. So go ahead, dive into the world of native module exploitation with Frida, and take your mobile security testing to the next level!

Redfox Security is a diverse network of expert security consultants with a global mindset and a collaborative culture. If you are looking to improve your organization’s security posture, contact us today to discuss your security testing needs. Our team of security professionals can help you identify vulnerabilities and weaknesses in your systems and provide recommendations to remediate them.

“Join us on our journey of growth and development by signing up for our comprehensive courses.