Introduction: The Challenge of Non-Rooted Android Penetration Testing
Frida is an indispensable toolkit for dynamic instrumentation, allowing security researchers to inject custom scripts into running processes. However, its standard setup typically requires a rooted Android device, as it relies on a Frida server running with elevated privileges. For real-world penetration testing scenarios, especially with production applications, targeting non-rooted devices is often a necessity. This is where the Frida Gadget comes into play – a self-contained library that can be injected directly into an application’s process without requiring root access. This article will guide you through the process of building a custom Frida Gadget, integrating it into an Android application’s .so files, and deploying it on a non-rooted device.
Understanding Frida Gadget: A Rootless Solution
Frida Gadget is essentially a Frida agent compiled as a shared library (.so file). When this library is loaded by an application, it initializes Frida’s instrumentation engine within the app’s own process. This allows you to connect to the Gadget from your host machine using the Frida client, even if the Android device isn’t rooted. The core challenge lies in ensuring that the target application loads this custom .so file during its startup.
Why Customization?
Customizing the Frida Gadget often involves renaming it to blend in with existing application libraries or modifying its loading mechanism to ensure stealth or compatibility. While Frida provides pre-built gadgets, integrating them effectively into a third-party APK requires specific techniques we’ll explore.
Prerequisites for Custom Gadget Deployment
- Android SDK Platform Tools: For
adbandapksigner. - Java Development Kit (JDK): For `jarsigner` (if `apksigner` causes issues).
- Apktool: For decompiling and recompiling Android applications. Download from Apktool’s official site.
- Frida CLI Tools:
fridaandfrida-toolsinstalled viapip. - Frida Gadget: Download the appropriate architecture-specific
frida-gadget.sofrom Frida’s GitHub releases page.
Step 1: Obtain and Prepare the Frida Gadget
First, download the correct frida-gadget.so for your target application’s architecture. Most modern Android apps use arm64-v8a or armeabi-v7a. You can typically find this by inspecting the lib directory inside an APK.
For example, for arm64-v8a:
wget https://github.com/frida/frida/releases/download/16.1.4/frida-gadget-16.1.4-android-arm64.so.xz
unxz frida-gadget-16.1.4-android-arm64.so.xz
mv frida-gadget-16.1.4-android-arm64.so frida-gadget.so
It’s good practice to rename it to something generic like libfrida-gadget.so for consistency.
Step 2: Decompile the Target Android Application (APK)
Use apktool to decompile the target APK. This will extract its resources, AndroidManifest.xml, and most importantly, its Smali code.
apktool d target_app.apk -o target_app_decompiled
This creates a directory named target_app_decompiled containing the decompiled contents.
Step 3: Integrate Frida Gadget into the APK’s Native Libraries
Method A: Placing the Gadget
Copy your renamed libfrida-gadget.so into the appropriate native library directory within the decompiled APK structure. This ensures it’s packaged with the app.
cp frida-gadget.so target_app_decompiled/lib/arm64-v8a/
Note: Ensure the architecture (e.g., arm64-v8a) matches the Gadget you downloaded and the primary architecture of the target app. If the app supports multiple architectures, you might need to copy it to all relevant directories.
Method B: Injecting the LoadLibrary Call (Smali Modification)
To make the application load libfrida-gadget.so, we need to inject a System.loadLibrary() call into its Smali code. A common and effective place is within the application’s main Application class’s onCreate() method, or an early-loading Activity‘s onCreate().
Identify the Application Class:
Open target_app_decompiled/AndroidManifest.xml and look for the <application> tag’s android:name attribute.
<application android:name="com.example.MyApp" ...>
If android:name is not specified, the default android.app.Application class is used. Otherwise, locate the corresponding Smali file (e.g., target_app_decompiled/smali_classesX/com/example/MyApp.smali).
Modify the Smali Code:
Edit the identified .smali file. Find the .method public onCreate()V (or a similar initialization method) and insert the following Smali code at the beginning or end of the method, before any return-void:
.method public onCreate()V
.locals 0
.prologue
invoke-super {p0}, Landroid/app/Application;->onCreate()V
# Inject Frida Gadget load
const-string v0, "frida-gadget"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
return-void
.end method
Ensure that the const-string v0, "frida-gadget" matches the name you used for your .so file (e.g., libfrida-gadget.so becomes "frida-gadget" for loadLibrary).
Method C: Library Hijacking (Advanced)
This method involves renaming an existing, legitimate native library that the application already loads (e.g., libnative-lib.so to libnative-lib_orig.so), replacing it with libfrida-gadget.so, and then modifying the Gadget to load the original library after its initialization. This requires recompiling the Gadget or using specific Frida APIs within your script. This approach is more complex and beyond the scope of this general guide but offers maximum stealth.
Step 4: Recompile and Resign the APK
Recompile the APK:
After making all necessary modifications, recompile the APK using apktool:
apktool b target_app_decompiled -o target_app_frida.apk
Sign the Recompiled APK:
Android requires all applications to be signed. Since recompiling invalidates the original signature, you must sign it with your own debug key. If you don’t have one, generate it:
keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000
Sign the APK using apksigner (recommended for Android 7.0+):
apksigner sign --ks debug.keystore --ks-key-alias androiddebugkey --key-pass pass:android --ks-pass pass:android target_app_frida.apk
Alternatively, using jarsigner for older Android versions:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore debug.keystore target_app_frida.apk androiddebugkey
Then zipalign it for optimization:
zipalign -v 4 target_app_frida.apk target_app_frida_aligned.apk
Step 5: Deploy and Connect
Uninstall the original application (if installed) and then install your newly signed APK on the non-rooted Android device:
adb uninstall com.example.targetapp
adb install target_app_frida.apk
Now, launch the application on the device. Since libfrida-gadget.so is loaded during startup, Frida should now be active within the app’s process. You can connect to it from your host machine:
frida-ps -U
frida -U -f com.example.targetapp -l my_script.js --no-pause
Or simply attach if the app is already running:
frida -U com.example.targetapp -l my_script.js
You should now be able to execute your Frida scripts against the application, even on a non-rooted device.
Conclusion
Deploying a custom Frida Gadget on non-rooted Android devices significantly expands the scope of mobile application penetration testing. By understanding the application’s structure, injecting the Gadget, and handling the recompilation and signing process, you can bypass the root requirement and perform dynamic analysis on a wider range of targets. This technique is a crucial skill for any mobile security researcher, enabling deeper insights into application behavior without altering the device’s fundamental security posture.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →